diff --git a/apps/obsidian/src/components/ModifyNodeModal.tsx b/apps/obsidian/src/components/ModifyNodeModal.tsx index 7c07369bc..7bd3fcee4 100644 --- a/apps/obsidian/src/components/ModifyNodeModal.tsx +++ b/apps/obsidian/src/components/ModifyNodeModal.tsx @@ -19,11 +19,15 @@ type ModifyNodeFormProps = { title: string; initialFile?: TFile; // for edit mode selectedExistingNode?: TFile; + /** DiscourseRelation.id; when set, relation is created with currentFile as the other end. */ + relationshipId?: string; + relationshipTargetFile?: TFile; }) => Promise; onCancel: () => void; initialTitle?: string; initialNodeType?: DiscourseNode; initialFile?: TFile; // for edit mode + currentFile?: TFile; // the file where the node is being created from plugin: DiscourseGraphPlugin; }; @@ -34,6 +38,7 @@ export const ModifyNodeForm = ({ initialTitle = "", initialNodeType, initialFile, + currentFile, plugin, }: ModifyNodeFormProps) => { const isEditMode = !!initialFile; @@ -48,6 +53,9 @@ export const ModifyNodeForm = ({ const [isFocused, setIsFocused] = useState(false); const [searchResults, setSearchResults] = useState([]); const [isSearching, setIsSearching] = useState(false); + const [selectedRelationshipKey, setSelectedRelationshipKey] = useState< + string | undefined + >(undefined); const queryEngine = useRef(new QueryEngine(plugin.app)); const titleInputRef = useRef(null); const popoverRef = useRef(null); @@ -136,6 +144,78 @@ export const ModifyNodeForm = ({ } }, [activeIndex, isOpen]); + // Determine available relationships based on current file and selected node type + const availableRelationships = useMemo(() => { + if (!currentFile || !selectedNodeType || isEditMode) { + return []; + } + + const currentFileCache = plugin.app.metadataCache.getFileCache(currentFile); + const currentNodeTypeId = currentFileCache?.frontmatter?.nodeTypeId as + | string + | undefined; + + if (!currentNodeTypeId) { + return []; + } + + // Find all relations that connect the current node type to the selected node type + const relevantRelations = plugin.settings.discourseRelations.filter( + (relation) => + (relation.sourceId === currentNodeTypeId && + relation.destinationId === selectedNodeType.id) || + (relation.sourceId === selectedNodeType.id && + relation.destinationId === currentNodeTypeId), + ); + + const relations = relevantRelations + .map((relation) => { + const relationType = plugin.settings.relationTypes.find( + (rt) => rt.id === relation.relationshipTypeId, + ); + if (!relationType) return null; + + const isCurrentFileSource = relation.sourceId === currentNodeTypeId; + return { + relationTypeId: relation.relationshipTypeId, + label: isCurrentFileSource + ? relationType.label + : relationType.complement, + isCurrentFileSource, + uniqueKey: relation.id, + }; + }) + .filter(Boolean) as Array<{ + relationTypeId: string; + label: string; + isCurrentFileSource: boolean; + uniqueKey: string; + }>; + + return [ + ...relations, + { + uniqueKey: "", + label: "No relation", + relationTypeId: "", + isCurrentFileSource: false, + }, + ]; + }, [currentFile, selectedNodeType, isEditMode, plugin]); + + // Default to first option when list appears or selection is no longer valid + useEffect(() => { + const first = availableRelationships[0]; + if (!first) return; + const isValid = + selectedRelationshipKey !== undefined && + availableRelationships.some( + (r) => r.uniqueKey === selectedRelationshipKey, + ); + if (isValid) return; + setSelectedRelationshipKey(first.uniqueKey); + }, [availableRelationships, selectedRelationshipKey]); + const isFormValid = title.trim() && selectedNodeType; const handleSelect = useCallback((file: TFile) => { @@ -194,6 +274,7 @@ export const ModifyNodeForm = ({ setQuery(""); setTitle(""); } + setSelectedRelationshipKey(undefined); }; const handleQueryChange = (e: React.ChangeEvent) => { @@ -223,11 +304,18 @@ export const ModifyNodeForm = ({ try { setIsSubmitting(true); + const key = + selectedRelationshipKey ?? availableRelationships[0]?.uniqueKey ?? ""; + const selectedRel = key + ? availableRelationships.find((r) => r.uniqueKey === key) + : undefined; await onSubmit({ nodeType: selectedNodeType, title: trimmedTitle, initialFile, selectedExistingNode: selectedExistingNode || undefined, + relationshipId: selectedRel?.uniqueKey || undefined, + relationshipTargetFile: currentFile || undefined, }); onCancel(); } catch (error) { @@ -252,6 +340,9 @@ export const ModifyNodeForm = ({ isEditMode, initialFile, selectedExistingNode, + selectedRelationshipKey, + currentFile, + availableRelationships, ]); return ( @@ -371,6 +462,32 @@ export const ModifyNodeForm = ({ + {availableRelationships.length > 0 && !isEditMode && currentFile && ( +
+
+ Relationship with "{currentFile.basename}" +
+
+ +
+
+ )} +