diff --git a/frontend/editor-poc.html b/frontend/editor-poc.html
index 33d18e3..dd2093b 100644
--- a/frontend/editor-poc.html
+++ b/frontend/editor-poc.html
@@ -285,7 +285,7 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
- Type / for commands
+ Type / for commands | Alt+I Insert
Ctrl+J AI panel
Ctrl+S save
✨ AI processing...
@@ -609,6 +609,32 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
});
})();
+ // ---- Caret position to pixel (for monospace textarea) ----
+ function caretToPixel(pos) {
+ if (pos == null) pos = getPos().s;
+ var v = val();
+ var before = v.substring(0, pos);
+ var lineNum = before.split('\n').length - 1;
+ var colNum = pos - v.lastIndexOf('\n', pos - 1) - 1;
+ if (colNum < 0) colNum = 0;
+ var style = getComputedStyle(ta);
+ var fontSize = parseFloat(style.fontSize);
+ var lineH = parseFloat(style.lineHeight);
+ if (isNaN(lineH)) lineH = fontSize * 1.75;
+ var charW = fontSize * 0.615;
+ var wrapRect = editorWrap.getBoundingClientRect();
+ var taRect = ta.getBoundingClientRect();
+ // Position relative to editorWrap
+ var x = taRect.left - wrapRect.left + 2 + colNum * charW;
+ var y = taRect.top - wrapRect.top + (lineNum + 1) * lineH - ta.scrollTop;
+ // Clamp
+ if (x < 4) x = 4;
+ if (x > wrapRect.width - 310) x = wrapRect.width - 310;
+ if (y > wrapRect.height - 360) y = wrapRect.height - 360;
+ if (y < 20) y = 20;
+ return { x: x, y: y };
+ }
+
// ---- Slash menu ----
function initSlashItems() { slashItems = Array.from(slashMenu.querySelectorAll('.slash-item')); }
@@ -631,9 +657,11 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
slashMenu.classList.add('on'); slashVisible = true; slashIdx = -1;
slashSearch.value = ''; slashSearch.focus();
filterSlash('');
- // Position
- var wr = editorWrap.getBoundingClientRect();
- slashMenu.style.left = '40px'; slashMenu.style.top = '80px';
+ // Position below current line at slash character
+ var pos = getPos().s - 1; // position of the / character
+ var pt = caretToPixel(pos);
+ slashMenu.style.left = pt.x + 'px';
+ slashMenu.style.top = pt.y + 'px';
}
function hideSlash() { slashMenu.classList.remove('on'); slashVisible = false; resetSlash(); }
@@ -675,9 +703,9 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
function showBubble() {
if (ta.selectionStart === ta.selectionEnd) { hideBubble(); return; }
var tr = ta.getBoundingClientRect();
- var top = tr.top - 44;
+ var top = tr.bottom + 6;
var left = tr.left + (tr.width / 2) - (bubble.offsetWidth / 2);
- if (top < 56) top = tr.bottom + 6;
+ if (top + 36 > window.innerHeight) top = tr.top - 44;
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';
@@ -706,10 +734,19 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
}
// ---- Quick Insert ----
+ function showQI() {
+ qiMenu.classList.remove('on');
+ // Position below current line, left-aligned
+ var pt = caretToPixel(null);
+ pt.x = Math.max(pt.x - 10, 4);
+ pt.y += 2;
+ qiMenu.style.left = pt.x + 'px';
+ qiMenu.style.top = pt.y + 'px';
+ qiMenu.classList.add('on');
+ }
function toggleQI() {
- var r = document.getElementById('qi-btn').getBoundingClientRect();
- qiMenu.style.left = (r.right + 6) + 'px'; qiMenu.style.top = r.top + 'px';
- qiMenu.classList.toggle('on');
+ if (qiMenu.classList.contains('on')) { qiMenu.classList.remove('on'); return; }
+ showQI();
}
// ---- AI Panel ----
@@ -792,6 +829,8 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
if (e.ctrlKey && e.key === 's') { e.preventDefault(); forceSave(); }
if (e.ctrlKey && e.key === 'b') { e.preventDefault(); wrapSelection('**'); }
if (e.ctrlKey && e.key === 'i') { e.preventDefault(); wrapSelection('*'); }
+ // Quick Insert: Alt+I
+ if (e.altKey && e.key === 'i') { e.preventDefault(); showQI(); }
});
ta.addEventListener('keyup', function(e) {