174 lines
9.2 KiB
JavaScript
174 lines
9.2 KiB
JavaScript
// AI Chat Panel — DashCaddy AI Intent Router frontend
|
||
(function() {
|
||
let chatMessages = [];
|
||
let chatHistory = [];
|
||
|
||
function createChatPanel() {
|
||
const existing = document.getElementById('ai-chat-panel');
|
||
if (existing) existing.remove();
|
||
|
||
const panel = document.createElement('div');
|
||
panel.id = 'ai-chat-panel';
|
||
panel.style.cssText = `
|
||
position: fixed; bottom: 80px; right: 20px; width: 400px; max-width: 95vw;
|
||
height: 500px; max-height: 70vh; background: var(--bg-card, #1a1a2e);
|
||
border: 1px solid var(--border, #333); border-radius: 12px;
|
||
display: none; flex-direction: column; z-index: 9999;
|
||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||
`;
|
||
|
||
panel.innerHTML = `
|
||
<div style="padding: 14px 18px; border-bottom: 1px solid var(--border,#333); display: flex; align-items: center; gap: 10px;">
|
||
<span style="font-size: 1.3rem;">🤖</span>
|
||
<div style="flex:1;">
|
||
<div style="font-weight: 700; font-size: 0.95rem;">AI Assistant</div>
|
||
<div id="ai-status" style="font-size: 0.72rem; color: var(--text-muted,#666);">Pattern matching • No key needed</div>
|
||
</div>
|
||
<button onclick="document.getElementById('ai-chat-panel').style.display='none'" style="background:none;border:none;font-size:1.4rem;cursor:pointer;color:var(--text-muted,#888);">×</button>
|
||
</div>
|
||
<div id="ai-chat-messages" style="flex:1; overflow-y:auto; padding:14px; display:flex; flex-direction:column; gap:10px;">
|
||
<div style="background:rgba(99,102,241,0.08); border-radius:10px; padding:12px; font-size:0.85rem; color:var(--text-muted,#aaa);">
|
||
👋 Hi! I can help you deploy apps, diagnose issues, and manage your server. Try:
|
||
<div style="margin-top:8px; display:flex; flex-wrap:wrap; gap:6px;">
|
||
<button class="ai-suggestion" onclick="window.aiChat.send('"Deploy Plex"')" style="background:rgba(99,102,241,0.15); border:1px solid rgba(99,102,241,0.3); border-radius:16px; padding:4px 12px; font-size:0.78rem; cursor:pointer; color:#a5b4fc;">Deploy Plex</button>
|
||
<button class="ai-suggestion" onclick="window.aiChat.send('"I want to stream movies"')" style="background:rgba(99,102,241,0.15); border:1px solid rgba(99,102,241,0.3); border-radius:16px; padding:4px 12px; font-size:0.78rem; cursor:pointer; color:#a5b4fc;">Stream movies</button>
|
||
<button class="ai-suggestion" onclick="window.aiChat.send('"Block ads on my network"')" style="background:rgba(99,102,241,0.15); border:1px solid rgba(99,102,241,0.3); border-radius:16px; padding:4px 12px; font-size:0.78rem; cursor:pointer; color:#a5b4fc;">Block ads</button>
|
||
<button class="ai-suggestion" onclick="window.aiChat.send('"Is everything OK?')" style="background:rgba(99,102,241,0.15); border:1px solid rgba(99,102,241,0.3); border-radius:16px; padding:4px 12px; font-size:0.78rem; cursor:pointer; color:#a5b4fc;">System health</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div style="padding:12px; border-top:1px solid var(--border,#333);">
|
||
<div style="display:flex; gap:8px;">
|
||
<input id="ai-chat-input" type="text" placeholder="Ask me anything..."
|
||
onkeypress="if(event.key==='Enter') window.aiChat.send()"
|
||
style="flex:1; background:var(--bg-input,#0f0f1a); border:1px solid var(--border,#333); border-radius:8px; padding:10px 14px; color:var(--text,#fff); font-size:0.88rem; outline:none;">
|
||
<button onclick="window.aiChat.send()" style="background:#6366f1; border:none; border-radius:8px; padding:0 16px; color:#fff; cursor:pointer; font-weight:600;">→</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
document.body.appendChild(panel);
|
||
}
|
||
|
||
function toggle() {
|
||
let panel = document.getElementById('ai-chat-panel');
|
||
if (!panel) { createChatPanel(); panel = document.getElementById('ai-chat-panel'); }
|
||
panel.style.display = panel.style.display === 'flex' ? 'none' : 'flex';
|
||
if (panel.style.display === 'flex') document.getElementById('ai-chat-input').focus();
|
||
}
|
||
|
||
function addMessage(role, text, extra) {
|
||
const container = document.getElementById('ai-chat-messages');
|
||
if (!container) return;
|
||
const div = document.createElement('div');
|
||
const isUser = role === 'user';
|
||
div.style.cssText = isUser
|
||
? 'align-self:flex-end; background:rgba(99,102,241,0.15); border-radius:10px 10px 2px 10px; padding:10px 14px; max-width:85%; font-size:0.85rem;'
|
||
: 'align-self:flex-start; background:rgba(255,255,255,0.05); border-radius:10px 10px 10px 2px; padding:10px 14px; max-width:85%; font-size:0.85rem;';
|
||
div.textContent = text;
|
||
container.appendChild(div);
|
||
|
||
// Extra content (recommendations, action buttons)
|
||
if (extra) {
|
||
const extraDiv = document.createElement('div');
|
||
extraDiv.style.cssText = 'align-self:flex-start; max-width:85%; margin-top:4px;';
|
||
if (extra.disclaimer) {
|
||
const disc = document.createElement('div');
|
||
disc.style.cssText = 'font-size:0.7rem; color:var(--text-muted,#666); padding:6px 8px; line-height:1.4; margin-top:4px;';
|
||
disc.textContent = '⚠️ ' + extra.disclaimer;
|
||
extraDiv.appendChild(disc);
|
||
}
|
||
if (extra.recommendations) {
|
||
extra.recommendations.forEach(rec => {
|
||
const btn = document.createElement('button');
|
||
btn.textContent = `📦 Deploy ${rec.app}`;
|
||
btn.style.cssText = 'display:block; margin:4px 0; background:rgba(34,197,94,0.12); border:1px solid rgba(34,197,94,0.3); border-radius:8px; padding:8px 12px; cursor:pointer; font-size:0.8rem; color:#4ade80;';
|
||
btn.onclick = () => window.aiChat.send('Deploy ' + rec.app);
|
||
extraDiv.appendChild(btn);
|
||
});
|
||
}
|
||
if (extra.appId) {
|
||
const btn = document.createElement('button');
|
||
btn.textContent = `🚀 Deploy ${extra.appId}`;
|
||
btn.style.cssText = 'display:block; margin:4px 0; background:rgba(34,197,94,0.12); border:1px solid rgba(34,197,94,0.3); border-radius:8px; padding:8px 12px; cursor:pointer; font-size:0.8rem; color:#4ade80;';
|
||
btn.onclick = () => window.aiChat.deploy(extra.appId);
|
||
extraDiv.appendChild(btn);
|
||
}
|
||
if (extra.suggestions) {
|
||
const list = document.createElement('div');
|
||
list.style.cssText = 'font-size:0.78rem; color:var(--text-muted,#888); padding:4px 0;';
|
||
list.innerHTML = extra.suggestions.map(s => '• ' + s).join('<br>');
|
||
extraDiv.appendChild(list);
|
||
}
|
||
if (extra.action === 'diagnose') {
|
||
const btn = document.createElement('button');
|
||
btn.textContent = '🔍 Run Diagnostics';
|
||
btn.style.cssText = 'display:block; margin:4px 0; background:rgba(251,191,36,0.12); border:1px solid rgba(251,191,36,0.3); border-radius:8px; padding:8px 12px; cursor:pointer; font-size:0.8rem; color:#fbbf24;';
|
||
btn.onclick = () => window.location.href = '#health';
|
||
extraDiv.appendChild(btn);
|
||
}
|
||
if (extra.action === 'backup') {
|
||
const btn = document.createElement('button');
|
||
btn.textContent = '💾 Create Backup';
|
||
btn.style.cssText = 'display:block; margin:4px 0; background:rgba(99,102,241,0.12); border:1px solid rgba(99,102,241,0.3); border-radius:8px; padding:8px 12px; cursor:pointer; font-size:0.8rem; color:#a5b4fc;';
|
||
btn.onclick = () => { if (window.createBackup) window.createBackup(); };
|
||
extraDiv.appendChild(btn);
|
||
}
|
||
container.appendChild(extraDiv);
|
||
}
|
||
container.scrollTop = container.scrollHeight;
|
||
}
|
||
|
||
async function send(prefilled) {
|
||
const input = document.getElementById('ai-chat-input');
|
||
const message = prefilled || input.value.trim();
|
||
if (!message) return;
|
||
input.value = '';
|
||
|
||
addMessage('user', message);
|
||
|
||
// Typing indicator
|
||
const container = document.getElementById('ai-chat-messages');
|
||
const typing = document.createElement('div');
|
||
typing.id = 'ai-typing';
|
||
typing.style.cssText = 'align-self:flex-start; color:var(--text-muted,#666); font-size:0.8rem; padding:8px 14px;';
|
||
typing.textContent = '🤖 Thinking...';
|
||
container.appendChild(typing);
|
||
container.scrollTop = container.scrollHeight;
|
||
|
||
try {
|
||
const res = await secureFetch('/api/v1/ai/intent', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ message, context: {} })
|
||
});
|
||
const data = await res.json();
|
||
typing.remove();
|
||
|
||
if (data.success !== false && data.data) {
|
||
const r = data.data;
|
||
const response = r.response || {};
|
||
addMessage('ai', response.message || r.response?.message || 'Action: ' + (r.action || r.intent), response);
|
||
} else {
|
||
addMessage('ai', data.error || 'Sorry, something went wrong.');
|
||
}
|
||
} catch (e) {
|
||
typing.remove();
|
||
addMessage('ai', 'Connection error: ' + e.message);
|
||
}
|
||
}
|
||
|
||
async function deploy(appId) {
|
||
addMessage('user', 'Deploy ' + appId);
|
||
addMessage('ai', `Launching ${appId} from the catalog...`);
|
||
// Trigger the app selector with this app
|
||
if (window.openAppSelector) {
|
||
window.openAppSelector(appId);
|
||
} else {
|
||
addMessage('ai', `Search for ${appId} in the App Catalog to deploy it.`);
|
||
}
|
||
}
|
||
|
||
window.aiChat = { send, deploy, toggle };
|
||
window.toggleAIChat = toggle;
|
||
})();
|