import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/+esm"; import { getAll, putItem } from "./db.js"; import { SensoryDiceRoller } from "./dice.js"; import { searchChunks } from "./rag.js"; async function callHuggingFaceGM(systemPrompt, userAction) { // MUST use the direct subdomain host (NOT huggingface.co/spaces/...) const SPACE_URL = "https://dippergl231-myrpg.hf.space"; // 1. Send action to Gradio queue const res = await fetch(`${SPACE_URL}/gradio_api/call/predict`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ data: [systemPrompt, userAction] }) }); if (!res.ok) { throw new Error(`HF Space returned status ${res.status}`); } const { event_id } = await res.json(); // 2. Receive stream response return new Promise((resolve, reject) => { const eventSource = new EventSource(`${SPACE_URL}/gradio_api/call/predict/${event_id}`); eventSource.onmessage = (e) => { const data = JSON.parse(e.data); if (data[0]) { eventSource.close(); resolve(data[0]); } }; eventSource.onerror = (err) => { eventSource.close(); reject(err); }; }); } // Global App State let hfClient = null; let rulebookChunks = []; let currentPointsMax = 27; const defaultStats = { "D&D 5e": { STR: 8, DEX: 8, CON: 8, INT: 8, WIS: 8, CHA: 8 }, "Pathfinder 2e": { STR: 10, DEX: 10, CON: 10, INT: 10, WIS: 10, CHA: 10 }, "GURPS": { ST: 10, DX: 10, IQ: 10, HT: 10 } }; let activeCharacter = { id: 'char_default', name: 'Hero', system: 'D&D 5e', stats: { ...defaultStats["D&D 5e"] } }; let activePrefab = { id: 'prefab_default', title: 'Dark Fantasy Campaign', tone: 'Gritty, tactical, immersive', supplements: ['Xanathar\'s Guide'] }; // Connect to HF Space API async function getClient() { if (!hfClient) { document.getElementById('gm-status').innerText = 'GM: Connecting...'; hfClient = await Client.connect("Dippergl231/MYRPG"); document.getElementById('gm-status').innerText = 'GM: Ready'; } return hfClient; } // Tab Navigation Setup document.querySelectorAll('.nav-btn').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active')); btn.classList.add('active'); document.getElementById(btn.dataset.tab).classList.add('active'); }); }); // Character Point Buy Calculator & UI function renderStats() { const container = document.getElementById('stats-container'); container.innerHTML = ''; let currentCost = 0; Object.entries(activeCharacter.stats).forEach(([stat, val]) => { if (activeCharacter.system === 'GURPS') { currentCost += (val - 10) * 10; } else { currentCost += (val - 8); } const box = document.createElement('div'); box.className = 'stat-box'; box.innerHTML = ` ${stat}: ${val}
${item.content}
`; container.appendChild(card); }); } // Rulebook Upload (.txt RAG) document.getElementById('file-rulebook').addEventListener('change', (e) => { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (evt) => { const text = evt.target.result; rulebookChunks = text.split('\n\n').filter(c => c.trim().length > 20); alert(`Rulebook loaded! Split into ${rulebookChunks.length} search chunks.`); }; reader.readAsText(file); }); // Main Action Dispatch to HF Space Engine document.getElementById('btn-send').onclick = async () => { const userInput = document.getElementById('user-input'); const userAction = userInput.value.trim(); if (!userAction) return; // Add message to chat display const chatArea = document.getElementById('chat-history'); chatArea.innerHTML += `