| <!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> |
|
|