(() => { const MONTAIGNE_SUGGESTIONS = [ { title: ['What occupies your thoughts', 'most lately?'], content: 'What occupies your thoughts most lately?' }, { title: ['Which of your habits', 'deserve examination?'], content: 'Which of my habits deserve the closest examination, and how might I begin?' }, { title: ['What do solitude and', 'conversation each reveal?'], content: "What do solitude and conversation each reveal about a person's character?" }, { title: ['How have you changed', 'without noticing?'], content: 'How can someone recognize the ways they have changed without noticing?' }, { title: ['Where does ambition', 'help or mislead you?'], content: 'Where does ambition improve a life, and where does it quietly mislead us?' }, { title: ['What truth about yourself', 'is hardest to admit?'], content: 'What kinds of truths about ourselves are the hardest to admit, and why?' } ]; const HIDE_TEXT = new Set([ 'Settings', 'Archived Chats', 'Keyboard shortcuts', 'Update your status', 'Playground', 'Admin Panel', 'Documentation', 'Releases', 'Set as default' ]); const HIDE_HREF = new Set(['/playground', '/admin']); const HIDE_ARIA = new Set(['Controls', 'Add Model', 'Remove Model', 'Voice Input', 'Voice mode']); const CHAT_COMPLETIONS_PATH = '/api/chat/completions'; const GUEST_TOKEN_KEY = 'montaigne_guest_upgrade_token'; const GUEST_CLAIM_URL_KEY = 'montaigne_guest_claim_url'; const MONTAIGNE_TOKEN_KEY = 'montaigne_access_token'; function normalize(text) { return (text || '').replace(/\s+/g, ' ').trim(); } function removeGuestBanner() { const banner = document.getElementById('montaigne-guest-banner'); if (banner) { banner.remove(); } } function closeGuestModal() { const modal = document.getElementById('montaigne-guest-modal'); if (modal) { modal.remove(); } } async function submitGuestClaim(form, statusEl) { const guestUpgradeToken = localStorage.getItem(GUEST_TOKEN_KEY); const claimUrl = localStorage.getItem(GUEST_CLAIM_URL_KEY); const username = form.querySelector('[name="username"]').value.trim(); const password = form.querySelector('[name="password"]').value; if (!guestUpgradeToken || !claimUrl) { statusEl.textContent = 'This guest session can no longer be claimed. Please refresh and try again.'; return; } if (username.length < 3) { statusEl.textContent = 'Choose a username with at least 3 characters.'; return; } if (password.length < 6) { statusEl.textContent = 'Choose a password with at least 6 characters.'; return; } statusEl.textContent = 'Creating your account...'; try { const response = await fetch(claimUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ guest_upgrade_token: guestUpgradeToken, username, password }) }); const payload = await response.json(); if (!response.ok) { throw new Error(payload.detail || 'Could not create your account.'); } localStorage.setItem(MONTAIGNE_TOKEN_KEY, payload.access_token); localStorage.removeItem(GUEST_TOKEN_KEY); localStorage.removeItem(GUEST_CLAIM_URL_KEY); closeGuestModal(); removeGuestBanner(); const toast = document.createElement('div'); toast.style.cssText = 'position:fixed;bottom:18px;left:50%;transform:translateX(-50%);z-index:100000;' + 'background:#16311f;color:#e6f6ea;padding:10px 14px;border-radius:10px;' + 'font:13px Inter,system-ui,sans-serif;box-shadow:0 10px 30px rgba(0,0,0,.25);'; toast.textContent = 'Account created. Send your next message to continue.'; document.body.appendChild(toast); setTimeout(() => toast.remove(), 4000); } catch (error) { statusEl.textContent = error instanceof Error ? error.message : 'Could not create your account.'; } } function openGuestModal() { closeGuestModal(); const overlay = document.createElement('div'); overlay.id = 'montaigne-guest-modal'; overlay.style.cssText = 'position:fixed;inset:0;z-index:100000;display:flex;align-items:center;justify-content:center;' + 'background:rgba(22,16,12,.62);padding:20px;'; const card = document.createElement('div'); card.style.cssText = 'width:min(420px,100%);border-radius:18px;padding:22px;background:#f5ede0;color:#2b2118;' + 'box-shadow:0 20px 60px rgba(0,0,0,.35);font:14px Inter,system-ui,sans-serif;'; const title = document.createElement('h2'); title.textContent = 'Create your account'; title.style.cssText = 'margin:0 0 8px;font:600 22px Georgia,serif;'; const intro = document.createElement('p'); intro.textContent = 'Keep this conversation and continue chatting with Montaigne.'; intro.style.cssText = 'margin:0 0 16px;line-height:1.45;color:#5b4a3c;'; const form = document.createElement('form'); form.style.cssText = 'display:grid;gap:12px;'; const username = document.createElement('input'); username.name = 'username'; username.placeholder = 'Username'; username.autocomplete = 'username'; username.style.cssText = 'width:100%;padding:12px 14px;border:1px solid #cfb89b;border-radius:10px;background:#fffdf9;'; const password = document.createElement('input'); password.name = 'password'; password.type = 'password'; password.placeholder = 'Password'; password.autocomplete = 'new-password'; password.style.cssText = username.style.cssText; const status = document.createElement('div'); status.style.cssText = 'min-height:18px;font-size:13px;color:#8a2f2f;'; const actions = document.createElement('div'); actions.style.cssText = 'display:flex;justify-content:flex-end;gap:10px;margin-top:4px;'; const cancel = document.createElement('button'); cancel.type = 'button'; cancel.textContent = 'Cancel'; cancel.style.cssText = 'padding:10px 14px;border:1px solid #cfb89b;border-radius:10px;background:#fff8ef;cursor:pointer;'; cancel.onclick = () => closeGuestModal(); const submit = document.createElement('button'); submit.type = 'submit'; submit.textContent = 'Create account'; submit.style.cssText = 'padding:10px 14px;border:none;border-radius:10px;background:#8b6243;color:#fff;cursor:pointer;font-weight:600;'; form.addEventListener('submit', async (event) => { event.preventDefault(); await submitGuestClaim(form, status); }); actions.appendChild(cancel); actions.appendChild(submit); form.appendChild(username); form.appendChild(password); form.appendChild(status); form.appendChild(actions); card.appendChild(title); card.appendChild(intro); card.appendChild(form); overlay.appendChild(card); overlay.addEventListener('click', (event) => { if (event.target === overlay) { closeGuestModal(); } }); document.body.appendChild(overlay); username.focus(); } function showGuestBanner() { if (document.getElementById('montaigne-guest-banner')) { return; } const banner = document.createElement('div'); banner.id = 'montaigne-guest-banner'; banner.style.cssText = 'position:fixed;bottom:0;left:0;right:0;z-index:99999;background:linear-gradient(135deg,#3d2b1f,#5c3d2e);' + 'color:#f5e6d3;padding:14px 20px;display:flex;align-items:center;justify-content:center;gap:16px;' + 'font:14px Inter,system-ui,sans-serif;box-shadow:0 -2px 12px rgba(0,0,0,.3);'; const message = document.createElement('span'); message.textContent = 'You have used your 5 free messages. Create a free account to continue chatting with Montaigne.'; const button = document.createElement('button'); button.type = 'button'; button.textContent = 'Create free account'; button.style.cssText = 'background:#c9a96e;color:#2b1d0e;padding:8px 18px;border:none;border-radius:6px;' + 'font-weight:600;white-space:nowrap;cursor:pointer;'; button.onclick = () => openGuestModal(); const close = document.createElement('button'); close.type = 'button'; close.textContent = 'x'; close.style.cssText = 'background:none;border:none;color:#f5e6d3;font-size:20px;cursor:pointer;padding:0 4px;line-height:1;'; close.onclick = () => removeGuestBanner(); banner.appendChild(message); banner.appendChild(button); banner.appendChild(close); document.body.appendChild(banner); } function installFetchPatch() { if (window.__montaigneFetchPatched) { return; } window.__montaigneFetchPatched = true; const originalFetch = window.fetch.bind(window); window.fetch = async (...args) => { try { const input = args[0]; const rawUrl = input instanceof Request ? input.url : String(input); const url = new URL(rawUrl, window.location.origin); if (url.pathname === CHAT_COMPLETIONS_PATH) { const body = args[1] && args[1].body; if (typeof body === 'string') { try { const parsed = JSON.parse(body); parsed.variables = parsed.variables || {}; const montaigneToken = localStorage.getItem(MONTAIGNE_TOKEN_KEY); if (montaigneToken) { parsed.variables.montaigne_token = montaigneToken; } args[1] = { ...args[1], body: JSON.stringify(parsed) }; } catch (_) { // Ignore non-JSON payloads. } } } } catch (_) { // Ignore URL parsing errors. } const response = await originalFetch(...args); try { const input = args[0]; const rawUrl = input instanceof Request ? input.url : String(input); const url = new URL(rawUrl, window.location.origin); if (url.pathname === '/api/config') { const payload = await response.clone().json(); payload.default_prompt_suggestions = MONTAIGNE_SUGGESTIONS; const headers = new Headers(response.headers); headers.set('content-type', 'application/json'); headers.delete('content-length'); return new Response(JSON.stringify(payload), { status: response.status, statusText: response.statusText, headers }); } if (url.pathname === CHAT_COMPLETIONS_PATH && response.status === 403) { const payload = await response.clone().json().catch(() => null); if (payload && payload.code === 'guest_limit_reached' && payload.guest_upgrade_token && payload.claim_guest_url) { localStorage.setItem(GUEST_TOKEN_KEY, payload.guest_upgrade_token); localStorage.setItem(GUEST_CLAIM_URL_KEY, payload.claim_guest_url); showGuestBanner(); } } } catch (_) { // Keep the UI resilient. } return response; }; } function hide(el) { if (!el || el.dataset.montaigneHidden === '1') { return; } el.dataset.montaigneHidden = '1'; el.style.display = 'none'; } function hideMenuItem(el) { hide(el.closest('button, a, [role="menuitem"]') || el); } function hideModelSelector(trigger) { hide(trigger); let node = trigger; for (let i = 0; i < 6 && node; i += 1) { const className = typeof node.className === 'string' ? node.className : ''; if (className.includes('overflow-hidden')) { hide(node.parentElement || node); return; } node = node.parentElement; } hide(trigger); } function applyMinimalUi() { document .querySelectorAll('[id^="model-selector-"][id$="-button"]') .forEach(hideModelSelector); document.querySelectorAll('#voice-input-button, [aria-label="Voice mode"]').forEach(hide); document.querySelectorAll('[aria-label^="Get information on "]').forEach(hide); document.querySelectorAll('[role="dialog"], dialog').forEach((el) => { const text = normalize(el.textContent); if (text.includes("What's New") || text.includes('Release Notes')) { const closeButton = el.querySelector('button[aria-label="Close"]'); if (closeButton) { closeButton.click(); } hide(el); } }); document.querySelectorAll('button, a, [role="menuitem"]').forEach((el) => { const text = normalize(el.textContent); const aria = normalize(el.getAttribute('aria-label')); const href = el.getAttribute('href'); if (HIDE_TEXT.has(text) || HIDE_ARIA.has(aria) || HIDE_HREF.has(href)) { hideMenuItem(el); } }); document.querySelectorAll('#temporary-chat-button').forEach(hide); } installFetchPatch(); const run = () => window.requestAnimationFrame(applyMinimalUi); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', run, { once: true }); } else { run(); } new MutationObserver(run).observe(document.documentElement, { childList: true, subtree: true }); })();