add chat page
All checks were successful
Build and Push Container / build-and-push (push) Successful in 14s
All checks were successful
Build and Push Container / build-and-push (push) Successful in 14s
This commit is contained in:
285
css/style.css
285
css/style.css
@@ -497,3 +497,288 @@ footer {
|
|||||||
color: var(--text-footer);
|
color: var(--text-footer);
|
||||||
box-shadow: 0 -4px 20px rgba(255,20,147,0.1);
|
box-shadow: 0 -4px 20px rgba(255,20,147,0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Chat page styles */
|
||||||
|
.chat-config {
|
||||||
|
background: var(--white);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
border: 2px solid var(--pink-200);
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-row label {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--pink-600);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-row input {
|
||||||
|
padding: 0.6rem 0.8rem;
|
||||||
|
border: 2px solid var(--pink-200);
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--pink-900);
|
||||||
|
background: var(--pink-50);
|
||||||
|
transition: border-color 0.2s, background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-row input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--pink-400);
|
||||||
|
background: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-btn {
|
||||||
|
background: linear-gradient(135deg, var(--pink-400), var(--pink-500));
|
||||||
|
color: var(--white);
|
||||||
|
border: none;
|
||||||
|
padding: 0.6rem 1.2rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
transition: background 0.2s, transform 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-btn:hover {
|
||||||
|
background: linear-gradient(135deg, var(--pink-500), var(--pink-600));
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-messages {
|
||||||
|
background: var(--white);
|
||||||
|
border-radius: 16px;
|
||||||
|
border: 2px solid var(--pink-200);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
min-height: 400px;
|
||||||
|
max-height: 60vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.8rem;
|
||||||
|
margin-bottom: 1.2rem;
|
||||||
|
animation: messageIn 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes messageIn {
|
||||||
|
from { opacity: 0; transform: translateY(10px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-message {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-avatar {
|
||||||
|
background: linear-gradient(135deg, var(--pink-400), var(--pink-500));
|
||||||
|
color: var(--white);
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 800;
|
||||||
|
flex-shrink: 0;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-message .message-avatar {
|
||||||
|
background: linear-gradient(135deg, var(--pink-600), var(--pink-700));
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-bubble {
|
||||||
|
max-width: 75%;
|
||||||
|
padding: 0.8rem 1.2rem;
|
||||||
|
border-radius: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-message .message-bubble {
|
||||||
|
background: linear-gradient(135deg, var(--pink-400), var(--pink-500));
|
||||||
|
color: var(--white);
|
||||||
|
border-bottom-right-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-message .message-bubble {
|
||||||
|
background: var(--pink-50);
|
||||||
|
border: 2px solid var(--pink-200);
|
||||||
|
color: var(--pink-900);
|
||||||
|
border-bottom-left-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content.streaming::after {
|
||||||
|
content: '▊';
|
||||||
|
animation: blink 0.8s infinite;
|
||||||
|
color: var(--pink-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blink {
|
||||||
|
0%, 100% { opacity: 1; }
|
||||||
|
50% { opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input-area {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.8rem;
|
||||||
|
background: var(--white);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 1rem 1.2rem;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
border: 2px solid var(--pink-200);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input-area textarea {
|
||||||
|
flex: 1;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: var(--pink-900);
|
||||||
|
font-family: inherit;
|
||||||
|
resize: none;
|
||||||
|
outline: none;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input-area textarea::placeholder {
|
||||||
|
color: var(--pink-300);
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-btn {
|
||||||
|
background: linear-gradient(135deg, var(--pink-500), var(--pink-600));
|
||||||
|
color: var(--white);
|
||||||
|
border: none;
|
||||||
|
padding: 0.6rem 1.5rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 700;
|
||||||
|
transition: background 0.2s, transform 0.1s, opacity 0.2s;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-btn:hover:not(:disabled) {
|
||||||
|
background: linear-gradient(135deg, var(--pink-600), var(--pink-700));
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-btn:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark mode chat styles */
|
||||||
|
[data-theme="dark"] .chat-config {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border-color: var(--border-primary);
|
||||||
|
box-shadow: 0 10px 25px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .config-row label {
|
||||||
|
color: var(--text-heading-strong);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .config-row input {
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border-color: var(--border-primary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .config-row input:focus {
|
||||||
|
border-color: var(--border-accent);
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .clear-btn {
|
||||||
|
background: linear-gradient(135deg, var(--pink-600), #0f3460);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .clear-btn:hover {
|
||||||
|
background: linear-gradient(135deg, var(--pink-500), #1a1a3e);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .chat-messages {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border-color: var(--border-primary);
|
||||||
|
box-shadow: 0 10px 25px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .message-avatar {
|
||||||
|
background: linear-gradient(135deg, var(--pink-500), var(--pink-600));
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .assistant-message .message-avatar {
|
||||||
|
background: linear-gradient(135deg, #0f3460, #1a1a3e);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .user-message .message-bubble {
|
||||||
|
background: linear-gradient(135deg, var(--pink-500), var(--pink-600));
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .assistant-message .message-bubble {
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border-color: var(--border-primary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .message-content {
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .user-message .message-content {
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .message-content.streaming::after {
|
||||||
|
color: var(--pink-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .chat-input-area {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border-color: var(--border-primary);
|
||||||
|
box-shadow: 0 10px 25px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .chat-input-area textarea {
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .chat-input-area textarea::placeholder {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .send-btn {
|
||||||
|
background: linear-gradient(135deg, var(--pink-600), #0f3460);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .send-btn:hover:not(:disabled) {
|
||||||
|
background: linear-gradient(135deg, var(--pink-500), #1a1a3e);
|
||||||
|
}
|
||||||
|
|||||||
15
index.html
15
index.html
@@ -10,14 +10,15 @@
|
|||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<div class="nav-inner">
|
<div class="nav-inner">
|
||||||
<a href="index.html" class="nav-brand active">AI Cheat Sheet</a>
|
<a href="/" class="nav-brand active">AI Cheat Sheet</a>
|
||||||
<div class="nav-links">
|
<div class="nav-links">
|
||||||
<a href="pages/terminology.html">Terminology</a>
|
<a href="/pages/terminology.html">Terminology</a>
|
||||||
<a href="pages/techniques.html">Techniques</a>
|
<a href="/pages/techniques.html">Techniques</a>
|
||||||
<a href="pages/use-cases.html">Use Cases</a>
|
<a href="/pages/use-cases.html">Use Cases</a>
|
||||||
<a href="pages/model-types.html">Model Types</a>
|
<a href="/pages/model-types.html">Model Types</a>
|
||||||
<a href="pages/prompts.html">Prompt Guide</a>
|
<a href="/pages/prompts.html">Prompt Guide</a>
|
||||||
<a href="pages/math.html">Math & Concepts</a>
|
<a href="/pages/math.html">Math & Concepts</a>
|
||||||
|
<a href="/pages/chat.html">Chat</a>
|
||||||
</div>
|
</div>
|
||||||
<button class="dark-toggle" id="darkToggle" aria-label="Toggle dark mode">🌙</button>
|
<button class="dark-toggle" id="darkToggle" aria-label="Toggle dark mode">🌙</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
305
pages/chat.html
Normal file
305
pages/chat.html
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
<!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 — 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>
|
||||||
@@ -10,14 +10,15 @@
|
|||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<div class="nav-inner">
|
<div class="nav-inner">
|
||||||
<a href="../index.html" class="nav-brand">AI Cheat Sheet</a>
|
<a href="/" class="nav-brand">AI Cheat Sheet</a>
|
||||||
<div class="nav-links">
|
<div class="nav-links">
|
||||||
<a href="terminology.html">Terminology</a>
|
<a href="/pages/terminology.html">Terminology</a>
|
||||||
<a href="techniques.html">Techniques</a>
|
<a href="/pages/techniques.html">Techniques</a>
|
||||||
<a href="use-cases.html">Use Cases</a>
|
<a href="/pages/use-cases.html">Use Cases</a>
|
||||||
<a href="model-types.html">Model Types</a>
|
<a href="/pages/model-types.html">Model Types</a>
|
||||||
<a href="prompts.html">Prompt Guide</a>
|
<a href="/pages/prompts.html">Prompt Guide</a>
|
||||||
<a href="math.html" class="active">Math & Concepts</a>
|
<a href="/pages/math.html" class="active">Math & Concepts</a>
|
||||||
|
<a href="/pages/chat.html">Chat</a>
|
||||||
</div>
|
</div>
|
||||||
<button class="dark-toggle" id="darkToggle" aria-label="Toggle dark mode">🌙</button>
|
<button class="dark-toggle" id="darkToggle" aria-label="Toggle dark mode">🌙</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,14 +10,15 @@
|
|||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<div class="nav-inner">
|
<div class="nav-inner">
|
||||||
<a href="../index.html" class="nav-brand">AI Cheat Sheet</a>
|
<a href="/" class="nav-brand">AI Cheat Sheet</a>
|
||||||
<div class="nav-links">
|
<div class="nav-links">
|
||||||
<a href="terminology.html">Terminology</a>
|
<a href="/pages/terminology.html">Terminology</a>
|
||||||
<a href="techniques.html">Techniques</a>
|
<a href="/pages/techniques.html">Techniques</a>
|
||||||
<a href="use-cases.html">Use Cases</a>
|
<a href="/pages/use-cases.html">Use Cases</a>
|
||||||
<a href="model-types.html" class="active">Model Types</a>
|
<a href="/pages/model-types.html" class="active">Model Types</a>
|
||||||
<a href="prompts.html">Prompt Guide</a>
|
<a href="/pages/prompts.html">Prompt Guide</a>
|
||||||
<a href="math.html">Math & Concepts</a>
|
<a href="/pages/math.html">Math & Concepts</a>
|
||||||
|
<a href="/pages/chat.html">Chat</a>
|
||||||
</div>
|
</div>
|
||||||
<button class="dark-toggle" id="darkToggle" aria-label="Toggle dark mode">🌙</button>
|
<button class="dark-toggle" id="darkToggle" aria-label="Toggle dark mode">🌙</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,14 +10,15 @@
|
|||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<div class="nav-inner">
|
<div class="nav-inner">
|
||||||
<a href="../index.html" class="nav-brand">AI Cheat Sheet</a>
|
<a href="/" class="nav-brand">AI Cheat Sheet</a>
|
||||||
<div class="nav-links">
|
<div class="nav-links">
|
||||||
<a href="terminology.html">Terminology</a>
|
<a href="/pages/terminology.html">Terminology</a>
|
||||||
<a href="techniques.html">Techniques</a>
|
<a href="/pages/techniques.html">Techniques</a>
|
||||||
<a href="use-cases.html">Use Cases</a>
|
<a href="/pages/use-cases.html">Use Cases</a>
|
||||||
<a href="model-types.html">Model Types</a>
|
<a href="/pages/model-types.html">Model Types</a>
|
||||||
<a href="prompts.html" class="active">Prompt Guide</a>
|
<a href="/pages/prompts.html" class="active">Prompt Guide</a>
|
||||||
<a href="math.html">Math & Concepts</a>
|
<a href="/pages/math.html">Math & Concepts</a>
|
||||||
|
<a href="/pages/chat.html">Chat</a>
|
||||||
</div>
|
</div>
|
||||||
<button class="dark-toggle" id="darkToggle" aria-label="Toggle dark mode">🌙</button>
|
<button class="dark-toggle" id="darkToggle" aria-label="Toggle dark mode">🌙</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,14 +10,15 @@
|
|||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<div class="nav-inner">
|
<div class="nav-inner">
|
||||||
<a href="../index.html" class="nav-brand">AI Cheat Sheet</a>
|
<a href="/" class="nav-brand">AI Cheat Sheet</a>
|
||||||
<div class="nav-links">
|
<div class="nav-links">
|
||||||
<a href="terminology.html">Terminology</a>
|
<a href="/pages/terminology.html">Terminology</a>
|
||||||
<a href="techniques.html" class="active">Techniques</a>
|
<a href="/pages/techniques.html" class="active">Techniques</a>
|
||||||
<a href="use-cases.html">Use Cases</a>
|
<a href="/pages/use-cases.html">Use Cases</a>
|
||||||
<a href="model-types.html">Model Types</a>
|
<a href="/pages/model-types.html">Model Types</a>
|
||||||
<a href="prompts.html">Prompt Guide</a>
|
<a href="/pages/prompts.html">Prompt Guide</a>
|
||||||
<a href="math.html">Math & Concepts</a>
|
<a href="/pages/math.html">Math & Concepts</a>
|
||||||
|
<a href="/pages/chat.html">Chat</a>
|
||||||
</div>
|
</div>
|
||||||
<button class="dark-toggle" id="darkToggle" aria-label="Toggle dark mode">🌙</button>
|
<button class="dark-toggle" id="darkToggle" aria-label="Toggle dark mode">🌙</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,14 +10,15 @@
|
|||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<div class="nav-inner">
|
<div class="nav-inner">
|
||||||
<a href="../index.html" class="nav-brand">AI Cheat Sheet</a>
|
<a href="/" class="nav-brand">AI Cheat Sheet</a>
|
||||||
<div class="nav-links">
|
<div class="nav-links">
|
||||||
<a href="terminology.html" class="active">Terminology</a>
|
<a href="/pages/terminology.html" class="active">Terminology</a>
|
||||||
<a href="techniques.html">Techniques</a>
|
<a href="/pages/techniques.html">Techniques</a>
|
||||||
<a href="use-cases.html">Use Cases</a>
|
<a href="/pages/use-cases.html">Use Cases</a>
|
||||||
<a href="model-types.html">Model Types</a>
|
<a href="/pages/model-types.html">Model Types</a>
|
||||||
<a href="prompts.html">Prompt Guide</a>
|
<a href="/pages/prompts.html">Prompt Guide</a>
|
||||||
<a href="math.html">Math & Concepts</a>
|
<a href="/pages/math.html">Math & Concepts</a>
|
||||||
|
<a href="/pages/chat.html">Chat</a>
|
||||||
</div>
|
</div>
|
||||||
<button class="dark-toggle" id="darkToggle" aria-label="Toggle dark mode">🌙</button>
|
<button class="dark-toggle" id="darkToggle" aria-label="Toggle dark mode">🌙</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,14 +10,15 @@
|
|||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<div class="nav-inner">
|
<div class="nav-inner">
|
||||||
<a href="../index.html" class="nav-brand">AI Cheat Sheet</a>
|
<a href="/" class="nav-brand">AI Cheat Sheet</a>
|
||||||
<div class="nav-links">
|
<div class="nav-links">
|
||||||
<a href="terminology.html">Terminology</a>
|
<a href="/pages/terminology.html">Terminology</a>
|
||||||
<a href="techniques.html">Techniques</a>
|
<a href="/pages/techniques.html">Techniques</a>
|
||||||
<a href="use-cases.html" class="active">Use Cases</a>
|
<a href="/pages/use-cases.html" class="active">Use Cases</a>
|
||||||
<a href="model-types.html">Model Types</a>
|
<a href="/pages/model-types.html">Model Types</a>
|
||||||
<a href="prompts.html">Prompt Guide</a>
|
<a href="/pages/prompts.html">Prompt Guide</a>
|
||||||
<a href="math.html">Math & Concepts</a>
|
<a href="/pages/math.html">Math & Concepts</a>
|
||||||
|
<a href="/pages/chat.html">Chat</a>
|
||||||
</div>
|
</div>
|
||||||
<button class="dark-toggle" id="darkToggle" aria-label="Toggle dark mode">🌙</button>
|
<button class="dark-toggle" id="darkToggle" aria-label="Toggle dark mode">🌙</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user