import { type RefObject, useEffect, useState } from 'react'; import type { EditorView } from '@codemirror/view'; import type { Asset } from '../../../../lib/types'; interface Opts { getView: () => EditorView | null; editorRef: RefObject; getCachedAssets: () => Promise; } /** Inline `/` or `!` asset autocomplete dropdown. */ export function useAssetAutocomplete({ getView, editorRef, getCachedAssets }: Opts) { const [showAutocomplete, setShowAutocomplete] = useState(false); const [autocompleteAssets, setAutocompleteAssets] = useState([]); const [autocompletePos, setAutocompletePos] = useState({ top: 0, left: 0 }); async function triggerAutocomplete(view: EditorView) { try { const assets = await getCachedAssets(); setAutocompleteAssets(assets.slice(0, 8)); const pos = view.state.selection.main.head; const coords = view.coordsAtPos(pos); if (coords) { const editorRect = editorRef.current?.getBoundingClientRect(); if (editorRect) { setAutocompletePos({ top: coords.bottom - editorRect.top + 4, left: coords.left - editorRect.left, }); } } setShowAutocomplete(true); } catch { /* ignore */ } } function insertAssetMarkdown(asset: Asset) { const view = getView(); if (!view) return; const isImage = /\.(jpg|jpeg|png|webp|gif|svg)$/i.test(asset.name); const md = isImage ? `![${asset.name}](${asset.url})` : `[${asset.name}](${asset.url})`; const pos = view.state.selection.main.head; const line = view.state.doc.lineAt(pos); const textBefore = line.text.slice(0, pos - line.from); const triggerIdx = Math.max(textBefore.lastIndexOf('/'), textBefore.lastIndexOf('!')); if (triggerIdx !== -1) { const from = line.from + triggerIdx; view.dispatch({ changes: { from, to: pos, insert: md } }); } else { view.dispatch({ changes: { from: pos, insert: md } }); } view.focus(); setShowAutocomplete(false); } useEffect(() => { if (!showAutocomplete) return; const handler = () => setShowAutocomplete(false); window.addEventListener('click', handler); return () => window.removeEventListener('click', handler); }, [showAutocomplete]); return { showAutocomplete, setShowAutocomplete, autocompleteAssets, autocompletePos, triggerAutocomplete, insertAssetMarkdown, }; }