-
Notifications
You must be signed in to change notification settings - Fork 3
[ENG-979] Consolidate create node flow #546
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
base: main
Are you sure you want to change the base?
Conversation
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
| "react-in-viewport": "1.0.0-alpha.20", | ||
| "react-vertical-timeline-component": "3.5.2", | ||
| "roamjs-components": "0.85.4", | ||
| "roamjs-components": "0.85.6", |
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.
i left these changes here after trying out the autoSelectFirstOption props because I think we should upgrade roamjs-compoenents anyway
📝 WalkthroughWalkthroughThis PR introduces a new ModifyNodeDialog component to replace the deprecated CreateNodeDialog, adding fuzzy-search and lockable input components for node creation/editing. Related utilities and canvas integrations are updated to use the new dialog, and a dependency is bumped. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Canvas as DiscourseNodeUtil
participant Dialog as ModifyNodeDialog
participant API as Roam API
participant Toast as Toast Notifications
User->>Canvas: Double-click node (edit mode)
Canvas->>Canvas: Set editing=true, dialogRendered=true
Canvas->>Dialog: renderModifyNodeDialog({mode: "edit", ...})
Dialog->>API: Fetch node types, content options
API-->>Dialog: Node types, content loaded
User->>Dialog: Select content / referenced node
User->>Dialog: Click Confirm
Dialog->>API: updateBlock(sourceBlockUid, newText)
API-->>Dialog: Update complete
Dialog->>Canvas: onSuccess(result)
Canvas->>API: Update node title, uid, image
Canvas->>Toast: Show success notification
Canvas->>Canvas: Set editing=false, dialogRendered=false
Dialog-->>User: Close dialog
sequenceDiagram
actor User
participant Popup as renderNodeTagPopup
participant Dialog as ModifyNodeDialog
participant API as Roam API
User->>Popup: Click create node trigger
Popup->>Popup: Get initialReferencedNode
Popup->>Dialog: renderModifyNodeDialog({mode: "create", initialValue, ...})
Dialog->>API: Fetch node types and content options
API-->>Dialog: Options loaded
User->>Dialog: Select node type
Dialog->>Dialog: Update content input options
User->>Dialog: Select/lock content
User->>Dialog: Click Confirm
Dialog->>API: fireQuery(createDiscourseNode)
API-->>Dialog: New node created
Dialog->>API: updateBlock(sourceBlockUid, newText)
API-->>Dialog: Source block updated
Dialog-->>User: Close with success
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Pre-merge checks✅ Passed checks (3 passed)
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. 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.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/roam/src/utils/formatUtils.ts (1)
64-91: Remove redundant DOM manipulation code.The
setupButtonControlfunction manipulates the dialog's DOM to enable/disable the submit button based on input. However,ModifyNodeDialogalready handles this internally (line 481 disables the button when!contentText.trim()). This code is redundant and could cause issues if the dialog's DOM structure changes.Apply this diff to remove the obsolete code:
}); - const setupButtonControl = () => { - const dialogs = document.querySelectorAll(".bp3-dialog"); - const dialog = dialogs[dialogs.length - 1] as HTMLElement; - - const input = dialog.querySelector( - 'input[type="text"].bp3-input', - ) as HTMLInputElement; - const submitBtn = dialog.querySelector( - ".bp3-dialog-footer .bp3-button.bp3-intent-primary", - ) as HTMLButtonElement; - - const updateButtonState = () => { - const currentValue = input.value; - const hasValue = currentValue.trim().length > 0; - const shouldBeDisabled = !hasValue; - submitBtn.disabled = shouldBeDisabled; - }; - - updateButtonState(); - - const listenerKey = "_discourseNodeListenerAttached"; - if (!(input as any)[listenerKey]) { - input.addEventListener("input", updateButtonState); - (input as any)[listenerKey] = true; - } - }; - requestAnimationFrame(setupButtonControl); });
🧹 Nitpick comments (2)
apps/roam/src/components/LockableAutocompleteInput.tsx (1)
34-37: Remove debug logging from production code.The
console.logcalls will fire on every selection and clutter the console. Please drop these before merging.Apply this cleanup:
- console.log("locking value", q); - console.log("initialUid", initialUid); - console.log("q.uid", q.uid); - console.log("mode", mode);apps/roam/src/utils/renderNodeTagPopup.tsx (1)
45-48: Track the TODO for initialReferencedNode implementation.The placeholder implementation returns empty values. Ensure this TODO is tracked in your issue tracker to implement proper referenced node detection from the block context.
Would you like me to open a new issue to track this implementation task?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
apps/roam/package.json(2 hunks)apps/roam/src/components/CreateNodeDialog.tsx(1 hunks)apps/roam/src/components/FuzzySelectInput.tsx(1 hunks)apps/roam/src/components/LockableAutocompleteInput.tsx(1 hunks)apps/roam/src/components/ModifyNodeDialog.tsx(1 hunks)apps/roam/src/components/canvas/DiscourseNodeUtil.tsx(3 hunks)apps/roam/src/utils/formatUtils.ts(2 hunks)apps/roam/src/utils/renderNodeTagPopup.tsx(2 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/main.mdc)
**/*.{ts,tsx}: Prefertypeoverinterface
Use explicit return types for functions
Avoidanytypes when possible
Files:
apps/roam/src/components/FuzzySelectInput.tsxapps/roam/src/components/CreateNodeDialog.tsxapps/roam/src/components/LockableAutocompleteInput.tsxapps/roam/src/utils/formatUtils.tsapps/roam/src/components/ModifyNodeDialog.tsxapps/roam/src/utils/renderNodeTagPopup.tsxapps/roam/src/components/canvas/DiscourseNodeUtil.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/main.mdc)
**/*.{ts,tsx,js,jsx}: Prefer arrow functions over regular function declarations
Use named parameters (object destructuring) when a function has more than 2 parameters
Use Prettier with the project's configuration
Maintain consistent naming conventions: PascalCase for components and types
Maintain consistent naming conventions: camelCase for variables and functions
Maintain consistent naming conventions: UPPERCASE for constants
Files:
apps/roam/src/components/FuzzySelectInput.tsxapps/roam/src/components/CreateNodeDialog.tsxapps/roam/src/components/LockableAutocompleteInput.tsxapps/roam/src/utils/formatUtils.tsapps/roam/src/components/ModifyNodeDialog.tsxapps/roam/src/utils/renderNodeTagPopup.tsxapps/roam/src/components/canvas/DiscourseNodeUtil.tsx
apps/roam/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/roam.mdc)
apps/roam/**/*.{js,jsx,ts,tsx}: Use BlueprintJS 3 components and Tailwind CSS for platform-native UI in the Roam Research extension
Use the roamAlphaApi documentation from https://roamresearch.com/#/app/developer-documentation/page/tIaOPdXCj when working with the Roam API
Use Roam Depot/Extension API documentation from https://roamresearch.com/#/app/developer-documentation/page/y31lhjIqU when working with the Roam Extension API
Files:
apps/roam/src/components/FuzzySelectInput.tsxapps/roam/src/components/CreateNodeDialog.tsxapps/roam/src/components/LockableAutocompleteInput.tsxapps/roam/src/utils/formatUtils.tsapps/roam/src/components/ModifyNodeDialog.tsxapps/roam/src/utils/renderNodeTagPopup.tsxapps/roam/src/components/canvas/DiscourseNodeUtil.tsx
apps/roam/package.json
📄 CodeRabbit inference engine (.cursor/rules/roam.mdc)
Prefer existing dependencies from package.json when adding or using dependencies in the Roam Research extension
Files:
apps/roam/package.json
🧠 Learnings (11)
📚 Learning: 2025-07-19T22:34:23.619Z
Learnt from: CR
Repo: DiscourseGraphs/discourse-graph PR: 0
File: .cursor/rules/roam.mdc:0-0
Timestamp: 2025-07-19T22:34:23.619Z
Learning: Applies to apps/roam/**/*.{js,jsx,ts,tsx} : Use BlueprintJS 3 components and Tailwind CSS for platform-native UI in the Roam Research extension
Applied to files:
apps/roam/src/components/FuzzySelectInput.tsxapps/roam/src/components/LockableAutocompleteInput.tsxapps/roam/package.jsonapps/roam/src/components/ModifyNodeDialog.tsx
📚 Learning: 2025-07-19T22:34:08.725Z
Learnt from: CR
Repo: DiscourseGraphs/discourse-graph PR: 0
File: .cursor/rules/main.mdc:0-0
Timestamp: 2025-07-19T22:34:08.725Z
Learning: Applies to {apps/website,packages/ui}/**/*.{tsx,jsx} : Use platform-native UI components first with shadcn/ui as a fallback
Applied to files:
apps/roam/src/components/FuzzySelectInput.tsx
📚 Learning: 2025-07-19T22:34:23.619Z
Learnt from: CR
Repo: DiscourseGraphs/discourse-graph PR: 0
File: .cursor/rules/roam.mdc:0-0
Timestamp: 2025-07-19T22:34:23.619Z
Learning: Applies to apps/roam/**/*.{js,jsx,ts,tsx} : Use Roam Depot/Extension API documentation from https://roamresearch.com/#/app/developer-documentation/page/y31lhjIqU when working with the Roam Extension API
Applied to files:
apps/roam/src/components/CreateNodeDialog.tsxapps/roam/package.json
📚 Learning: 2025-08-11T19:09:58.252Z
Learnt from: maparent
Repo: DiscourseGraphs/discourse-graph PR: 337
File: apps/roam/src/components/DiscourseFloatingMenu.tsx:43-43
Timestamp: 2025-08-11T19:09:58.252Z
Learning: The roam subdirectory (apps/roam) is constrained to React 17 and cannot use React 18+ features like createRoot API. ReactDOM.render should be used instead of createRoot in this subdirectory.
Applied to files:
apps/roam/src/components/CreateNodeDialog.tsxapps/roam/package.json
📚 Learning: 2025-07-19T22:34:23.619Z
Learnt from: CR
Repo: DiscourseGraphs/discourse-graph PR: 0
File: .cursor/rules/roam.mdc:0-0
Timestamp: 2025-07-19T22:34:23.619Z
Learning: Applies to apps/roam/**/*.{js,jsx,ts,tsx} : Use the roamAlphaApi documentation from https://roamresearch.com/#/app/developer-documentation/page/tIaOPdXCj when working with the Roam API
Applied to files:
apps/roam/src/components/CreateNodeDialog.tsxapps/roam/package.jsonapps/roam/src/components/ModifyNodeDialog.tsxapps/roam/src/utils/renderNodeTagPopup.tsx
📚 Learning: 2025-07-19T22:34:23.619Z
Learnt from: CR
Repo: DiscourseGraphs/discourse-graph PR: 0
File: .cursor/rules/roam.mdc:0-0
Timestamp: 2025-07-19T22:34:23.619Z
Learning: Applies to apps/roam/package.json : Prefer existing dependencies from package.json when adding or using dependencies in the Roam Research extension
Applied to files:
apps/roam/package.json
📚 Learning: 2025-07-19T22:34:16.794Z
Learnt from: CR
Repo: DiscourseGraphs/discourse-graph PR: 0
File: .cursor/rules/obsidian.mdc:0-0
Timestamp: 2025-07-19T22:34:16.794Z
Learning: Applies to apps/obsidian/package.json : Prefer existing dependencies from package.json when adding or using dependencies in the Obsidian plugin
Applied to files:
apps/roam/package.json
📚 Learning: 2025-06-22T10:40:52.752Z
Learnt from: sid597
Repo: DiscourseGraphs/discourse-graph PR: 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/roam/src/utils/formatUtils.tsapps/roam/src/components/canvas/DiscourseNodeUtil.tsx
📚 Learning: 2025-06-17T23:37:45.289Z
Learnt from: maparent
Repo: DiscourseGraphs/discourse-graph PR: 220
File: apps/roam/src/utils/conceptConversion.ts:42-56
Timestamp: 2025-06-17T23:37:45.289Z
Learning: In the DiscourseNode interface from apps/roam/src/utils/getDiscourseNodes.ts, the field `type` serves as the unique identifier field, not a type classification field. The interface has no `uid` or `id` field, making `node.type` the correct field to use for UID-related operations.
Applied to files:
apps/roam/src/utils/formatUtils.ts
📚 Learning: 2025-06-17T23:37:45.289Z
Learnt from: maparent
Repo: DiscourseGraphs/discourse-graph PR: 220
File: apps/roam/src/utils/conceptConversion.ts:42-56
Timestamp: 2025-06-17T23:37:45.289Z
Learning: In the DiscourseNode interface from apps/roam/src/utils/getDiscourseNodes.ts, the field `node.type` serves as the UID field rather than having a conventional `node.uid` field. This is an unusual naming convention where the type field actually contains the unique identifier.
Applied to files:
apps/roam/src/utils/formatUtils.tsapps/roam/src/components/ModifyNodeDialog.tsx
📚 Learning: 2025-08-25T15:53:21.799Z
Learnt from: sid597
Repo: DiscourseGraphs/discourse-graph PR: 372
File: apps/roam/src/components/DiscourseNodeMenu.tsx:116-116
Timestamp: 2025-08-25T15:53:21.799Z
Learning: In apps/roam/src/components/DiscourseNodeMenu.tsx, when handling tag insertion, multiple leading hashtags (like ##foo) should be preserved as they represent user intent, not normalized to a single hashtag. The current regex /^#/ is correct as it only removes one leading # before adding one back, maintaining any additional hashtags the user intended.
Applied to files:
apps/roam/src/components/ModifyNodeDialog.tsxapps/roam/src/utils/renderNodeTagPopup.tsx
🔇 Additional comments (12)
apps/roam/src/utils/formatUtils.ts (2)
15-18: LGTM: Clean import addition.The new imports for
ModifyNodeDialogand related types are well-organized and follow the project's import conventions.
20-23: LGTM: Type parameter correctly updated.The generic type parameter change from
FormDialogPropstoModifyNodeDialogPropsproperly reflects the migration to the new dialog component.apps/roam/src/utils/renderNodeTagPopup.tsx (2)
8-8: LGTM: Import updated correctly.The import change from the old dialog to
renderModifyNodeDialogaligns with the consolidation objective.
57-70: ****The no-op callbacks are intentional and appropriate. This function renders a simple UI button that triggers dialog-driven node creation—it doesn't manage node state or handle post-creation logic. The
ModifyNodeDialogcomponent itself handles creation, persistence, and cleanup. Unlike other call sites (e.g.,DiscourseNodeUtil.tsx) where the parent needs to track the created node's UID for state updates,renderNodeTagPopupButtonhas no such responsibility. The comment "Success is handled by the dialog itself" accurately reflects this design.Likely an incorrect or invalid review comment.
apps/roam/src/components/ModifyNodeDialog.tsx (8)
1-33: LGTM: Comprehensive imports.The imports are well-organized and include all necessary dependencies for the dialog functionality.
52-97: LGTM: Well-structured component initialization.The component uses appropriate React hooks with proper initialization and memoization. The filtering logic for discourse nodes based on
isFromCanvasis clear and well-documented.
98-124: LGTM: Robust referenced node detection.The logic correctly parses the node format string to extract referenced node information, with proper filtering of special cases like "content" and "context".
126-202: LGTM: Excellent async race condition handling.The use of request IDs and alive flags properly handles race conditions when fetching options. The cleanup function ensures no state updates occur after unmounting.
239-391: LGTM: Comprehensive create and edit logic.The submission logic handles both create and edit modes appropriately, with proper error handling, loading state management, and user feedback via toasts. The locked content flow is handled correctly.
393-497: LGTM: Well-structured dialog UI.The component follows BlueprintJS patterns correctly, with proper event handling to prevent TLDraw interference, appropriate disabled states, and clear error messaging.
As per coding guidelines
499-505: LGTM: Standard overlay render pattern.The helper function and default export follow established patterns in the codebase.
320-328: Fix invalidtypevalue; remove incorrect error suppression.The
@ts-expect-errorsuppression on"block-uid"is misplaced. According to the Roam API type definition,"block-uid"is a valid optional property. The actual TypeScript error is caused bytype: "outline", which is invalid. The validtypevalues are"block" | "page" | "query" | "graph" | "search".Fix: Replace
type: "outline"with a valid type value (e.g.,"block"), or adjust the window configuration to match the intended functionality. Remove the@ts-expect-errorsuppression.⛔ Skipped due to learnings
Learnt from: maparent Repo: DiscourseGraphs/discourse-graph PR: 0 File: :0-0 Timestamp: 2025-11-06T13:48:35.007Z Learning: In apps/roam/src/utils/createReifiedBlock.ts, the `setBlockProps` function does not need await as it is synchronous or fire-and-forget.Learnt from: maparent Repo: DiscourseGraphs/discourse-graph PR: 220 File: apps/roam/src/utils/conceptConversion.ts:42-56 Timestamp: 2025-06-17T23:37:45.289Z Learning: In the DiscourseNode interface from apps/roam/src/utils/getDiscourseNodes.ts, the field `type` serves as the unique identifier field, not a type classification field. The interface has no `uid` or `id` field, making `node.type` the correct field to use for UID-related operations.Learnt from: CR Repo: DiscourseGraphs/discourse-graph PR: 0 File: .cursor/rules/roam.mdc:0-0 Timestamp: 2025-07-19T22:34:23.619Z Learning: Applies to apps/roam/**/*.{js,jsx,ts,tsx} : Use the roamAlphaApi documentation from https://roamresearch.com/#/app/developer-documentation/page/tIaOPdXCj when working with the Roam API
| useEffect(() => { | ||
| if (mode === "edit" && referencedNode) { | ||
| setReferencedNodeLoading(true); | ||
| // TODO: replace this with a regex. this is extremely hacky primitive | ||
| const parseResult = contentText.trim().split("-"); | ||
| if (parseResult) { | ||
| // parseResult[1] is content, parseResult[2] is refnode | ||
| if (parseResult[1]) { | ||
| setContentText(parseResult[1].trim()); | ||
| } | ||
| if (parseResult[2]) { | ||
| const refText = parseResult[2].trim().replace(/[[\]]/g, ""); | ||
| setReferencedNodeText(refText); | ||
| const result = getPageUidByPageTitle(refText); | ||
| setReferencedNodeUid(result); | ||
| } | ||
| } | ||
| } | ||
| setReferencedNodeLoading(false); | ||
| }, [mode, referencedNode, nodeFormat, contentText]); |
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.
🛠️ Refactor suggestion | 🟠 Major
Fragile parsing logic needs improvement.
The comment on line 207 correctly identifies this as "extremely hacky primitive" parsing. Splitting on "-" is fragile and will break if the content contains hyphens. The regex mentioned in the TODO would be more robust.
Consider using the nodeFormat pattern to parse the content more reliably:
useEffect(() => {
if (mode === "edit" && referencedNode) {
setReferencedNodeLoading(true);
// Use the nodeFormat to extract content and referenced node
const regex = new RegExp(
nodeFormat
.replace(/{content}/gi, '(.+?)')
.replace(/{[\w\d-]+}/g, '\\[\\[(.+?)\\]\\]')
.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
);
const match = contentText.match(regex);
if (match) {
setContentText(match[1]?.trim() || "");
if (match[2]) {
const refText = match[2].trim();
setReferencedNodeText(refText);
const result = getPageUidByPageTitle(refText);
setReferencedNodeUid(result);
}
}
}
setReferencedNodeLoading(false);
}, [mode, referencedNode, nodeFormat, contentText]);🤖 Prompt for AI Agents
In apps/roam/src/components/ModifyNodeDialog.tsx around lines 204 to 223, the
current split-on-hyphen parsing is fragile; replace it with a regex derived from
nodeFormat: escape regex metacharacters, replace {content} with a non-greedy
capture group (e.g. (.+?)), replace reference placeholders with a capture for
the inner page title and literal double-bracket markers (e.g. \[\[(.+?)\]\]),
then run contentText.match(regex) and if there is a match
setContentText(match[1]?.trim() || "") and, if present,
setReferencedNodeText(match[2].trim()) and
setReferencedNodeUid(getPageUidByPageTitle(match[2].trim())); keep the
referencedNodeLoading toggles around the parse and ensure you don’t rely on
brittle string splitting.
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.
the regex needs to be more complex I'll follow up with another ticket that address this issue + also auto detecting Source/ RefNode when created from tagged block of child node
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.
@trangdoan982, sounds good! That makes sense to address this along with the auto-detection feature in a follow-up.
Would you like me to create a GitHub issue to track this work? I can include:
- The parsing logic improvement (replacing the split-on-hyphen with proper regex)
- Auto-detecting Source/RefNode when created from tagged block or child node
- Links back to this PR and comment for context
🧠 Learnings used
Learnt from: sid597
Repo: DiscourseGraphs/discourse-graph PR: 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.
Learnt from: sid597
Repo: DiscourseGraphs/discourse-graph PR: 372
File: apps/roam/src/components/DiscourseNodeMenu.tsx:116-116
Timestamp: 2025-08-25T15:53:21.799Z
Learning: In apps/roam/src/components/DiscourseNodeMenu.tsx, when handling tag insertion, multiple leading hashtags (like ##foo) should be preserved as they represent user intent, not normalized to a single hashtag. The current regex /^#/ is correct as it only removes one leading # before adding one back, maintaining any additional hashtags the user intended.
|
next action: rebase |
In node tag
https://www.loom.com/share/efccc26071d640c484858c9b9fc93ad2
In Canvas:
https://www.loom.com/share/da0df074fb264dc690acfb36c0bc07c8
In command
https://www.loom.com/share/a94cd3802e0f43f39f94ff65e4979dcd
Summary by CodeRabbit
New Features
Chores