feat: auto-show toolbar on text selection, position above selection
This commit is contained in:
@@ -27,7 +27,7 @@ export default defineContentScript({
|
|||||||
const end = el.selectionEnd ?? -1;
|
const end = el.selectionEnd ?? -1;
|
||||||
if (end - start >= 2) {
|
if (end - start >= 2) {
|
||||||
const text = el.value.substring(start, end).trim();
|
const text = el.value.substring(start, end).trim();
|
||||||
if (text.length >= 2) {
|
if (text.length > 10) {
|
||||||
selectedText = text;
|
selectedText = text;
|
||||||
storedStart = start;
|
storedStart = start;
|
||||||
storedEnd = end;
|
storedEnd = end;
|
||||||
@@ -43,7 +43,7 @@ export default defineContentScript({
|
|||||||
const sel = window.getSelection();
|
const sel = window.getSelection();
|
||||||
if (sel && sel.rangeCount > 0) {
|
if (sel && sel.rangeCount > 0) {
|
||||||
const text = sel.toString().trim();
|
const text = sel.toString().trim();
|
||||||
if (text.length >= 2) {
|
if (text.length > 10) {
|
||||||
selectedText = text;
|
selectedText = text;
|
||||||
storedRange = sel.getRangeAt(0).cloneRange(); // CLONE — selection will be lost later
|
storedRange = sel.getRangeAt(0).cloneRange(); // CLONE — selection will be lost later
|
||||||
storedElement = null;
|
storedElement = null;
|
||||||
@@ -98,29 +98,32 @@ export default defineContentScript({
|
|||||||
|
|
||||||
// ─── Toolbar ──────────────────────────────────────────────────────────────
|
// ─── Toolbar ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function getToolbarPosition(mouseX: number, mouseY: number) {
|
function getToolbarPosition(rect: DOMRect) {
|
||||||
const TOOLBAR_W = 280;
|
const TOOLBAR_W = 280;
|
||||||
const TOOLBAR_H = 40;
|
const TOOLBAR_H = 40;
|
||||||
const MARGIN = 8;
|
const MARGIN = 8;
|
||||||
const vpW = window.innerWidth;
|
|
||||||
const vpH = window.innerHeight;
|
|
||||||
const scrollX = window.scrollX;
|
const scrollX = window.scrollX;
|
||||||
const scrollY = window.scrollY;
|
const scrollY = window.scrollY;
|
||||||
|
|
||||||
let top = mouseY + scrollY - TOOLBAR_H - MARGIN;
|
// Center above the selection rect
|
||||||
let left = mouseX + scrollX - TOOLBAR_W / 2;
|
let left = rect.left + scrollX + rect.width / 2 - TOOLBAR_W / 2;
|
||||||
|
let top = rect.top + scrollY - TOOLBAR_H - MARGIN;
|
||||||
|
|
||||||
left = Math.max(scrollX + MARGIN, Math.min(left, scrollX + vpW - TOOLBAR_W - MARGIN));
|
// Clamp horizontally to viewport
|
||||||
if (top < scrollY + MARGIN) top = mouseY + scrollY + MARGIN + 16;
|
left = Math.max(scrollX + MARGIN, Math.min(left, scrollX + window.innerWidth - TOOLBAR_W - MARGIN));
|
||||||
if (top + TOOLBAR_H > scrollY + vpH - MARGIN) top = mouseY + scrollY - TOOLBAR_H - MARGIN - 16;
|
|
||||||
|
// Flip below if not enough space above
|
||||||
|
if (top < scrollY + MARGIN) {
|
||||||
|
top = rect.bottom + scrollY + MARGIN;
|
||||||
|
}
|
||||||
|
|
||||||
return { top, left };
|
return { top, left };
|
||||||
}
|
}
|
||||||
|
|
||||||
function showToolbar(mouseX: number, mouseY: number) {
|
function showToolbar(rect: DOMRect) {
|
||||||
hideToolbar();
|
hideToolbar();
|
||||||
|
|
||||||
const { top, left } = getToolbarPosition(mouseX, mouseY);
|
const { top, left } = getToolbarPosition(rect);
|
||||||
|
|
||||||
toolbar = document.createElement('div');
|
toolbar = document.createElement('div');
|
||||||
toolbar.id = 'lexai-toolbar';
|
toolbar.id = 'lexai-toolbar';
|
||||||
@@ -412,16 +415,31 @@ export default defineContentScript({
|
|||||||
if (target?.closest?.('[data-lexai="true"]')) return;
|
if (target?.closest?.('[data-lexai="true"]')) return;
|
||||||
|
|
||||||
// Capture selection state IMMEDIATELY — positions are valid right now.
|
// Capture selection state IMMEDIATELY — positions are valid right now.
|
||||||
// The setTimeout below is only for showing the toolbar (lets browser finalise).
|
// The setTimeout below lets the browser finalise the selection before we read it.
|
||||||
const captured = captureSelectionNow();
|
const captured = captureSelectionNow();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (captured && selectedText) {
|
if (!captured || !selectedText || selectedText.length <= 10) {
|
||||||
showToolbar(e.clientX + window.scrollX, e.clientY + window.scrollY);
|
hideToolbar();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get bounding rect from the live selection (still valid inside setTimeout)
|
||||||
|
let rect: DOMRect | null = null;
|
||||||
|
const sel = window.getSelection();
|
||||||
|
if (sel && sel.rangeCount > 0) {
|
||||||
|
rect = sel.getRangeAt(0).getBoundingClientRect();
|
||||||
|
} else if (storedElement) {
|
||||||
|
// fallback: use the element's bounding rect for textarea/input
|
||||||
|
rect = storedElement.getBoundingClientRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rect && rect.width > 0) {
|
||||||
|
showToolbar(rect);
|
||||||
} else {
|
} else {
|
||||||
hideToolbar();
|
hideToolbar();
|
||||||
}
|
}
|
||||||
}, 50);
|
}, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Hide toolbar when clicking elsewhere (not on our UI)
|
// Hide toolbar when clicking elsewhere (not on our UI)
|
||||||
|
|||||||
Reference in New Issue
Block a user