🌐
index.html
Back
📝 Html ⚡ Executable Ctrl+S: Save • Ctrl+R: Run • Ctrl+F: Find
<!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <title>AI + Editor — Bottom Composer + Settings Overlay (Hardened)</title> <style> :root { --bg: #0f1117; --panel:#141824; --panel-2:#0f1320; --fg:#e7e7e9; --muted:#9aa3b2; --border:#23293a; --radius:14px; --bar-h:52px; --composer-h:86px; } * { box-sizing: border-box } html, body { height: 100% } body { margin: 0; background: var(--bg); color: var(--fg); font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; } /* Top bar */ .topbar { position: fixed; inset: 0 0 auto 0; height: var(--bar-h); display: grid; grid-template-columns: 1fr auto; gap: 8px; align-items: center; padding: 0 10px; background: var(--panel); border-bottom: 1px solid var(--border); z-index: 5; } .brand { font-weight: 700; letter-spacing: .3px; opacity: .9 } .actions { display: flex; gap: 8px; } button { appearance: none; border: 1px solid var(--border); border-radius: 10px; background: linear-gradient(180deg, #1a2030, #141a29); color: var(--fg); padding: 8px 12px; font-weight: 600; cursor: pointer; } #aiBtn { border-color:#2d3b6a; background: linear-gradient(180deg,#1c2741,#172036) } #settingsBtn { border-color:#2a354e } /* Main area (editor) */ .main { position: fixed; inset: var(--bar-h) 0 var(--composer-h) 0; padding: 10px; } .pane { width: 100%; height: 100%; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; } .editor-header { display: flex; align-items: center; justify-content: space-between; padding: 8px 10px; border-bottom: 1px solid var(--border); background: var(--panel-2); } .editor-header small { color: var(--muted) } #editor { width: 100%; height: calc(100% - 42px); } /* Bottom composer */ .composer { position: fixed; inset: auto 0 0 0; height: var(--composer-h); background: var(--panel); border-top: 1px solid var(--border); display: grid; align-items: center; padding: 8px 10px; z-index: 4; } #promptForm { display: grid; grid-template-columns: 1fr auto; gap: 8px; align-items: end; } #prompt { resize: none; min-height: 54px; max-height: 40vh; padding: 12px; border-radius: 12px; outline: none; border: 1px solid var(--border); background: #0e1424; color: var(--fg); } .send-btn { padding: 10px 14px; font-weight: 700 } /* AI Overlay (messages) */ #aiOverlay { position: fixed; inset: 0; display: grid; place-items: center; background: rgba(0,0,0,.45); opacity: 0; pointer-events: none; transition: opacity .18s ease; z-index: 9999; /* on top */ } #aiOverlay.open { opacity: 1; pointer-events: auto; } .overlay-card { width: min(980px, 92vw); height: min(720px, 88vh); background: var(--panel); border: 1px solid var(--border); border-radius: 16px; display: grid; grid-template-rows: auto 1fr; box-shadow: 0 10px 30px rgba(0,0,0,.35); } .overlay-top { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px; border-bottom: 1px solid var(--border); background: var(--panel-2); } .messages-wrap { overflow: auto; padding: 12px; background: radial-gradient(1200px 700px at 20% -10%, rgba(110,168,254,.06), transparent); } #messages { display: grid; gap: 10px; } .msg { padding: 10px 12px; border: 1px solid var(--border); border-radius: 12px; white-space: pre-wrap; line-height: 1.4; background: #101729; } .msg.user { background:#0f1b2f; border-color:#233354 } .msg.assistant { background:#0f1726; border-color:#2a3550 } /* Settings Overlay */ #settingsOverlay { position: fixed; inset: 0; display: grid; place-items: center; background: rgba(0,0,0,.45); opacity: 0; pointer-events: none; transition: opacity .18s ease; z-index: 10000; } #settingsOverlay.open { opacity: 1; pointer-events: auto; } .settings-card { width: min(560px, 92vw); background: var(--panel); border: 1px solid var(--border); border-radius: 16px; display: grid; grid-template-rows: auto 1fr auto; max-height: 90vh; } .settings-top { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px; border-bottom: 1px solid var(--border); background: var(--panel-2); } .settings-body { padding: 12px; display: grid; gap: 10px; overflow: auto; } .field { display: grid; gap: 6px; } label { font-size: 12px; color: var(--muted) } select, input[type="text"], input[type="password"], input[type="number"] { width: 100%; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--border); background: #111626; color: var(--fg); } .settings-bottom { padding: 10px 12px; border-top: 1px solid var(--border); text-align: right; } .muted { color: var(--muted); font-size: 12px } @media (max-width: 700px) { .editor-header small { display: none } } </style> </head> <body> <!-- Top bar --> <div class="topbar" role="toolbar"> <div class="brand">AI + Editor</div> <div class="actions"> <button id="aiBtn" type="button" title="Open AI Messages (Ctrl/Cmd+Shift+A)">💬 Messages</button> <button id="settingsBtn" type="button" title="Open Settings">⚙️ Settings</button> </div> </div> <!-- Main editor area --> <main class="main"> <section class="pane"> <div class="editor-header"> <div><strong>Editor</strong> <small>(Ace)</small></div> <small>Select code and press <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>A</kbd> to view chat</small> </div> <div id="editor"></div> </section> </main> <!-- Bottom composer (questions here) --> <div class="composer"> <form id="promptForm"> <textarea id="prompt" placeholder="Ask for a refactor, bug fix, docstring, etc…"></textarea> <button class="send-btn" type="submit">Send</button> </form> </div> <!-- AI Overlay (messages) — controlled mainly by myai.js --> <div id="aiOverlay" aria-hidden="true" aria-label="AI Chat Overlay"> <div class="overlay-card" role="dialog" aria-modal="true"> <div class="overlay-top"> <div class="title">AI Assistant</div> <button id="overlayClose" type="button" title="Close (Esc)">✕</button> </div> <div class="messages-wrap"> <div id="messages" aria-live="polite" aria-atomic="false"></div> </div> </div> </div> <!-- Settings Overlay --> <div id="settingsOverlay" aria-hidden="true" aria-label="Settings Overlay"> <div class="settings-card" role="dialog" aria-modal="true"> <div class="settings-top"> <div class="title"><strong>AI Settings</strong></div> <button id="settingsClose" type="button" title="Close">✕</button> </div> <div class="settings-body"> <div class="field"> <label for="ai-provider">Provider</label> <select id="ai-provider"> <option value="none">— Select —</option> <option value="openai">OpenAI</option> <option value="anthropic">Anthropic</option> <option value="deepseek">DeepSeek</option> </select> </div> <div class="field"> <label for="ai-model">Model</label> <input id="ai-model" type="text" placeholder="e.g., gpt-4.1, claude-3-5-sonnet, deepseek-chat" /> </div> <div class="field"> <label for="ai-key">API Key</label> <input id="ai-key" type="password" placeholder="sk-..." autocomplete="off" /> </div> <div class="field"> <label for="ai-temp">Temperature</label> <input id="ai-temp" type="number" step="0.1" min="0" max="1" value="0.4" /> </div> </div> <div class="settings-bottom"> <span class="muted">Stored in-page for this demo.</span> </div> </div> </div> <!-- Ace Editor --> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.32.0/ace.js" integrity="sha512-iVn5o0pZ5c1b9I1m1eBpZxXQ1jZrC7oX9cV7t2qG2cBf3t0QYwEJt3q8C5y1o3E2h0z0XH1K7o4c9o3h3Cq4Yw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <!-- Minimal editor bootstrap to satisfy window.editorAPI --> <script> (function initEditor() { const editor = ace.edit("editor"); editor.setTheme("ace/theme/monokai"); editor.session.setMode("ace/mode/javascript"); editor.session.setUseWorker(false); editor.setOptions({ fontSize: "13px", tabSize: 2, useSoftTabs: true, wrap: true }); editor.setValue(`// Type a question below, click "Send", then open 💬 Messages to see replies function greet(name) { return \`Hello, \${name}!\`; } console.log(greet("world"));`, -1); window.editorAPI = { editor, getSelectedText: () => editor.session.getTextRange(editor.getSelectionRange()), getValue: () => editor.getValue(), focus: () => editor.focus() }; })(); // Settings overlay controls (function settingsOverlayInit() { const settingsBtn = document.getElementById('settingsBtn'); const settingsOverlay = document.getElementById('settingsOverlay'); const settingsClose = document.getElementById('settingsClose'); function openSettings() { settingsOverlay.classList.add('open'); } function closeSettings() { settingsOverlay.classList.remove('open'); } settingsBtn.addEventListener('click', openSettings); settingsClose.addEventListener('click', closeSettings); window.addEventListener('keydown', (e) => { if (settingsOverlay.classList.contains('open') && e.key === 'Escape') closeSettings(); }); settingsOverlay.addEventListener('click', (e) => { if (e.target === settingsOverlay) closeSettings(); }); })(); </script> <!-- Your AI module (uses aiBtn, aiOverlay, overlayClose, promptForm, prompt, messages, ai-*) --> <script src="./myai.js"></script> <!-- Hardened overlay sanity patch (runs AFTER myai.js) --> <script> (() => { const o = document.getElementById('aiOverlay'); const b = document.getElementById('aiBtn'); const x = document.getElementById('overlayClose'); console.log('[overlay-check]', { overlay: !!o, aiBtn: !!b, overlayClose: !!x, hasOpenClass: o?.classList.contains('open') }); if (!o || !b || !x) { console.error('Missing required elements for AI overlay:', { o, b, x }); return; } // Re-bind to guarantee operability even if original listeners failed b.addEventListener('click', () => { o.classList.add('open'); const prompt = document.getElementById('prompt'); if (prompt) { prompt.focus(); } }); x.addEventListener('click', () => o.classList.remove('open')); // Click outside to close o.addEventListener('click', (e) => { if (e.target === o) o.classList.remove('open'); }); // ESC to close window.addEventListener('keydown', (e) => { if (e.key === 'Escape' && o.classList.contains('open')) o.classList.remove('open'); }); })(); </script> </body> </html>