diff --git a/frontend/editor-poc.html b/frontend/editor-poc.html
index dd2093b..c87d328 100644
--- a/frontend/editor-poc.html
+++ b/frontend/editor-poc.html
@@ -702,10 +702,15 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
// ---- Bubble menu ----
function showBubble() {
if (ta.selectionStart === ta.selectionEnd) { hideBubble(); return; }
- var tr = ta.getBoundingClientRect();
- var top = tr.bottom + 6;
- var left = tr.left + (tr.width / 2) - (bubble.offsetWidth / 2);
- if (top + 36 > window.innerHeight) top = tr.top - 44;
+ // Position below the selection start, using caret pixel calc
+ var pt = caretToPixel(ta.selectionStart);
+ // Convert from editorWrap-relative to viewport-relative
+ var wr = editorWrap.getBoundingClientRect();
+ var top = wr.top + pt.y + 4;
+ var left = wr.left + pt.x - 10;
+ // Fallback if caret calc gives odd values
+ if (top < 50) { var tr = ta.getBoundingClientRect(); top = tr.bottom + 6; left = tr.left + (tr.width / 2) - (bubble.offsetWidth / 2); }
+ if (top + 36 > window.innerHeight) top = top - 60;
if (left < 6) left = 6;
if (left + bubble.offsetWidth > window.innerWidth - 6) left = window.innerWidth - bubble.offsetWidth - 6;
bubble.style.left = left + 'px'; bubble.style.top = top + 'px';
@@ -736,6 +741,7 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
// ---- Quick Insert ----
function showQI() {
qiMenu.classList.remove('on');
+ qiIdx = -1; qiItems = [];
// Position below current line, left-aligned
var pt = caretToPixel(null);
pt.x = Math.max(pt.x - 10, 4);
@@ -809,22 +815,12 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
// ---- Event handlers ----
ta.addEventListener('keydown', function(e) {
- // Slash menu
+ // Slash menu trigger: type / at start of line
if (e.key === '/') {
var l = getLine(); var p = getPos();
var before = l.text.substring(0, p.s - l.start);
if (before === '' || /^\s*$/.test(before)) setTimeout(showSlash, 20);
}
- if (slashVisible) {
- if (e.key === 'Escape') { e.preventDefault(); hideSlash(); }
- else if (e.key === 'ArrowDown') { e.preventDefault(); navSlash(1); }
- else if (e.key === 'ArrowUp') { e.preventDefault(); navSlash(-1); }
- else if (e.key === 'Enter') {
- e.preventDefault();
- var vis = getVisibleSlash();
- if (slashIdx >= 0 && slashIdx < vis.length) selectSlashAction(vis[slashIdx].getAttribute('data-action'));
- }
- }
if (e.ctrlKey && e.key === 'j') { e.preventDefault(); toggleAIPanel(); }
if (e.ctrlKey && e.key === 's') { e.preventDefault(); forceSave(); }
if (e.ctrlKey && e.key === 'b') { e.preventDefault(); wrapSelection('**'); }
@@ -833,6 +829,41 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
if (e.altKey && e.key === 'i') { e.preventDefault(); showQI(); }
});
+ // Slash menu keyboard nav (bound to search input which has focus)
+ slashSearch.addEventListener('keydown', function(e) {
+ if (!slashVisible) return;
+ if (e.key === 'Escape') { e.preventDefault(); hideSlash(); ta.focus(); }
+ else if (e.key === 'ArrowDown') { e.preventDefault(); navSlash(1); }
+ else if (e.key === 'ArrowUp') { e.preventDefault(); navSlash(-1); }
+ else if (e.key === 'Enter') {
+ e.preventDefault();
+ var vis = getVisibleSlash();
+ if (slashIdx >= 0 && slashIdx < vis.length) selectSlashAction(vis[slashIdx].getAttribute('data-action'));
+ }
+ });
+
+ // Quick Insert keyboard nav (document-level since no input to focus)
+ var qiIdx = -1;
+ var qiItems = [];
+ document.addEventListener('keydown', function(e) {
+ if (!qiMenu.classList.contains('on')) return;
+ if (!qiItems.length) qiItems = Array.from(qiMenu.querySelectorAll('.qi-item'));
+ if (e.key === 'Escape') { e.preventDefault(); qiMenu.classList.remove('on'); ta.focus(); }
+ else if (e.key === 'ArrowDown') { e.preventDefault(); qiIdx = (qiIdx + 1) % qiItems.length; highlightQI(); }
+ else if (e.key === 'ArrowUp') { e.preventDefault(); qiIdx = (qiIdx - 1 + qiItems.length) % qiItems.length; highlightQI(); }
+ else if (e.key === 'Enter') {
+ e.preventDefault();
+ if (qiIdx >= 0 && qiIdx < qiItems.length) {
+ var action = qiItems[qiIdx].getAttribute('data-qi');
+ qiMenu.classList.remove('on');
+ selectSlashAction(action);
+ }
+ }
+ });
+ function highlightQI() {
+ qiItems.forEach(function(item, i) { item.style.background = i === qiIdx ? 'var(--bg-hover)' : ''; item.style.color = i === qiIdx ? 'var(--text)' : ''; });
+ }
+
ta.addEventListener('keyup', function(e) {
if (e.key === '/' && slashVisible) {
var l = getLine(); var p = getPos();
@@ -879,7 +910,7 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
document.addEventListener('click', function(e) {
if (slashVisible && !slashMenu.contains(e.target) && e.target !== ta) hideSlash();
if (bubble.classList.contains('on') && !bubble.contains(e.target)) hideBubble();
- if (qiMenu.classList.contains('on') && !qiMenu.contains(e.target) && e.target.id !== 'qi-btn') qiMenu.classList.remove('on');
+ if (qiMenu.classList.contains('on') && !qiMenu.contains(e.target) && e.target.id !== 'qi-btn') { qiMenu.classList.remove('on'); qiIdx = -1; qiItems = []; }
});
btnAI.addEventListener('click', toggleAIPanel);