<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Chatnificent</title>
    <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
    <style>
        :root {
            --bg: #f7f8fa;
            --surface: #ffffff;
            --border: #e2e5e9;
            --text: #1a1d21;
            --text-secondary: #6b7280;
            --accent: #2563eb;
            --accent-hover: #1d4ed8;
            --accent-subtle: rgba(37, 99, 235, 0.08);
            --accent-ring: rgba(37, 99, 235, 0.15);
            --user-bubble: #2563eb;
            --user-text: #ffffff;
            --assistant-bg: #ffffff;
            --sidebar-bg: #f1f3f5;
            --sidebar-w: 280px;
            --header-h: 56px;
            --radius: 16px;
            --radius-sm: 10px;
            --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06);
            --transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1);
        }

        html[data-theme="dark"] {
            --bg: #111318;
            --surface: #1a1d24;
            --border: #2d3139;
            --text: #e4e6ea;
            --text-secondary: #8b8f96;
            --accent: #3b82f6;
            --accent-hover: #2563eb;
            --accent-subtle: rgba(59, 130, 246, 0.12);
            --accent-ring: rgba(59, 130, 246, 0.2);
            --user-bubble: #2563eb;
            --user-text: #ffffff;
            --assistant-bg: #1e2128;
            --sidebar-bg: #15171c;
        }

        *,
        *::before,
        *::after {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
            background: var(--bg);
            color: var(--text);
            height: 100vh;
            overflow: hidden;
        }

        #header {
            height: var(--header-h);
            background: var(--surface);
            border-bottom: 1px solid var(--border);
            color: var(--text);
            display: flex;
            align-items: center;
            gap: 14px;
            padding: 0 20px;
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            z-index: 100;
        }

        #sidebar-toggle {
            background: none;
            border: none;
            color: var(--text-secondary);
            cursor: pointer;
            padding: 6px;
            border-radius: 8px;
            transition: background var(--transition), color var(--transition);
            display: flex;
            align-items: center;
            justify-content: center;
            width: 36px;
            height: 36px;
        }

        #sidebar-toggle:hover {
            background: var(--accent-subtle);
            color: var(--accent);
        }

        #header-title {
            font-size: 1.05rem;
            font-weight: 600;
            letter-spacing: -0.01em;
        }

        #header-actions {
            margin-left: auto;
            display: flex;
            align-items: center;
            gap: 6px;
        }

        #theme-toggle {
            background: none;
            border: none;
            color: var(--text-secondary);
            cursor: pointer;
            padding: 6px;
            border-radius: 8px;
            transition: background var(--transition), color var(--transition);
            display: flex;
            align-items: center;
            justify-content: center;
            width: 36px;
            height: 36px;
        }

        #theme-toggle:hover {
            background: var(--accent-subtle);
            color: var(--accent);
        }

        .theme-icon {
            display: none;
        }

        html:not([data-theme="dark"]) .theme-icon-light {
            display: block;
        }

        html[data-theme="dark"] .theme-icon-dark {
            display: block;
        }

        #new-chat-btn {
            background: var(--accent);
            border: none;
            color: #fff;
            padding: 7px 16px;
            border-radius: 20px;
            font-size: 0.85rem;
            font-weight: 500;
            cursor: pointer;
            transition: background var(--transition);
            display: flex;
            align-items: center;
            gap: 6px;
        }

        #new-chat-btn:hover {
            background: var(--accent-hover);
        }

        #sidebar {
            position: fixed;
            top: var(--header-h);
            left: 0;
            bottom: 0;
            width: var(--sidebar-w);
            background: var(--sidebar-bg);
            border-right: 1px solid var(--border);
            transform: translateX(-100%);
            transition: transform var(--transition);
            z-index: 90;
            overflow-y: auto;
            padding: 12px;
        }

        #sidebar.open {
            transform: translateX(0);
        }

        #sidebar::-webkit-scrollbar {
            width: 4px;
        }

        #sidebar::-webkit-scrollbar-thumb {
            background: var(--text-secondary);
            opacity: 0.3;
            border-radius: 4px;
        }

        .convo-item {
            padding: 10px 14px;
            margin-bottom: 4px;
            border-radius: var(--radius-sm);
            cursor: pointer;
            font-size: 0.88rem;
            color: var(--text);
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            transition: background var(--transition);
        }

        .convo-item:hover {
            background: var(--accent-subtle);
        }

        .convo-item.active {
            background: var(--accent-subtle);
            font-weight: 500;
            color: var(--accent);
        }

        #sidebar-label {
            font-size: 0.72rem;
            font-weight: 600;
            text-transform: uppercase;
            letter-spacing: 0.06em;
            color: var(--text-secondary);
            padding: 8px 14px 6px;
            margin-top: 4px;
        }

        #chat-wrap {
            position: fixed;
            top: var(--header-h);
            bottom: 0;
            left: 0;
            right: 0;
            display: flex;
            flex-direction: column;
            transition: left var(--transition);
        }

        #chat-wrap.shifted {
            left: var(--sidebar-w);
        }

        #messages {
            flex: 1;
            overflow-y: auto;
            padding: 24px 20px 12px;
            display: flex;
            flex-direction: column;
            gap: 6px;
            max-width: 720px;
            margin: 0 auto;
            width: 100%;
        }

        #messages::-webkit-scrollbar {
            width: 5px;
        }

        #messages::-webkit-scrollbar-thumb {
            background: var(--border);
            border-radius: 5px;
        }

        .msg {
            max-width: 80%;
            padding: 12px 16px;
            line-height: 1.6;
            word-wrap: break-word;
            white-space: pre-wrap;
            animation: msgIn 0.3s ease-out;
        }

        @keyframes msgIn {
            from {
                opacity: 0;
                transform: translateY(8px);
            }

            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        .msg.user {
            align-self: flex-end;
            background: var(--user-bubble);
            color: var(--user-text);
            border-radius: var(--radius) var(--radius) 4px var(--radius);
            box-shadow: var(--shadow-sm);
        }

        .msg.assistant {
            align-self: flex-start;
            background: var(--assistant-bg);
            color: var(--text);
            border-radius: var(--radius) var(--radius) var(--radius) 4px;
            border: 1px solid var(--border);
            box-shadow: var(--shadow-sm);
            white-space: normal;
        }

        .msg.assistant p {
            margin: 0 0 0.6em;
        }

        .msg.assistant p:last-child {
            margin-bottom: 0;
        }

        .msg.assistant pre {
            background: var(--bg);
            border: 1px solid var(--border);
            border-radius: 8px;
            padding: 12px;
            overflow-x: auto;
            margin: 0.6em 0;
            font-size: 0.88em;
        }

        .msg.assistant code {
            font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;
            font-size: 0.9em;
        }

        .msg.assistant :not(pre)>code {
            background: var(--accent-subtle);
            padding: 2px 6px;
            border-radius: 4px;
        }

        .msg.assistant ul,
        .msg.assistant ol {
            padding-left: 1.4em;
            margin: 0.4em 0;
        }

        .msg.assistant blockquote {
            border-left: 3px solid var(--accent);
            margin: 0.6em 0;
            padding: 4px 12px;
            color: var(--text-secondary);
        }

        .msg.assistant h1,
        .msg.assistant h2,
        .msg.assistant h3 {
            margin: 0.6em 0 0.3em;
            font-size: 1em;
            font-weight: 600;
        }

        .msg.assistant table {
            border-collapse: collapse;
            margin: 0.6em 0;
            font-size: 0.9em;
        }

        .msg.assistant th,
        .msg.assistant td {
            border: 1px solid var(--border);
            padding: 6px 10px;
        }

        .msg.assistant th {
            background: var(--bg);
            font-weight: 600;
        }

        .status-indicator {
            color: var(--text-secondary);
            font-size: 0.85em;
            font-style: italic;
            margin-top: 0.4em;
        }

        #loading {
            align-self: flex-start;
            padding: 14px 20px;
            display: none;
            gap: 5px;
        }

        #loading.visible {
            display: flex;
        }

        .dot {
            width: 8px;
            height: 8px;
            border-radius: 50%;
            background: var(--accent);
            opacity: 0.3;
            animation: bounce 1.4s ease-in-out infinite;
        }

        .dot:nth-child(2) {
            animation-delay: 0.16s;
        }

        .dot:nth-child(3) {
            animation-delay: 0.32s;
        }

        @keyframes bounce {

            0%,
            80%,
            100% {
                opacity: 0.3;
                transform: scale(1);
            }

            40% {
                opacity: 1;
                transform: scale(1.2);
            }
        }

        #welcome {
            flex: 1;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            gap: 8px;
            color: var(--text-secondary);
            text-align: center;
        }

        #welcome h2 {
            font-size: 1.4rem;
            font-weight: 600;
            color: var(--text);
            margin-bottom: 2px;
        }

        #welcome p {
            font-size: 0.92rem;
            max-width: 360px;
            line-height: 1.5;
        }

        #input-bar {
            flex-shrink: 0;
            background: var(--surface);
            border-top: 1px solid var(--border);
            display: flex;
            align-items: flex-end;
            justify-content: center;
            padding: 12px 20px;
        }

        #input-inner {
            display: flex;
            align-items: flex-end;
            gap: 10px;
            width: 100%;
            max-width: 720px;
            background: var(--bg);
            border: 1px solid var(--border);
            border-radius: 26px;
            padding: 6px 8px 6px 18px;
            transition: border-color var(--transition), box-shadow var(--transition);
        }

        #input-inner:focus-within {
            border-color: var(--accent);
            box-shadow: 0 0 0 3px var(--accent-ring);
        }

        #input {
            flex: 1;
            border: none;
            outline: none;
            background: none;
            font-size: 0.95rem;
            font-family: inherit;
            resize: none;
            line-height: 1.5;
            max-height: 144px;
            padding: 8px 0;
            color: var(--text);
            overflow-y: auto;
        }

        #input::placeholder {
            color: var(--text-secondary);
            opacity: 0.6;
        }

        #send {
            width: 40px;
            height: 40px;
            border-radius: 50%;
            background: var(--accent);
            border: none;
            color: #fff;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: background var(--transition), transform 0.15s ease;
            flex-shrink: 0;
        }

        #send:hover {
            background: var(--accent-hover);
        }

        #send:active {
            transform: scale(0.92);
        }

        #send:disabled {
            background: var(--border);
            cursor: not-allowed;
            transform: none;
        }

        #send svg {
            width: 18px;
            height: 18px;
        }

        @media (max-width: 768px) {
            #sidebar {
                width: 260px;
            }

            #chat-wrap.shifted {
                left: 0;
            }

            #messages {
                padding: 16px 14px 8px;
            }

            .msg {
                max-width: 90%;
            }
        }
    </style>
</head>

<body>
    <div id="header">
        <button id="sidebar-toggle" aria-label="Toggle sidebar"><svg viewBox="0 0 24 24" width="22" height="22"
                fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
                <line x1="3" y1="6" x2="21" y2="6" />
                <line x1="3" y1="12" x2="21" y2="12" />
                <line x1="3" y1="18" x2="21" y2="18" />
            </svg></button>
        <span id="header-title">Chatnificent</span>
        <div id="header-actions">
            <button id="theme-toggle" aria-label="Toggle theme"><svg class="theme-icon theme-icon-light"
                    viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2"
                    stroke-linecap="round">
                    <circle cx="12" cy="12" r="5" />
                    <line x1="12" y1="1" x2="12" y2="3" />
                    <line x1="12" y1="21" x2="12" y2="23" />
                    <line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
                    <line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
                    <line x1="1" y1="12" x2="3" y2="12" />
                    <line x1="21" y1="12" x2="23" y2="12" />
                    <line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
                    <line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
                </svg><svg class="theme-icon theme-icon-dark" viewBox="0 0 24 24" width="20" height="20" fill="none"
                    stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                    <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
                </svg></button>
            <button id="new-chat-btn"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor"
                    stroke-width="2.2" stroke-linecap="round">
                    <line x1="12" y1="5" x2="12" y2="19" />
                    <line x1="5" y1="12" x2="19" y2="12" />
                </svg> New chat</button>
        </div>
    </div>
    <div id="sidebar">
        <div id="sidebar-label">Conversations</div>
        <div id="convo-list"></div>
    </div>
    <div id="chat-wrap">
        <div id="messages">
            <div id="welcome">
                <h2>Welcome to Chatnificent</h2>
                <p>Start a conversation below. Your chats will appear in the sidebar.</p>
            </div>
        </div>
        <div id="input-bar">
            <div id="input-inner">
                <textarea id="input" rows="1" placeholder="Send a message..."></textarea>
                <button id="send" aria-label="Send"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
                        stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                        <line x1="22" y1="2" x2="11" y2="13" />
                        <polygon points="22 2 15 22 11 13 2 9 22 2" />
                    </svg></button>
            </div>
        </div>
    </div>
    <script>
        (function () {
            "use strict";
            var $ = function (s) { return document.querySelector(s) };
            var msgs = $("#messages"), input = $("#input"), sendBtn = $("#send");
            var sidebar = $("#sidebar"), convoList = $("#convo-list");
            var chatWrap = $("#chat-wrap"), welcome = $("#welcome");
            var convoId = null, sidebarOpen = false;

            function setTheme(dark) {
                document.documentElement.setAttribute("data-theme", dark ? "dark" : "light");
                try { localStorage.setItem("chatnificent-theme", dark ? "dark" : "light") } catch (e) { }
            }
            (function () {
                var saved;
                try { saved = localStorage.getItem("chatnificent-theme") } catch (e) { }
                if (saved === "dark") setTheme(true);
                else if (saved === "light") setTheme(false);
                else if (window.matchMedia && window.matchMedia("(prefers-color-scheme:dark)").matches) setTheme(true);
            })();
            $("#theme-toggle").addEventListener("click", function () {
                setTheme(document.documentElement.getAttribute("data-theme") !== "dark");
            });

            function toggleSidebar() {
                sidebarOpen = !sidebarOpen;
                sidebar.classList.toggle("open", sidebarOpen);
                chatWrap.classList.toggle("shifted", sidebarOpen);
            }
            $("#sidebar-toggle").addEventListener("click", toggleSidebar);

            $("#new-chat-btn").addEventListener("click", function () {
                convoId = null; msgs.innerHTML = "";
                msgs.appendChild(welcome); welcome.style.display = "";
                history.pushState({ convoId: null }, "", "/");
                refreshConvoList();
            });

            input.addEventListener("input", function () {
                this.style.height = "auto";
                this.style.height = Math.min(this.scrollHeight, 144) + "px";
                var rtlPattern = /[\u0590-\u05ff\u0600-\u06ff\u0750-\u077f\u08a0-\u08ff\ufb1d-\ufb4f\ufb50-\ufdff\ufe70-\ufeff]/;
                var isRTL = rtlPattern.test(this.value);
                this.dir = isRTL ? "rtl" : "ltr";
            });

            function renderMarkdown(text) {
                try { return DOMPurify.sanitize(marked.parse(text)); }
                catch (e) { return DOMPurify.sanitize(text); }
            }
            function addMsg(role, text) {
                if (welcome.style.display !== "none") welcome.style.display = "none";
                var d = document.createElement("div");
                d.className = "msg " + role;
                if (role === "assistant") d.innerHTML = renderMarkdown(text);
                else d.textContent = text;
                var rtlPattern = /[\u0590-\u05ff\u0600-\u06ff\u0750-\u077f\u08a0-\u08ff\ufb1d-\ufb4f\ufb50-\ufdff\ufe70-\ufeff]/;
                if (rtlPattern.test(text)) d.dir = "rtl";
                msgs.appendChild(d); msgs.scrollTop = msgs.scrollHeight;
                return d;
            }
            function renderMessageList(messages) {
                msgs.innerHTML = "";
                if (!messages || !messages.length) {
                    msgs.appendChild(welcome);
                    welcome.style.display = "";
                    return;
                }
                welcome.style.display = "none";
                messages.forEach(function (m) {
                    addMsg(m.role, m.content || "");
                });
            }
            function showLoading() {
                var el = document.createElement("div");
                el.id = "loading"; el.className = "visible";
                el.innerHTML = '<div class="dot"></div><div class="dot"></div><div class="dot"></div>';
                msgs.appendChild(el); msgs.scrollTop = msgs.scrollHeight;
            }
            function hideLoading() { var el = $("#loading"); if (el) el.remove(); }

            async function send() {
                var text = input.value.trim();
                if (!text) return;
                addMsg("user", text); input.value = ""; input.style.height = "auto";
                sendBtn.disabled = true; showLoading();
                try {
                    var r = await fetch("/api/chat", {
                        method: "POST",
                        headers: {
                            "Content-Type": "application/json",
                            "Accept": "text/event-stream"
                        },
                        body: JSON.stringify({ message: text, conversation_id: convoId })
                    });
                    var ct = (r.headers.get("Content-Type") || "");
                    if (ct.indexOf("text/event-stream") !== -1) {
                        hideLoading();
                        var bubble = addMsg("assistant", "");
                        var accumulated = "";
                        var bubbleRtl = false;
                        var rtlRe = /[\u0590-\u05ff\u0600-\u06ff\u0750-\u077f\u08a0-\u08ff\ufb1d-\ufb4f\ufb50-\ufdff\ufe70-\ufeff]/;
                        var reader = r.body.getReader();
                        var decoder = new TextDecoder();
                        var buf = "";
                        while (true) {
                            var result = await reader.read();
                            if (result.done) break;
                            buf += decoder.decode(result.value, { stream: true });
                            var lines = buf.split("\n");
                            buf = lines.pop();
                            for (var i = 0; i < lines.length; i++) {
                                var line = lines[i];
                                if (line.indexOf("data: ") !== 0) continue;
                                var payload;
                                try { payload = JSON.parse(line.slice(6)); } catch (_) { continue; }
                                if (payload.event === "delta") {
                                    accumulated += payload.data;
                                    bubble.innerHTML = renderMarkdown(accumulated);
                                    if (!bubbleRtl && rtlRe.test(payload.data)) { bubbleRtl = true; bubble.dir = "rtl"; }
                                    msgs.scrollTop = msgs.scrollHeight;
                                } else if (payload.event === "status") {
                                    bubble.innerHTML = renderMarkdown(accumulated || "") +
                                        '<div class="status-indicator">' + DOMPurify.sanitize(payload.data) + '</div>';
                                    msgs.scrollTop = msgs.scrollHeight;
                                } else if (payload.event === "done") {
                                    if (payload.data && payload.data.conversation_id) {
                                        convoId = payload.data.conversation_id;
                                        history.replaceState({ convoId: convoId }, "", payload.data.path || "/" + convoId);
                                        upsertSidebarEntry(convoId, text.length > 30 ? text.slice(0, 30) + "…" : text);
                                    }
                                    bubble.innerHTML = renderMarkdown(accumulated);
                                } else if (payload.event === "error") {
                                    bubble.innerHTML = renderMarkdown(accumulated || "Error: " + payload.data);
                                }
                            }
                        }
                    } else {
                        var data = await r.json(); hideLoading();
                        if (data.error) addMsg("assistant", "Error: " + data.error);
                        else {
                            renderMessageList(data.messages || []);
                            convoId = data.conversation_id;
                            history.replaceState({ convoId: convoId }, "", data.path || "/" + convoId);
                            upsertSidebarEntry(convoId, text.length > 30 ? text.slice(0, 30) + "…" : text);
                        }
                    }
                } catch (e) { hideLoading(); addMsg("assistant", "Error: " + e.message); }
                sendBtn.disabled = false; input.focus();
            }
            sendBtn.addEventListener("click", send);
            input.addEventListener("keydown", function (e) {
                if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); }
            });

            async function refreshConvoList() {
                try {
                    var r = await fetch("/api/conversations"); var data = await r.json();
                    convoList.innerHTML = "";
                    (data.conversations || []).forEach(function (c) {
                        var id = c.id || c, title = c.title || id;
                        var el = document.createElement("div");
                        el.className = "convo-item" + (id === convoId ? " active" : "");
                        el.dataset.convoId = id;
                        el.textContent = title; el.title = title;
                        el.addEventListener("click", function () { loadConvo(id) });
                        convoList.appendChild(el);
                    });
                } catch (e) { }
            }
            function upsertSidebarEntry(id, title) {
                var items = convoList.querySelectorAll(".convo-item");
                var existing = null;
                for (var i = 0; i < items.length; i++) {
                    items[i].classList.remove("active");
                    if (items[i].dataset.convoId === id) existing = items[i];
                }
                if (existing) {
                    existing.classList.add("active");
                } else {
                    var el = document.createElement("div");
                    el.className = "convo-item active";
                    el.dataset.convoId = id;
                    el.textContent = title || id; el.title = title || id;
                    el.addEventListener("click", function () { loadConvo(id) });
                    convoList.prepend(el);
                }
            }
            async function loadConvo(id, replaceHistory) {
                try {
                    var r = await fetch("/api/conversations/" + id); var data = await r.json();
                    if (data.error) return;
                    convoId = id;
                    if (replaceHistory) history.replaceState({ convoId: id }, "", data.path || "/" + id);
                    else history.pushState({ convoId: id }, "", data.path || "/" + id);
                    renderMessageList(data.messages || []);
                    refreshConvoList();
                } catch (e) { }
            }
            window.addEventListener("popstate", function (e) {
                var cid = e.state && e.state.convoId;
                if (cid) loadConvo(cid); else { convoId = null; msgs.innerHTML = ""; msgs.appendChild(welcome); welcome.style.display = ""; refreshConvoList(); }
            });
            (function initFromURL() {
                var initConvo = window.__CHATNIFICENT_CONVO__;
                if (initConvo) loadConvo(initConvo); else refreshConvoList();
            })();
            input.focus();
        })();
    </script>
</body>

</html>