IMPROVEMENT (infrastructure): javascript code is now based on jQuery

This commit is contained in:
Frederico @ VilaRosa02 2025-09-18 09:42:28 +00:00
parent 42206afc88
commit 00fa5ae75a
4 changed files with 105 additions and 92 deletions

1
files Normal file
View File

@ -0,0 +1 @@
main.js

View File

@ -6,6 +6,7 @@
<title>FMF GPT</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="/main.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
</head>
<body>
<div class="container py-4 chat-wrap">

140
main.js
View File

@ -1,102 +1,112 @@
// Minimal client that sends CSRF, renders assistant HTML, and toggles theme.
// Minimal client that sends CSRF, renders assistant HTML, and toggles theme. (jQuery version)
const qs = (s, r = document) => r.querySelector(s);
const chatEl = qs('#chat');
const formEl = qs('#chatForm');
const msgInput = qs('#messageInput');
const sysInput = qs('#systemInput');
const resetBtn = qs('#resetBtn');
const themeBtn = qs('#themeToggle');
$(function () {
// Element refs
const $chat = $('#chat');
const $form = $('#chatForm');
const $msg = $('#messageInput');
const $sys = $('#systemInput');
const $reset = $('#resetBtn');
const $theme = $('#themeToggle');
function post(action, data = {}) {
const body = new URLSearchParams({ action, csrf: (window.__APP__ && window.__APP__.csrf) || '', ...data });
return fetch(location.pathname, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
body
}).then(r => r.json());
}
// Helpers
function escapeHtmlWithNewlines(s) {
// jQuery-safe escape via .text(), then convert \n -> <br>
return $('<div>').text(s).html().replace(/\n/g, '<br>');
}
// Simple helpers to append messages to the chat
function appendUserMessage(text) {
const wrap = document.createElement('div');
wrap.className = 'msg user';
wrap.innerHTML = `
function nowTime() {
const d = new Date();
const hh = String(d.getHours()).padStart(2, '0');
const mm = String(d.getMinutes()).padStart(2, '0');
return `${hh}:${mm}`;
}
function appendUserMessage(text) {
const html = `
<div class="msg user">
<div class="content">${escapeHtmlWithNewlines(text)}</div>
<div class="time">${nowTime()}</div>
`;
chatEl.appendChild(wrap);
chatEl.scrollTop = chatEl.scrollHeight;
}
</div>`;
$chat.append(html);
$chat.prop('scrollTop', $chat[0].scrollHeight);
}
function appendAssistantHtml(html) {
function appendAssistantHtml(html) {
const clean = (window.DOMPurify ? DOMPurify.sanitize(html) : html);
const wrap = document.createElement('div');
wrap.className = 'msg assistant';
wrap.innerHTML = `
const block = `
<div class="msg assistant">
<div class="content">${clean}</div>
<div class="time">${nowTime()}</div>
`;
chatEl.appendChild(wrap);
chatEl.scrollTop = chatEl.scrollHeight;
}
</div>`;
$chat.append(block);
$chat.prop('scrollTop', $chat[0].scrollHeight);
}
function escapeHtmlWithNewlines(s) {
const t = document.createElement('div');
t.innerText = s;
return t.innerHTML.replace(/\n/g, '<br>');
}
// AJAX POST helper (x-www-form-urlencoded)
function post(action, data = {}) {
const csrf = (window.__APP__ && window.__APP__.csrf) || '';
const payload = new URLSearchParams({ action, csrf, ...data }).toString();
return $.ajax({
url: location.pathname,
method: 'POST',
data: payload,
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
dataType: 'json'
});
}
function nowTime() {
const d = new Date();
const hh = String(d.getHours()).padStart(2,'0');
const mm = String(d.getMinutes()).padStart(2,'0');
return `${hh}:${mm}`;
}
// Submit chat
formEl?.addEventListener('submit', async (e) => {
// Submit chat
$form.on('submit', async function (e) {
e.preventDefault();
const message = msgInput.value.trim();
const system = sysInput ? sysInput.value.trim() : '';
const message = ($msg.val() || '').toString().trim();
const system = $sys.length ? ($sys.val() || '').toString().trim() : '';
if (!message) return;
appendUserMessage(message);
msgInput.value = '';
msgInput.focus();
$msg.val('').focus();
try {
const res = await post('chat', { message, system });
if (res.ok) {
// Prefer server-rendered sanitized HTML for assistant
appendAssistantHtml(res.reply_html || escapeHtmlWithNewlines(res.reply || ''));
} else {
appendAssistantHtml(`<p class="text-danger">Error: ${res.error || 'Unknown'}</p>`);
}
});
} catch (err) {
appendAssistantHtml(`<p class="text-danger">Network error</p>`);
}
});
// Reset chat
resetBtn?.addEventListener('click', async () => {
// Reset chat
$reset.on('click', async function () {
try {
const res = await post('reset', {});
if (res.ok) {
chatEl.innerHTML = `<div class="text-secondary">Start the conversation below…</div>`;
$chat.html('<div class="text-secondary">Start the conversation below…</div>');
}
});
} catch (_) { /* ignore */ }
});
// Theme toggle
themeBtn?.addEventListener('click', async () => {
// Theme toggle
$theme.on('click', async function () {
const current = (window.__APP__ && window.__APP__.theme) || 'dark';
const next = current === 'dark' ? 'light' : 'dark';
try {
const res = await post('set_theme', { theme: next });
if (res.ok) {
document.documentElement.setAttribute('data-theme', next);
window.__APP__.theme = next;
if (window.__APP__) window.__APP__.theme = next;
}
});
} catch (_) { /* ignore */ }
});
// With "enter" key press, send message
msgInput.addEventListener('keydown', e => {
// With "enter" key press, send message (Shift+Enter => newline)
$msg.on('keydown', function (e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault(); // stop newline
formEl.dispatchEvent(new Event('submit', {cancelable: true}));
e.preventDefault();
$form.trigger('submit');
}
});
});

1
ticket Normal file
View File

@ -0,0 +1 @@
give me a drop-in replacement that uses jQuery