Register AI sections across all content pages with dual-anchor scroll tracking, cross-page navigation via [SECTION:xxx] tags, and right-margin positioning for the notes editor. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
55 lines
2.0 KiB
TypeScript
55 lines
2.0 KiB
TypeScript
import { useEffect } from 'react';
|
|
import { useFloatingChat } from '@/context/FloatingChatContext';
|
|
|
|
// Elements where double-click should NOT trigger the AI popup
|
|
const INTERACTIVE_TAGS = new Set(['INPUT', 'TEXTAREA', 'SELECT']);
|
|
|
|
export function useDoubleClickAI(): void {
|
|
const { openAtSection, moveToSection, sections, state } = useFloatingChat();
|
|
|
|
useEffect(() => {
|
|
const handler = (e: MouseEvent) => {
|
|
const target = e.target as HTMLElement;
|
|
|
|
// Skip interactive elements (preserve text selection behavior)
|
|
if (INTERACTIVE_TAGS.has(target.tagName)) return;
|
|
|
|
// Skip contenteditable elements UNLESS they're inside Milkdown
|
|
if (target.isContentEditable) {
|
|
const inMilkdown =
|
|
target.closest('.milkdown-container') ||
|
|
target.closest('.crepe-editor');
|
|
if (!inMilkdown) return;
|
|
// For Milkdown: only trigger if no text was selected by the double-click
|
|
const selection = window.getSelection();
|
|
if (selection && selection.toString().trim().length > 0) return;
|
|
}
|
|
|
|
// Walk up DOM to find nearest [data-ai-section]
|
|
const sectionEl = (target as Element).closest('[data-ai-section]');
|
|
if (!sectionEl) return;
|
|
|
|
const sectionId = sectionEl.getAttribute('data-ai-section');
|
|
if (!sectionId) return;
|
|
|
|
// If popup is already open at THIS section, do nothing
|
|
if (state.isOpen && state.activeSectionId === sectionId) return;
|
|
|
|
// Build opts for right-margin sections
|
|
const section = sections.get(sectionId);
|
|
const opts = section?.anchorMode === 'right-margin' ? { clickY: e.clientY } : undefined;
|
|
|
|
// If chat is already open at a different section, move (keep conversation)
|
|
if (state.isOpen) {
|
|
moveToSection(sectionId, opts);
|
|
return;
|
|
}
|
|
|
|
openAtSection(sectionId, opts);
|
|
};
|
|
|
|
document.addEventListener('dblclick', handler);
|
|
return () => document.removeEventListener('dblclick', handler);
|
|
}, [openAtSection, moveToSection, sections, state.isOpen, state.activeSectionId]);
|
|
}
|