|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>ArmaForge Assistant - Live Chat</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<script src="https://unpkg.com/feather-icons"></script> |
|
|
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
|
|
<style> |
|
|
.chat-container { |
|
|
height: calc(100vh - 180px); |
|
|
} |
|
|
.code-block { |
|
|
font-family: 'Courier New', monospace; |
|
|
background-color: #1e293b; |
|
|
border-left: 3px solid #10b981; |
|
|
} |
|
|
.user-bubble { |
|
|
background-color: #1e40af; |
|
|
border-radius: 1rem 1rem 0 1rem; |
|
|
} |
|
|
.ai-bubble { |
|
|
background-color: #334155; |
|
|
border-radius: 1rem 1rem 1rem 0; |
|
|
} |
|
|
.typing-indicator span { |
|
|
height: 8px; |
|
|
width: 8px; |
|
|
background-color: #6ee7b7; |
|
|
border-radius: 50%; |
|
|
display: inline-block; |
|
|
margin: 0 2px; |
|
|
animation: bounce 1.4s infinite ease-in-out both; |
|
|
} |
|
|
.typing-indicator span:nth-child(1) { |
|
|
animation-delay: -0.32s; |
|
|
} |
|
|
.typing-indicator span:nth-child(2) { |
|
|
animation-delay: -0.16s; |
|
|
} |
|
|
@keyframes bounce { |
|
|
0%, 80%, 100% { transform: scale(0); } |
|
|
40% { transform: scale(1); } |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="bg-gray-900 text-gray-100 min-h-screen"> |
|
|
<div class="container mx-auto px-4 py-6"> |
|
|
|
|
|
<header class="flex justify-between items-center mb-6 border-b border-teal-500 pb-4"> |
|
|
<div class="flex items-center space-x-3"> |
|
|
<i data-feather="cpu" class="w-8 h-8 text-teal-400"></i> |
|
|
<h1 class="text-2xl font-bold text-teal-400">ArmaForge Assistant</h1> |
|
|
</div> |
|
|
<button class="text-teal-300 hover:text-white flex items-center"> |
|
|
<i data-feather="settings" class="mr-2"></i> Settings |
|
|
</button> |
|
|
</header> |
|
|
|
|
|
|
|
|
<div class="chat-container overflow-y-auto mb-4 space-y-4"> |
|
|
|
|
|
<div class="flex"> |
|
|
<div class="ai-bubble max-w-3xl p-4"> |
|
|
<p class="font-medium">Welcome to ArmaForge Assistant! 👋</p> |
|
|
<p class="mt-1">I'm here to help with all your Arma Reforger modding questions. You can ask about:</p> |
|
|
<ul class="list-disc pl-5 mt-2 space-y-1"> |
|
|
<li>Script debugging and optimization</li> |
|
|
<li>Workbench setup and configuration</li> |
|
|
<li>Mod packaging and deployment</li> |
|
|
<li>Game mechanics implementation</li> |
|
|
</ul> |
|
|
<p class="mt-2">What would you like to know about today?</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="flex justify-end"> |
|
|
<div class="user-bubble max-w-3xl p-4"> |
|
|
<p>How do I spawn AI units in a mission script?</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="flex"> |
|
|
<div class="ai-bubble max-w-3xl p-4"> |
|
|
<p>Here's a basic example of spawning AI units in Arma Reforger:</p> |
|
|
<div class="code-block p-4 rounded-lg mt-2 text-sm overflow-x-auto"> |
|
|
<pre class="text-green-400"> |
|
|
// Create a group for the AI |
|
|
auto group = GetGame().CreateGroup(); |
|
|
|
|
|
// Spawn a soldier at position [0,0,0] |
|
|
auto unit = GetGame().CreateCharacter(group, "Faction_US_Soldier_Rifleman_M4A1"); |
|
|
unit.SetPosition(vector.Zero); |
|
|
|
|
|
// Add waypoints |
|
|
auto waypoint = group.AddWaypoint(vector(100, 0, 100)); |
|
|
waypoint.SetType(EWaypointType.MOVE);</pre> |
|
|
</div> |
|
|
<p class="mt-3">Remember to set appropriate faction templates and loadouts for your units.</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="flex items-center space-x-2 opacity-75 hidden" id="typingIndicator"> |
|
|
<div class="ai-bubble px-4 py-2"> |
|
|
<div class="typing-indicator"> |
|
|
<span></span> |
|
|
<span></span> |
|
|
<span></span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="bg-gray-800 rounded-lg p-4"> |
|
|
<form id="chatForm" class="flex items-center space-x-2"> |
|
|
<input type="text" id="userInput" placeholder="Ask about Arma Reforger modding..." |
|
|
class="flex-grow bg-gray-700 border border-gray-600 rounded-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-teal-500"> |
|
|
<button type="submit" class="bg-teal-600 hover:bg-teal-500 p-3 rounded-lg"> |
|
|
<i data-feather="send" class="w-5 h-5"></i> |
|
|
</button> |
|
|
</form> |
|
|
<div class="flex justify-between mt-2 text-xs text-gray-400"> |
|
|
<div class="flex space-x-2"> |
|
|
<button class="hover:text-teal-400 flex items-center"> |
|
|
<i data-feather="code" class="w-3 h-3 mr-1"></i> Code |
|
|
</button> |
|
|
<button class="hover:text-teal-400 flex items-center"> |
|
|
<i data-feather="image" class="w-3 h-3 mr-1"></i> Image |
|
|
</button> |
|
|
</div> |
|
|
<div> |
|
|
<button class="hover:text-teal-400 flex items-center"> |
|
|
<i data-feather="trash-2" class="w-3 h-3 mr-1"></i> Clear |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<script> |
|
|
|
|
|
feather.replace(); |
|
|
|
|
|
const API_URL = 'http://localhost:11434/api/chat'; |
|
|
const chatContainer = document.querySelector('.chat-container'); |
|
|
const chatForm = document.getElementById('chatForm'); |
|
|
const userInput = document.getElementById('userInput'); |
|
|
const typingIndicator = document.getElementById('typingIndicator'); |
|
|
|
|
|
|
|
|
function addMessage(role, content) { |
|
|
const msgDiv = document.createElement('div'); |
|
|
msgDiv.className = role === 'user' ? 'flex justify-end' : 'flex'; |
|
|
|
|
|
const bubbleClass = role === 'user' ? 'user-bubble' : 'ai-bubble'; |
|
|
msgDiv.innerHTML = ` |
|
|
<div class="${bubbleClass} max-w-3xl p-4"> |
|
|
<p>${content}</p> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
chatContainer.appendChild(msgDiv); |
|
|
chatContainer.scrollTop = chatContainer.scrollHeight; |
|
|
} |
|
|
|
|
|
|
|
|
async function getAIResponse(prompt) { |
|
|
typingIndicator.classList.remove('hidden'); |
|
|
|
|
|
try { |
|
|
const response = await fetch(API_URL, { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/json' |
|
|
}, |
|
|
body: JSON.stringify({ |
|
|
model: "llama2", |
|
|
messages: [ |
|
|
{ |
|
|
role: "user", |
|
|
content: prompt |
|
|
} |
|
|
], |
|
|
stream: false |
|
|
}) |
|
|
}); |
|
|
|
|
|
if (!response.ok) throw new Error('Network response was not ok'); |
|
|
const data = await response.json(); |
|
|
return data.message?.content || "Sorry, I couldn't process the response."; |
|
|
} catch (error) { |
|
|
console.error('Error:', error); |
|
|
return "Sorry, I encountered an error. Please ensure OLLAMA is running and accessible at http://localhost:11434"; |
|
|
} finally { |
|
|
typingIndicator.classList.add('hidden'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
chatForm.addEventListener('submit', async function(e) { |
|
|
e.preventDefault(); |
|
|
const message = userInput.value.trim(); |
|
|
|
|
|
if (message) { |
|
|
addMessage('user', message); |
|
|
userInput.value = ''; |
|
|
|
|
|
const aiResponse = await getAIResponse(message); |
|
|
addMessage('assistant', aiResponse); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
userInput.addEventListener('keydown', function(e) { |
|
|
if (e.key === 'Enter' && !e.shiftKey) { |
|
|
e.preventDefault(); |
|
|
chatForm.dispatchEvent(new Event('submit')); |
|
|
} |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
|