PHP Classes

File: public_html/js/ai-assistant.js

Recommend this page to a friend!
  Packages of Matthew Asham   Binkterm PHP   public_html/js/ai-assistant.js   Download  
File: public_html/js/ai-assistant.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Binkterm PHP
Bulletin board system based on the Web
Author: By
Last change:
Date: 5 days ago
Size: 6,784 bytes
 

Contents

Class file image Download
/** * AI Assistant for echomail and netmail readers. * * Depends on: * - Bootstrap 5 (modal) * - window.t() for i18n * - currentMessageId (global in echomail.js / netmail.js) */ // Prompts shown when a message is open ? "this message" refers to the loaded context. const AI_ASSISTANT_PROMPTS_WITH_CONTEXT = [ { label: 'Summarize', prompt: 'Summarize the key points of this message in 2-3 sentences.' }, { label: 'Key Points', prompt: 'What are the main topics being discussed in this message?' }, { label: 'Explain Terms', prompt: 'Explain any technical or jargon terms used in this message.' }, { label: 'Thread Context', prompt: 'Summarize the full conversation thread this message belongs to.' }, ]; // Prompts shown when no message is open ? general BBS questions. const AI_ASSISTANT_PROMPTS_NO_CONTEXT = [ { label: 'Recent Activity', prompt: 'What are the most active echoareas right now?' }, { label: 'Top Posters', prompt: 'Who are the most active posters recently?' }, { label: 'Search Help', prompt: 'Help me find messages about a topic. What should I search for?' }, ]; /** Message context captured when the modal is opened. */ let _aiMessageId = null; let _aiMessageType = 'echomail'; /** * Open the AI assistant modal. * * @param {number|null} messageId ID of the message currently being viewed * @param {string} messageType 'echomail' or 'netmail' */ function openAiAssistant(messageId, messageType) { _aiMessageId = messageId || null; _aiMessageType = messageType || 'echomail'; // Reset modal state $('#aiPromptText').val(''); $('#aiResponseSection').addClass('d-none'); $('#aiResponseText').text(''); $('#aiCreditsUsed').text(''); // Context indicator const $ctx = $('#aiContextInfo').empty(); if (_aiMessageId) { const ctxMsg = window.t( 'ui.ai_assistant.context_message', { id: _aiMessageId }, 'Message #' + _aiMessageId + ' is loaded as context ? quick prompts will refer to it.' ); $('<div>') .addClass('alert alert-info py-2 small mb-0') .html('<i class="fas fa-circle-info me-2"></i>' + ctxMsg) .appendTo($ctx); $('#aiPromptText').attr( 'placeholder', window.t('ui.ai_assistant.prompt_placeholder_ctx', {}, 'Ask about this message...') ); } else { const ctxMsg = window.t( 'ui.ai_assistant.context_none', {}, 'No message is open. Open a message first for context-aware prompts, or ask a general question.' ); $('<div>') .addClass('alert alert-secondary py-2 small mb-0') .html('<i class="fas fa-circle-exclamation me-2"></i>' + ctxMsg) .appendTo($ctx); $('#aiPromptText').attr( 'placeholder', window.t('ui.ai_assistant.prompt_placeholder', {}, 'Ask the AI about this message...') ); } _updateCharCount(); // Build quick-prompt buttons ? different set based on whether context is available const prompts = _aiMessageId ? AI_ASSISTANT_PROMPTS_WITH_CONTEXT : AI_ASSISTANT_PROMPTS_NO_CONTEXT; const $container = $('#aiQuickPrompts').empty(); prompts.forEach(function(item) { $('<button>') .addClass('btn btn-outline-secondary btn-sm') .text(item.label) .on('click', function() { $('#aiPromptText').val(item.prompt); _updateCharCount(); }) .appendTo($container); }); bootstrap.Modal.getOrCreateInstance(document.getElementById('aiAssistantModal')).show(); } /** * Execute the AI assistant request. * Called by the Execute button in the modal. */ function executeAiAssistant() { const prompt = $('#aiPromptText').val().trim(); if (!prompt) { return; } if (prompt.length > 500) { const msg = window.t('errors.ai_assistant.prompt_too_long', {}, 'Prompt exceeds the 500 character limit.'); _showAiError(msg); return; } _setLoading(true); $('#aiResponseSection').addClass('d-none'); $.ajax({ url: '/api/messages/ai-assist', method: 'POST', contentType: 'application/json', data: JSON.stringify({ prompt: prompt, message_id: _aiMessageId, message_type: _aiMessageType, }), success: function(data) { _setLoading(false); if (data.success) { $('#aiResponseText').html(marked.parse(data.response)); if (typeof data.credits_used === 'number' && data.credits_used > 0) { const creditsLabel = window.t( 'ui.ai_assistant.credits_used', { credits: data.credits_used }, data.credits_used + ' credits used' ); $('#aiCreditsUsed').text(creditsLabel); } else { $('#aiCreditsUsed').text(''); } $('#aiResponseSection').removeClass('d-none'); // Update credit balance display if it exists on the page if (typeof data.balance === 'number') { $('.credit-balance-display').text(data.balance); } } else { _showAiError(window.getApiErrorMessage(data, window.t('errors.ai_assistant.failed', {}, 'AI request failed.'))); } }, error: function(xhr) { _setLoading(false); let payload = {}; try { payload = JSON.parse(xhr.responseText); } catch (_) {} _showAiError(window.getApiErrorMessage(payload, window.t('errors.ai_assistant.failed', {}, 'AI request failed.'))); }, }); } function _setLoading(loading) { $('#aiExecuteBtn').prop('disabled', loading); $('#aiExecuteSpinner').toggleClass('d-none', !loading); $('#aiExecuteIcon').toggleClass('d-none', loading); } function _showAiError(message) { $('#aiResponseText').text(message); $('#aiCreditsUsed').text(''); $('#aiResponseSection').removeClass('d-none'); } function _updateCharCount() { const remaining = 500 - ($('#aiPromptText').val() || '').length; const label = window.t('ui.ai_assistant.chars_remaining', { remaining: remaining }, remaining + ' characters remaining'); $('#aiCharCount').text(label); } // Live character counter $(document).on('input', '#aiPromptText', _updateCharCount); // Allow Ctrl+Enter to submit $(document).on('keydown', '#aiPromptText', function(e) { if (e.ctrlKey && e.key === 'Enter') { executeAiAssistant(); } });