<!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>