llm-chatcraft-pro / index.html
hogiahien's picture
write an LLM frontend with options to add an API key, a custom OpenAI-compatible API and chat saving in local browser storage.
39cb4cf verified
<!DOCTYPE html>
<html>
<head>
<title>LLM ChatCraft Pro</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f0f0f0;
}
.container {
width: 95%;
margin: 0 auto;
background-color: white;
border: 1px solid #ccc;
padding: 20px;
}
h1 {
color: #000080;
text-align: center;
font-size: 24px;
}
table {
width: 100%;
border-collapse: collapse;
}
td {
vertical-align: top;
padding: 10px;
}
.panel {
border: 1px solid #ccc;
padding: 15px;
margin-bottom: 10px;
background-color: #f8f8f8;
}
input, select, textarea {
border: 1px solid #666;
padding: 4px;
font-family: Arial, sans-serif;
}
button {
background-color: #e0e0e0;
border: 1px solid #666;
padding: 4px 8px;
cursor: pointer;
}
button:hover {
background-color: #d0d0d0;
}
.message-user {
background-color: #e0e8ff;
margin: 5px 0;
padding: 8px;
border: 1px solid #a0a0a0;
}
.message-assistant {
background-color: #f0f0f0;
margin: 5px 0;
padding: 8px;
border: 1px solid #a0a0a0;
}
.chat-container {
height: 300px;
overflow-y: auto;
border: 1px solid #666;
padding: 10px;
background-color: white;
}
</style>
</head>
<body>
<div class="container">
<h1>LLM ChatCraft Pro</h1>
<table>
<tr>
<td width="30%">
<div class="panel">
<h2>Settings</h2>
<table width="100%">
<tr>
<td><b>API Key:</b></td>
</tr>
<tr>
<td><input type="password" id="apiKey" placeholder="Enter your API key" style="width: 95%"></td>
</tr>
<tr>
<td><b>API Endpoint:</b></td>
</tr>
<tr>
<td><input type="text" id="apiEndpoint" placeholder="https://api.openai.com/v1" style="width: 95%"></td>
</tr>
<tr>
<td><b>Model:</b></td>
</tr>
<tr>
<td>
<select id="model" style="width: 95%">
<option value="gpt-3.5-turbo">gpt-3.5-turbo</option>
<option value="gpt-4">gpt-4</option>
<option value="custom">Custom Model</option>
</select>
</td>
</tr>
<tr id="customModelContainer" style="display: none">
<td><b>Custom Model:</b></td>
</tr>
<tr id="customModelContainer" style="display: none">
<td><input type="text" id="customModel" placeholder="Enter custom model name" style="width: 95%"></td>
</tr>
<tr>
<td><button onclick="saveSettings()" style="width: 95%">Save Settings</button></td>
</tr>
</table>
<hr style="border: 1px solid #ccc; margin: 15px 0;">
<h3>Chat Management</h3>
<table width="100%">
<tr>
<td><b>Chat Name:</b></td>
</tr>
<tr>
<td><input type="text" id="chatName" placeholder="Enter chat name" style="width: 95%"></td>
</tr>
<tr>
<td><button onclick="saveChat()" style="width: 95%">Save Current Chat</button></td>
</tr>
<tr>
<td><button onclick="loadChatList()" style="width: 95%">Load Saved Chat</button></td>
</tr>
<tr>
<td><div id="chatList"></div></td>
</tr>
</table>
</div>
</td>
<td width="70%">
<div class="panel">
<h2>Chat Interface</h2>
<div class="chat-container" id="chatMessages">
<!-- Chat messages will appear here -->
</div>
<br>
<table width="100%">
<tr>
<td width="80%"><input type="text" id="userInput" placeholder="Type your message..." style="width: 95%" onkeypress="if(event.key === 'Enter') sendMessage()"></td>
<td width="20%">
<button onclick="sendMessage()" style="width: 95%">Send</button>
<br><br>
<button onclick="clearChat()" style="width: 95%">Clear</button>
</td>
</tr>
</table>
</div>
</td>
</tr>
</table>
</div>
<script>
// Storage keys
const STORAGE_KEYS = {
SETTINGS: 'llm_chatcraft_settings',
CHATS: 'llm_chatcraft_chats',
CURRENT_CHAT: 'llm_chatcraft_current_chat'
};
// Current chat state
let currentChat = {
messages: [],
timestamp: Date.now()
};
// Initialize the application
document.addEventListener('DOMContentLoaded', function() {
loadSettings();
loadCurrentChat();
setupEventListeners();
});
function setupEventListeners() {
// Model selection change
document.getElementById('model').addEventListener('change', function() {
const customContainer = document.getElementById('customModelContainer');
customContainer.classList.toggle('hidden', this.value !== 'custom');
});
}
function loadSettings() {
const settings = JSON.parse(localStorage.getItem(STORAGE_KEYS.SETTINGS) || '{}');
document.getElementById('apiKey').value = settings.apiKey || '';
document.getElementById('apiEndpoint').value = settings.apiEndpoint || 'https://api.openai.com/v1';
document.getElementById('model').value = settings.model || 'gpt-3.5-turbo';
document.getElementById('customModel').value = settings.customModel || '';
const customContainer = document.getElementById('customModelContainer');
customContainer.classList.toggle('hidden', settings.model !== 'custom');
}
function saveSettings() {
const settings = {
apiKey: document.getElementById('apiKey').value,
apiEndpoint: document.getElementById('apiEndpoint').value || 'https://api.openai.com/v1',
model: document.getElementById('model').value,
customModel: document.getElementById('customModel').value
};
localStorage.setItem(STORAGE_KEYS.SETTINGS, JSON.stringify(settings));
alert('Settings saved successfully!');
}
function loadCurrentChat() {
const savedChat = localStorage.getItem(STORAGE_KEYS.CURRENT_CHAT);
if (savedChat) {
currentChat = JSON.parse(savedChat);
displayMessages();
}
}
function saveCurrentChat() {
localStorage.setItem(STORAGE_KEYS.CURRENT_CHAT, JSON.stringify(currentChat));
}
async function sendMessage() {
const userInput = document.getElementById('userInput');
const message = userInput.value.trim();
if (!message) return;
// Add user message to chat
addMessage('user', message);
userInput.value = '';
// Get settings
const settings = JSON.parse(localStorage.getItem(STORAGE_KEYS.SETTINGS) || '{}';
if (!settings.apiKey) {
addMessage('assistant', 'Please set your API key in the settings first.');
return;
}
try {
const response = await fetch(`${settings.apiEndpoint}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${settings.apiKey}`
},
body: JSON.stringify({
model: settings.model === 'custom' ? settings.customModel : settings.model,
messages: currentChat.messages,
stream: false
})
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
const assistantMessage = data.choices[0].message.content;
addMessage('assistant', assistantMessage);
} catch (error) {
addMessage('assistant', `Error: ${error.message}. Please check your settings and try again.`);
}
}
function addMessage(role, content) {
currentChat.messages.push({ role, content, timestamp: Date.now() });
displayMessages();
saveCurrentChat();
}
function displayMessages() {
const chatMessages = document.getElementById('chatMessages');
chatMessages.innerHTML = '';
currentChat.messages.forEach(message => {
const messageDiv = document.createElement('div');
messageDiv.className = `p-3 rounded mb-2 ${role === 'user' ? 'message-user' : 'message-assistant'}`;
messageDiv.textContent = `${role === 'user' ? 'You: ' : 'Assistant: '}${message.content}`;
chatMessages.appendChild(messageDiv);
});
chatMessages.scrollTop = chatMessages.scrollHeight;
}
function clearChat() {
currentChat = {
messages: [],
timestamp: Date.now()
};
displayMessages();
saveCurrentChat();
}
function saveChat() {
const chatName = document.getElementById('chatName').value.trim();
if (!chatName) {
alert('Please enter a chat name');
return;
}
const savedChats = JSON.parse(localStorage.getItem(STORAGE_KEYS.CHATS) || '{}');
savedChats[chatName] = {
...currentChat,
name: chatName,
savedAt: Date.now()
};
localStorage.setItem(STORAGE_KEYS.CHATS, JSON.stringify(savedChats));
document.getElementById('chatName').value = '';
alert('Chat saved successfully!');
loadChatList();
}
function loadChatList() {
const savedChats = JSON.parse(localStorage.getItem(STORAGE_KEYS.CHATS) || '{}');
const chatList = document.getElementById('chatList');
chatList.innerHTML = '';
Object.keys(savedChats).forEach(chatName => {
const chat = savedChats[chatName];
const chatItem = document.createElement('div');
chatItem.className = 'flex justify-between items-center p-2 border rounded';
chatItem.innerHTML = `
<span>${chatName}</span>
<div>
<button onclick="loadChat('${chatName}')" class="bg-blue-500 text-white px-2 py-1 rounded text-sm">Load</button>
<button onclick="deleteChat('${chatName}')" class="bg-red-500 text-white px-2 py-1 rounded text-sm ml-1">Delete</button>
</div>
`;
chatList.appendChild(chatItem);
});
}
function loadChat(chatName) {
const savedChats = JSON.parse(localStorage.getItem(STORAGE_KEYS.CHATS) || '{}');
const chat = savedChats[chatName];
if (chat) {
currentChat = { ...chat };
displayMessages();
alert(`Chat "${chatName}" loaded successfully!`);
}
}
function deleteChat(chatName) {
if (confirm(`Are you sure you want to delete the chat "${chatName}"?`)) {
const savedChats = JSON.parse(localStorage.getItem(STORAGE_KEYS.CHATS) || '{}');
delete savedChats[chatName];
localStorage.setItem(STORAGE_KEYS.CHATS, JSON.stringify(savedChats));
loadChatList();
}
}
</script>
</body>
</html>