diff --git a/apps/roam/src/components/CreateNodeDialog.tsx b/apps/roam/src/components/CreateNodeDialog.tsx index 4211f7c56..32e35da67 100644 --- a/apps/roam/src/components/CreateNodeDialog.tsx +++ b/apps/roam/src/components/CreateNodeDialog.tsx @@ -12,6 +12,8 @@ import getDiscourseNodes, { import { getNewDiscourseNodeText } from "~/utils/formatUtils"; import MenuItemSelect from "roamjs-components/components/MenuItemSelect"; import createBlock from "roamjs-components/writes/createBlock"; +import { findSimilarNodes, SuggestedNode } from "~/utils/hyde"; +import { Spinner } from "@blueprintjs/core"; export type CreateNodeDialogProps = { onClose: () => void; @@ -37,6 +39,12 @@ const CreateNodeDialog = ({ const [selectedType, setSelectedType] = useState(defaultNodeType); const [loading, setLoading] = useState(false); + const [filteredSuggestions, setFilteredSuggestions] = useState< + SuggestedNode[] + >([]); + const [rawSuggestions, setRawSuggestions] = useState([]); + const [suggestionsLoading, setSuggestionsLoading] = useState(false); + const [formattedTitle, setFormattedTitle] = useState(""); const inputRef = useRef(null); useEffect(() => { @@ -45,16 +53,55 @@ const CreateNodeDialog = ({ } }, []); + useEffect(() => { + let isCancelled = false; + const compute = async () => { + const base = title.trim(); + if (!base) { + setFormattedTitle(""); + return; + } + const ft = await getNewDiscourseNodeText({ + text: base, + nodeType: selectedType.type, + blockUid: sourceBlockUid, + }); + if (!isCancelled) setFormattedTitle(ft || ""); + }; + void compute(); + return () => { + isCancelled = true; + }; + }, [title, selectedType.type, sourceBlockUid]); + + useEffect(() => { + const fetchSuggestions = async () => { + if (formattedTitle.trim()) { + setSuggestionsLoading(true); + console.log( + "fetching suggestions for", + formattedTitle, + selectedType.type, + ); + const { raw, filtered } = await findSimilarNodes({ + text: formattedTitle, + nodeType: selectedType.type, + }); + setRawSuggestions(raw); + setFilteredSuggestions(filtered); + setSuggestionsLoading(false); + } else { + setRawSuggestions([]); + setFilteredSuggestions([]); + } + }; + void fetchSuggestions(); + }, [formattedTitle, selectedType.type]); + const onCreate = async () => { if (!title.trim()) return; setLoading(true); - const formattedTitle = await getNewDiscourseNodeText({ - text: title.trim(), - nodeType: selectedType.type, - blockUid: sourceBlockUid, - }); - if (!formattedTitle) { setLoading(false); return; @@ -119,6 +166,24 @@ const CreateNodeDialog = ({ onClose(); }; + const handleSuggestionClick = async (node: SuggestedNode) => { + if (sourceBlockUid) { + const pageRef = `[[${node.text}]]`; + await updateBlock({ + uid: sourceBlockUid, + text: pageRef, + }); + await createBlock({ + parentUid: sourceBlockUid, + order: 0, + node: { + text: initialTitle, + }, + }); + } + onClose(); + }; + return ( + {suggestionsLoading && ( +
+ + Fetching possible duplicates... +
+ )} + {rawSuggestions.length > 0 && ( +
+

Possible duplicates (Semantic)

+ +
+ )} + {filteredSuggestions.length > 0 && ( +
+

Possible duplicates (LLM Filtered)

+ +
+ )} +