-
Notifications
You must be signed in to change notification settings - Fork 3
[ENG-658] Add existing node flow #389
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ENG-658] Add existing node flow #389
Conversation
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
This stack of pull requests is managed by Graphite. Learn more about stacking. |
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
📝 WalkthroughWalkthroughRefactors QueryEngine.searchCompatibleNodeByTitle to use an options object and adds searchDiscourseNodesByTitle. Updates RelationshipSection to the new API. Extends SearchBar with an optional className. Adds ExistingNodeSearch and integrates it into DiscourseNodePanel. Wraps TldrawPreviewComponent with PluginProvider in TldrawView. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant ENS as ExistingNodeSearch
participant SB as SearchBar
participant QE as QueryEngine
participant DC as Datacore
participant Obs as Obsidian (Vault)
participant TL as tldraw Editor
participant FS as Canvas File
User->>SB: Type query
SB-->>ENS: onSearch(query)
ENS->>QE: searchDiscourseNodesByTitle(query, nodeTypeIds)
QE->>DC: Execute query (filter by nodeTypeId)
DC-->>QE: Results
QE-->>ENS: TFile[] (filtered/fuzzy)
ENS-->>SB: results
User->>SB: Select item
SB-->>ENS: onSelect(TFile)
ENS->>TL: getEditor()
alt Editor available
ENS->>FS: addWikilinkBlockrefForFile(selected)
ENS->>Obs: getFrontmatterForFile(selected)
ENS->>TL: create "discourse-node" shape (src, title, nodeTypeId)
ENS->>TL: select shape, commit history
else No editor
ENS-->>User: No-op
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~55 minutes Possibly related PRs
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
apps/obsidian/src/components/SearchBar.tsx (1)
38-53: Debounce can leave previous Promises unresolvedClearing the timeout cancels earlier searches but their returned Promises never resolve, which can leak if the caller holds them. Track the prior resolver and resolve to
[]on cancel.Apply:
class GenericSuggest<T> extends AbstractInputSuggest<T> { private getItemTextFn: (item: T) => string; private renderItemFn: (item: T, el: HTMLElement) => void; private onSelectCallback: (item: T) => void; private asyncSearchFn: (query: string) => Promise<T[]>; private minQueryLength: number; private debounceTimeout: number | null = null; + private pendingResolve: ((items: T[]) => void) | null = null; async getSuggestions(inputStr: string): Promise<T[]> { @@ - return new Promise((resolve) => { + return new Promise((resolve) => { if (this.debounceTimeout) { - clearTimeout(this.debounceTimeout); + clearTimeout(this.debounceTimeout); + // Resolve the previous promise to an empty result to avoid dangling promises. + this.pendingResolve?.([]); } + this.pendingResolve = resolve; this.debounceTimeout = window.setTimeout(async () => { try { const results = await this.asyncSearchFn(query); - resolve(results); + this.pendingResolve?.(results); } catch (error) { console.error(`[GenericSuggest] Error in async search:`, error); - resolve([]); + this.pendingResolve?.([]); } - }, 250); + this.pendingResolve = null; + }, 250); }); }apps/obsidian/src/services/QueryEngine.ts (3)
99-106: Fix query precedence, escaping, and missing await.
- Parentheses are needed around the OR group to avoid precedence issues.
- Escape IDs via JSON.stringify.
- dc.query is awaited in the other method; use await here too for consistency and correctness.
Apply this diff:
- const dcQuery = `@page and exists(nodeTypeId) and ${compatibleNodeTypeIds - .map((id) => `nodeTypeId = "${id}"`) - .join(" or ")}`; - - const potentialNodes = this.dc.query(dcQuery); + const dcQuery = `@page and exists(nodeTypeId) and (${compatibleNodeTypeIds + .map((id) => `nodeTypeId = ${JSON.stringify(id)}`) + .join(" or ")})`; + + const potentialNodes = await this.dc.query(dcQuery);
111-119: Normalize frontmatter relation value to an array.Frontmatter may be a single string or an array. Mapping over a string will throw.
Apply this diff:
- if (selectedRelationType) { - const fileCache = this.app.metadataCache.getFileCache(activeFile); - const existingRelations = - fileCache?.frontmatter?.[selectedRelationType] || []; - - existingRelatedFiles = existingRelations.map((relation: string) => { + if (selectedRelationType) { + const fileCache = this.app.metadataCache.getFileCache(activeFile); + const rawRelations = fileCache?.frontmatter?.[selectedRelationType]; + const existingRelations: string[] = Array.isArray(rawRelations) + ? rawRelations + : rawRelations + ? [rawRelations] + : []; + + existingRelatedFiles = existingRelations + .filter((r) => typeof r === "string" && r.trim().length > 0) + .map((relation: string) => { const match = relation.match(/\[\[(.*?)(?:\|.*?)?\]\]/); return match ? match[1] : relation.replace(/^\[\[|\]\]$/g, ""); - }); + });
121-130: Same as above: return only TFile instances (avoid unsafe cast).Align mapping with the stricter approach to prevent runtime errors accessing TFile props.
Apply this diff:
- const finalResults = searchResults - .map((dcFile: any) => { - if (dcFile && dcFile.$path) { - const realFile = this.app.vault.getAbstractFileByPath(dcFile.$path); - if (realFile && realFile instanceof TFile) { - return realFile; - } - } - return dcFile as TFile; - }) + const finalResults = searchResults + .map((dcFile: any) => { + if (dcFile?.$path) { + const real = this.app.vault.getAbstractFileByPath(dcFile.$path); + if (real instanceof TFile) return real; + } + return null; + }) + .filter((f): f is TFile => f instanceof TFile) .filter((file: TFile) => {
🧹 Nitpick comments (5)
apps/obsidian/src/components/SearchBar.tsx (1)
125-130: Avoid dynamic class fragments that JIT/Purge might missIf using a utility-CSS JIT/purge (e.g., Tailwind),
bg-${...}can fail to be included in the build. Prefer enumerating both tokens explicitly.Apply:
- className={`w-full p-2 ${ - selected ? "pr-9" : "" - } border-modifier-border rounded border bg-${ - selected || disabled ? "secondary" : "primary" - } ${disabled ? "cursor-not-allowed opacity-70" : "cursor-text"} ${className}`} + className={[ + "w-full p-2", + selected ? "pr-9" : "", + "border-modifier-border rounded border", + selected || disabled ? "bg-secondary" : "bg-primary", + disabled ? "cursor-not-allowed opacity-70" : "cursor-text", + className, + ].filter(Boolean).join(" ")}apps/obsidian/src/components/canvas/ExistingNodeSearch.tsx (1)
82-91: SearchBar styling hook works; consider min query lengthOptional: expose and pass a
minQueryLength={2}to avoid unnecessary queries on short inputs.- <SearchBar<TFile> + <SearchBar<TFile> onSelect={handleSelect} placeholder="Node search" getItemText={getItemText} renderItem={renderItem} asyncSearch={search} className="!bg-[var(--color-panel)] !text-[var(--color-text)]" + // minQueryLength prop would need to be added to SearchBar and forwarded to GenericSuggest + // minQueryLength={2} />apps/obsidian/src/services/QueryEngine.ts (3)
40-45: Consider a lightweight vault fallback when Datacore isn’t available.Right now search returns [] without Datacore. Optionally fall back to scanning vault markdown files and filtering by frontmatter nodeTypeId + fuzzy title match to keep UX functional.
I can draft a private fallbackSearchDiscourseNodesByTitle() that mirrors fallbackScanVault if you want.
149-151: Correct the log message to the actual method name.Apply this diff:
- console.error("Error in searchNodeByTitle:", error); + console.error("Error in searchCompatibleNodeByTitle:", error);
78-153: DRY: Reuse searchDiscourseNodesByTitle within searchCompatibleNodeByTitle.Call searchDiscourseNodesByTitle(query, compatibleNodeTypeIds) to build the base set, then apply active-file and existing-relations filters. Reduces duplicate query-building and result mapping logic.
I can provide a concrete refactor if you want this consolidated.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (6)
apps/obsidian/src/components/RelationshipSection.tsx(1 hunks)apps/obsidian/src/components/SearchBar.tsx(2 hunks)apps/obsidian/src/components/canvas/DiscourseNodePanel.tsx(2 hunks)apps/obsidian/src/components/canvas/ExistingNodeSearch.tsx(1 hunks)apps/obsidian/src/components/canvas/TldrawView.tsx(2 hunks)apps/obsidian/src/services/QueryEngine.ts(3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-22T10:40:52.752Z
Learnt from: sid597
PR: DiscourseGraphs/discourse-graph#232
File: apps/roam/src/utils/getAllDiscourseNodesSince.ts:18-31
Timestamp: 2025-06-22T10:40:52.752Z
Learning: In apps/roam/src/utils/getAllDiscourseNodesSince.ts, the user confirmed that querying for `?title` with `:node/title` and mapping it to the `text` field in the DiscourseGraphContent type is the correct implementation for retrieving discourse node content from Roam Research, despite it appearing to query page titles rather than block text content.
Applied to files:
apps/obsidian/src/services/QueryEngine.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
🔇 Additional comments (5)
apps/obsidian/src/components/canvas/TldrawView.tsx (1)
10-10: Wrap with PluginProvider looks correct; ensure single providerImport path and provider usage LGTM. This enables
usePlugin()in descendants rendered within this view.Also applies to: 151-159
apps/obsidian/src/components/SearchBar.tsx (1)
70-86: Public API extension (className) is fineAdding
className?: stringand threading it through is clean and backwards-compatible.apps/obsidian/src/components/canvas/DiscourseNodePanel.tsx (1)
35-41: Good integration of ExistingNodeSearchProps passed (
plugin,canvasFile,getEditor) are sufficient, and colocating with node types improves UX.apps/obsidian/src/components/canvas/ExistingNodeSearch.tsx (1)
25-29: Keep node type ids in sync if settings mutate in placeIf
plugin.settings.nodeTypesis mutated in place, this effect may not re-run. Consider derivingidson-demand for each search or listening to a settings change event if available.Would you like me to update this to read directly in
search()(and memoize) so it stays in sync without relying on reference changes?apps/obsidian/src/services/QueryEngine.ts (1)
78-88: Nice: object-arg signature for searchCompatibleNodeByTitle improves call-site clarity.Early guards for query length and Datacore availability are also good.
f19ba66 to
becf34e
Compare
9a430bf to
153be6d
Compare
becf34e to
2a3c1db
Compare
153be6d to
6ee110d
Compare
2a3c1db to
84361a4
Compare
6ee110d to
1b445e7
Compare
84361a4 to
62dc937
Compare
mdroidian
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good stuff so far!
1b445e7 to
3a43d20
Compare
62dc937 to
19a4601
Compare
mdroidian
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3a43d20 to
0d69cd7
Compare
48cce4c to
16fcb65
Compare
* eng-658-add-existing-nodes-flow * address PR comments * small changes
* eng-658-add-existing-nodes-flow * address PR comments * small changes
* eng-658-add-existing-nodes-flow * address PR comments * small changes
* eng-658-add-existing-nodes-flow * address PR comments * small changes
* [ENG-495] Tldraw obsidian setup (#285) * cleaned * sm * address PR comments * [ENG-598] Data persistence for tldraw (#303) * data persistence to the file * error handling * address PR comments * address some PR comments * address other PR comments * address PR comments * [ENG-624] TLDraw Obsidian asset store (#326) * current state * works now * clean up * address PR comments * address PR reviews * cleanup * fix styling issues * address PR comments * correct styles * [ENG-599] Discourse node shape (#341) * current state * works now * clean up * address PR comments * address PR reviews * fix styling issues * latest progress * update new shape * shape defined * address PR comments * sm address PR review * current progress * reorg * address other PR comments * clean * simplify flow * address PR comments * [ENG-604] Create node flow (#387) * eng-604: create node flow * pwd * [ENG-658] Add existing node flow (#389) * eng-658-add-existing-nodes-flow * address PR comments * small changes * [ENG-601] Create settings for canvas and attachment default folder (#338) * add new settings * small add * ENG-600: Discourse Relation shape definition (#408) * ENG-605: Add new relation flow (#411) * [ENG-603] Add existing relations (#412) https://www.loom.com/share/3641f2a642714b0d849262344e8c6ee5?sid=0614c657-e541-4bfd-92df-9b1aa60945b6 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - New Features - Added a Relations overlay on the canvas that shows a “Relations” button when a discourse node is selected. - Introduced a Relations panel to view and manage relations for the selected node, including adding or removing links, with clear loading/error states. - Overlay appears above the canvas without disrupting existing tools. - Chores - Consolidated relation-type lookup into shared utilities and updated imports. No user-facing changes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> * [ENG-844] Add color setting for relation types (#429) * add color setting * address PR reviews * address PR commens * fix icons * ENG-812 Update of database cli tools (#401) * eng-812 : Update database cli tools: supabase, vercel, cucumber. * newer cucumber constrains node * [ENG-495] Tldraw obsidian setup (#285) * cleaned * sm * address PR comments * [ENG-598] Data persistence for tldraw (#303) * data persistence to the file * error handling * address PR comments * address some PR comments * address other PR comments * address PR comments * switch to pnpm * delete wrong rebase file * fix pnpm lock * fix type checks * address all the PR comments * delete redundant files * fix types * shift click to open file on the right split (#485) * address PR comments * final lint cleanup --------- Co-authored-by: Marc-Antoine Parent <maparent@acm.org>


https://www.loom.com/share/826ab70ee976490f940ed41b7b428af2
Summary by CodeRabbit