Jump to content

MediaWiki:Common.js

From Retina Chip Wiki
Revision as of 09:19, 4 September 2026 by Ronald (talk | contribs) (add chat widget)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/**
 * Retina Chip Wiki chat widget (wiki3.rohmega.com).
 * Floating chat bubble in the bottom-right. Grounded Q&A over the wiki.
 * Public wiki -> the backend enforces a daily token budget.
 *
 * ES5-safe only: ResourceLoader minifier rejects trailing commas in call args,
 * arrow functions, template literals.
 */
(function () {
    'use strict';

    // Don't double-inject
    if (document.getElementById('rmg-chat-root')) {
        return;
    }

    var CHAT_ENDPOINT = '/chat/chat';  // same-origin via Caddy /chat/* -> :8111

    function buildUI() {
        var root = document.createElement('div');
        root.id = 'rmg-chat-root';

        // --- floating toggle button ---
        var btn = document.createElement('button');
        btn.id = 'rmg-chat-toggle';
        btn.setAttribute('aria-label', 'Ask the wiki');
        btn.textContent = '💬';
        root.appendChild(btn);

        // --- panel ---
        var panel = document.createElement('div');
        panel.id = 'rmg-chat-panel';
        panel.style.display = 'none';

        var head = document.createElement('div');
        head.id = 'rmg-chat-head';
        var title = document.createElement('span');
        title.textContent = 'Ask the Retina Chip Wiki';
        var close = document.createElement('button');
        close.textContent = '×';
        close.setAttribute('aria-label', 'Close');
        head.appendChild(title);
        head.appendChild(close);
        panel.appendChild(head);

        var msgs = document.createElement('div');
        msgs.id = 'rmg-chat-msgs';
        panel.appendChild(msgs);

        var foot = document.createElement('div');
        foot.id = 'rmg-chat-foot';
        var input = document.createElement('input');
        input.id = 'rmg-chat-input';
        input.setAttribute('placeholder', 'Ask about blocks, ADRs, methods…');
        var send = document.createElement('button');
        send.id = 'rmg-chat-send';
        send.textContent = 'Send';
        foot.appendChild(input);
        foot.appendChild(send);
        panel.appendChild(foot);

        root.appendChild(panel);
        document.body.appendChild(root);

        // wire events
        btn.addEventListener('click', function () {
            var open = panel.style.display !== 'none';
            panel.style.display = open ? 'none' : 'block';
        });
        close.addEventListener('click', function () {
            panel.style.display = 'none';
        });
        send.addEventListener('click', ask);
        input.addEventListener('keydown', function (e) {
            if (e.key === 'Enter') {
                ask();
            }
        });
    }

    function addBubble(text, who) {
        var msgs = document.getElementById('rmg-chat-msgs');
        var b = document.createElement('div');
        b.className = 'rmg-bubble ' + who;
        b.textContent = text;
        msgs.appendChild(b);
        msgs.scrollTop = msgs.scrollHeight;
        return b;
    }

    function ask() {
        var input = document.getElementById('rmg-chat-input');
        var send = document.getElementById('rmg-chat-send');
        var q = input.value.trim();
        if (!q) {
            return;
        }
        input.value = '';
        addBubble(q, 'user');
        send.disabled = true;

        var typing = addBubble('…', 'bot');

        var xhr = new XMLHttpRequest();
        xhr.open('POST', CHAT_ENDPOINT, true);
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.onreadystatechange = function () {
            if (xhr.readyState !== 4) {
                return;
            }
            send.disabled = false;
            if (xhr.status === 200) {
                try {
                    var d = JSON.parse(xhr.responseText);
                    typing.textContent = d.answer || '(no answer)';
                } catch (err) {
                    typing.textContent = 'Error reading response.';
                }
            } else {
                try {
                    var e = JSON.parse(xhr.responseText);
                    typing.textContent = e.answer || ('Error ' + xhr.status);
                } catch (err) {
                    typing.textContent = 'Error ' + xhr.status;
                }
            }
        };
        xhr.send(JSON.stringify({ message: q }));
    }

    // Run after DOM ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', buildUI);
    } else {
        buildUI();
    }
})();