Skip to content

Conversation

@sid597
Copy link
Collaborator

@sid597 sid597 commented Jul 25, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a modal dialog for creating discourse nodes, accessible from tag hover actions.
    • Added a popup button that appears when hovering over certain tags, allowing quick creation of nodes with prefilled information.
  • Style

    • Updated string formatting for consistency in menu item rendering.

@linear
Copy link

linear bot commented Jul 25, 2025

@supabase
Copy link

supabase bot commented Jul 25, 2025

This pull request has been ignored for the connected project zytfjzqyijgagqxrzbmz because there are no changes detected in packages/database/supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@sid597
Copy link
Collaborator Author

sid597 commented Jul 26, 2025

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 26, 2025

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 26, 2025

📝 Walkthrough

Walkthrough

A new modal dialog component for creating discourse nodes is introduced, along with a mechanism to trigger this dialog from a popup that appears when hovering over discourse node tags. Supporting utilities for rendering and removing the popup are implemented. Event listeners are updated to handle tag hover events and manage the lifecycle of the popup and dialog components.

Changes

File(s) Change Summary
apps/roam/src/components/CreateNodeDialog.tsx New React component and helper for a modal dialog to create discourse nodes, with state and UI logic.
apps/roam/src/utils/renderNodeTagPopup.tsx New utility module to render and remove a popup button near a tag element, with positioning and cleanup.
apps/roam/src/utils/initializeObserversAndListeners.ts Adds nodeTagHoverListener to handle tag hover, extract context, and trigger popup/dialog for node creation.
apps/roam/src/index.ts Registers/unregisters nodeTagHoverListener on extension load/unload.
apps/roam/src/components/DiscourseNodeMenu.tsx Minor stylistic change: replaces single with double quotes in a string literal.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DOM
    participant nodeTagHoverListener
    participant renderNodeTagPopup
    participant CreateNodeDialog

    User->>DOM: Hover over .rm-page-ref tag
    DOM-->>nodeTagHoverListener: mouseover event
    nodeTagHoverListener->>renderNodeTagPopup: Show popup near tag
    User->>renderNodeTagPopup: Click "Create node" button
    renderNodeTagPopup->>CreateNodeDialog: Open dialog with context (block text, tag, node types)
    User->>CreateNodeDialog: Enter title, select type, click Create
    CreateNodeDialog->>DOM: Create discourse node, update block, show toast
    CreateNodeDialog->>renderNodeTagPopup: Remove popup on close
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (6)
apps/roam/src/utils/renderNodeTagPopup.tsx (2)

28-35: Add error handling for DOM operations.

Consider adding error handling around DOM manipulation operations to prevent runtime errors if the element positioning fails or DOM access is restricted.

export const renderNodeTagPopup = ({
  tagElement,
  onClick,
  label = "Create node",
}: {
  tagElement: HTMLElement;
  onClick: () => void;
  label?: string;
}) => {
  removeNodeTagPopup();

+  try {
    const rect = tagElement.getBoundingClientRect();

    currentPopup = document.createElement("div");
    currentPopup.id = "discourse-node-tag-popup";
    currentPopup.style.position = "absolute";
    currentPopup.style.left = `${rect.left + window.scrollX}px`;
    currentPopup.style.top = `${rect.bottom + window.scrollY + 4}px`;
    currentPopup.className = "z-[9999] max-w-none font-inherit bg-white";

    document.body.appendChild(currentPopup);
+  } catch (error) {
+    console.error("Failed to create popup:", error);
+    return;
+  }

33-33: Consider making z-index configurable or using CSS custom properties.

The hardcoded z-index value of 9999 might conflict with other UI elements. Consider making it configurable or using a CSS custom property for better maintainability.

-  currentPopup.className = "z-[9999] max-w-none font-inherit bg-white";
+  currentPopup.className = "z-[var(--popup-z-index,9999)] max-w-none font-inherit bg-white";
apps/roam/src/utils/initializeObserversAndListeners.ts (2)

297-343: Consider performance optimization for the hover listener.

The current implementation processes every mouseover event on the document, which could impact performance during heavy DOM interaction. Consider adding debouncing or early filtering to reduce computational overhead.

+import { debounce } from "lodash"; // or implement a simple debounce

-  const nodeTagHoverListener = (e: Event) => {
+  const nodeTagHoverListener = debounce((e: Event) => {
    const target = e.target as HTMLElement | null;
    if (!target || !target.classList?.contains("rm-page-ref")) return;
    // ... rest of the function
-  };
+  }, 100); // 100ms debounce

Alternatively, consider event delegation with more specific targeting:

  const nodeTagHoverListener = (e: Event) => {
    const target = e.target as HTMLElement | null;
-    if (!target || !target.classList?.contains("rm-page-ref")) return;
+    if (!target?.classList?.contains("rm-page-ref") || 
+        target.closest('.discourse-node-tag-popup')) return; // Avoid self-triggering

325-326: Improve regex pattern for tag removal.

The current regex /#\w+/g only matches word characters. Consider a more comprehensive pattern that handles various tag formats in Roam.

-    const cleanedBlockText = rawBlockText.replace(/#\w+/g, "").trim();
+    const cleanedBlockText = rawBlockText.replace(/#[\w-]+/g, "").trim();

Or for more comprehensive tag removal:

-    const cleanedBlockText = rawBlockText.replace(/#\w+/g, "").trim();
+    const cleanedBlockText = rawBlockText
+      .replace(/#[\w\-_.]+/g, "") // Handle hyphens, underscores, dots
+      .replace(/\[\[#?[^\]]+\]\]/g, "") // Handle page references with tags
+      .trim();
apps/roam/src/components/CreateNodeDialog.tsx (2)

84-103: Simplify cursor positioning logic and add error handling.

The cursor positioning logic is complex with a fallback approach. Consider simplifying and adding error handling for better maintainability.

      const newCursorPosition = pageRef.length;
-      const windowId =
-        window.roamAlphaAPI.ui.getFocusedBlock?.()?.["window-id"] || "main";
-
-      if (window.roamAlphaAPI.ui.setBlockFocusAndSelection) {
-        window.roamAlphaAPI.ui.setBlockFocusAndSelection({
-          location: { "block-uid": blockUid, "window-id": windowId },
-          selection: { start: newCursorPosition },
-        });
-      } else {
-        setTimeout(() => {
-          const textareaElements = document.querySelectorAll("textarea");
-          for (const el of textareaElements) {
-            if (getUids(el as HTMLTextAreaElement).blockUid === blockUid) {
-              (el as HTMLTextAreaElement).focus();
-              (el as HTMLTextAreaElement).setSelectionRange(
-                newCursorPosition,
-                newCursorPosition,
-              );
-              break;
-            }
-          }
-        }, 50);
-      }
+      
+      // Attempt cursor positioning with error handling
+      try {
+        const windowId =
+          window.roamAlphaAPI.ui.getFocusedBlock?.()?.["window-id"] || "main";
+
+        if (window.roamAlphaAPI.ui.setBlockFocusAndSelection) {
+          window.roamAlphaAPI.ui.setBlockFocusAndSelection({
+            location: { "block-uid": blockUid, "window-id": windowId },
+            selection: { start: newCursorPosition },
+          });
+        } else {
+          // Fallback approach
+          requestAnimationFrame(() => {
+            const textareaElements = document.querySelectorAll("textarea");
+            for (const el of textareaElements) {
+              try {
+                if (getUids(el as HTMLTextAreaElement).blockUid === blockUid) {
+                  (el as HTMLTextAreaElement).focus();
+                  (el as HTMLTextAreaElement).setSelectionRange(
+                    newCursorPosition,
+                    newCursorPosition,
+                  );
+                  break;
+                }
+              } catch (uidError) {
+                // Skip this element if UID extraction fails
+                continue;
+              }
+            }
+          });
+        }
+      } catch (focusError) {
+        console.warn("Failed to set cursor position:", focusError);
+      }

128-133: Add input validation and improve placeholder text.

Consider adding validation for the title input and making the placeholder more dynamic based on the selected node type.

            <InputGroup
-              placeholder={`This is a potential ${selectedType.text.toLowerCase()}`}
+              placeholder={selectedType.format 
+                ? `Format: ${selectedType.format.replace(/{text}|{content}/gi, "your text here")}`
+                : `Enter ${selectedType.text.toLowerCase()} title`}
              value={title}
              onChange={(e) => setTitle(e.currentTarget.value)}
              inputRef={inputRef}
+              onKeyDown={(e) => {
+                if (e.key === "Enter" && title.trim() && !loading) {
+                  onCreate();
+                }
+              }}
            />
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd46113 and fc76dbf.

📒 Files selected for processing (5)
  • apps/roam/src/components/CreateNodeDialog.tsx (1 hunks)
  • apps/roam/src/components/DiscourseNodeMenu.tsx (1 hunks)
  • apps/roam/src/index.ts (2 hunks)
  • apps/roam/src/utils/initializeObserversAndListeners.ts (4 hunks)
  • apps/roam/src/utils/renderNodeTagPopup.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
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.
apps/roam/src/index.ts (5)

Learnt from: CR
PR: DiscourseGraphs/discourse-graph#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

Learnt from: CR
PR: DiscourseGraphs/discourse-graph#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

Learnt from: CR
PR: DiscourseGraphs/discourse-graph#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

Learnt from: CR
PR: DiscourseGraphs/discourse-graph#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

Learnt from: maparent
PR: #220
File: apps/roam/src/utils/conceptConversion.ts:11-40
Timestamp: 2025-06-23T11:49:45.457Z
Learning: In the DiscourseGraphs/discourse-graph codebase, direct access to window.roamAlphaAPI is the established pattern throughout the codebase. The team prefers to maintain this pattern consistently rather than making piecemeal changes, and plans to address dependency injection as a global refactor when scheduled.

apps/roam/src/components/DiscourseNodeMenu.tsx (6)

Learnt from: sid597
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: maparent
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.

Learnt from: maparent
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
PR: DiscourseGraphs/discourse-graph#0
File: .cursor/rules/main.mdc:0-0
Timestamp: 2025-07-19T22:34:08.725Z
Learning: Applies to {apps/website,packages/ui}/**/*.{tsx,jsx} : When refactoring inline styles, use tailwind classes

Learnt from: CR
PR: DiscourseGraphs/discourse-graph#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

Learnt from: sid597
PR: #232
File: apps/roam/src/utils/getAllDiscourseNodesSince.ts:15-16
Timestamp: 2025-06-22T10:40:21.679Z
Learning: In the getAllDiscourseNodesSince function in apps/roam/src/utils/getAllDiscourseNodesSince.ts, date validation is performed before the function is called, so additional date validation within the function is not needed.

apps/roam/src/utils/initializeObserversAndListeners.ts (6)

Learnt from: sid597
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: maparent
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.

Learnt from: CR
PR: DiscourseGraphs/discourse-graph#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

Learnt from: maparent
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
PR: DiscourseGraphs/discourse-graph#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

Learnt from: CR
PR: DiscourseGraphs/discourse-graph#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

apps/roam/src/utils/renderNodeTagPopup.tsx (3)

Learnt from: CR
PR: DiscourseGraphs/discourse-graph#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

Learnt from: CR
PR: DiscourseGraphs/discourse-graph#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

Learnt from: CR
PR: DiscourseGraphs/discourse-graph#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

apps/roam/src/components/CreateNodeDialog.tsx (7)

Learnt from: CR
PR: DiscourseGraphs/discourse-graph#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

Learnt from: sid597
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: maparent
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.

Learnt from: CR
PR: DiscourseGraphs/discourse-graph#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

Learnt from: maparent
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
PR: DiscourseGraphs/discourse-graph#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

Learnt from: maparent
PR: #220
File: apps/roam/src/utils/conceptConversion.ts:11-40
Timestamp: 2025-06-23T11:49:45.457Z
Learning: In the DiscourseGraphs/discourse-graph codebase, direct access to window.roamAlphaAPI is the established pattern throughout the codebase. The team prefers to maintain this pattern consistently rather than making piecemeal changes, and plans to address dependency injection as a global refactor when scheduled.

🔇 Additional comments (2)
apps/roam/src/index.ts (1)

105-105: LGTM! Clean integration following established patterns.

The nodeTagHoverListener integration follows the existing patterns perfectly - proper import, registration on document for mouseover events, and cleanup in the unload function.

Also applies to: 112-112, 151-151

apps/roam/src/components/DiscourseNodeMenu.tsx (1)

245-245: LGTM! Good stylistic consistency.

The change from single to double quotes maintains consistency with string literals in the codebase.

@sid597 sid597 marked this pull request as ready for review July 26, 2025 19:37
@sid597 sid597 requested a review from mdroidian July 26, 2025 19:38
Copy link
Contributor

@mdroidian mdroidian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good start! There is lots to consider with something like CreateNodeDialog so there's quite a few comment/suggestions.

We generate a type of "CreateNodeDialog" in multiple places (Canvas/DiscourseNodeMenu/DG Popover Menu/command palette (forthcoming)), so there are lots of previous patterns to glean from.

Just FYI, we'll be combing these in the future ENG-555: Consolidate node creation implementations but your CreateNodeDialog looks like a pretty good start 👍


const rect = tagElement.getBoundingClientRect();

currentPopup = document.createElement("div");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use Blueprint's <Popover> here. It's a pattern we've used quite a bit in the repo, and it handles things like positioning based on screen/DOM, click/hover, esc, overflow, etc.

See NodeMenu for the render/<Popover> pattern.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tried doing it but not able to get anywhere with it for quite some time, the popover needs a target to bind and creating the target then attaching to the popover leads to popover button rendered but not closing on mouse move from the button. keeping this part the same, need your help. Will give it another try tomorrow.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can see this exact pattern in Kanban as well: https://github.com/DiscourseGraphs/discourse-graph/blob/main/apps/roam/src/components/results-view/Kanban.tsx#L651

https://www.loom.com/share/c4b314168eee48108177c368f40b56c3

Attach the to the span of the hashtag as the target, as it already exists.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah the issue is that the span that we get is not a react element whereas the target requires (property) target?: string | JSX.Element | undefined so what we have to do is create a dummy element and attach it to dom and then render popover that element

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's see what you have at our next meeting and walk through it together.

@sid597 sid597 requested a review from mdroidian July 28, 2025 07:54
Copy link
Contributor

@mdroidian mdroidian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The biggest change we need to do is use an observer instead of a listener. We talked about this at the Dev meeting and we walked through some example code in our 1:1.

Here is the example code from our meeting:

const attributeObserver = createHTMLObserver({
    className: "rm-page-ref--tag",
    tag: "SPAN",
    callback: (s: HTMLSpanElement) => {
      // if (discourseTagSet.has(tag)) {
      renderAttributeButton(s);
      // }
    },
  });

I'll also attach the example button and styling code as it had a nice y offset and was centered.

export const renderAttributeButton = (parent: HTMLSpanElement) => {
  if (parent.dataset.attributeButtonRendered === "true") return;
  parent.dataset.attributeButtonRendered = "true";
  const wrapper = document.createElement("span");
  wrapper.style.position = "relative";
  wrapper.style.display = "inline-block";
  parent.parentNode?.insertBefore(wrapper, parent);
  wrapper.appendChild(parent);
  const reactRoot = document.createElement("span");
  reactRoot.style.position = "absolute";
  reactRoot.style.top = "0";
  reactRoot.style.left = "0";
  reactRoot.style.width = "100%";
  reactRoot.style.height = "100%";
  reactRoot.style.pointerEvents = "auto";
  reactRoot.style.zIndex = "10";
  wrapper.appendChild(reactRoot);
  ReactDOM.render(<AttributeButtonPopover />, reactRoot);
};

Popover modifiers

modifiers={{
        offset: {
          offset: "0, 10",
        },
        arrow: {
          enabled: false,
        },
      }}

Popover target

<span
  style={{
    display: "block",
    width: "100%",
    height: "100%",
 }}
/>

@sid597 sid597 changed the base branch from eng-621-show-node-tag-options-in-inline-node-creation-menu to main August 4, 2025 08:58
@sid597 sid597 requested a review from mdroidian August 4, 2025 09:31
Copy link
Contributor

@mdroidian mdroidian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 🚀

onChange={handleTagChange}
onBlur={handleTagBlur}
error={tagError}
placeholder={`#${node.text}`}
Copy link
Contributor

@mdroidian mdroidian Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I liked the # 😁.

I'm going to create a task to handle #, as we discussed today. We can re-add it once the use case is handled 😃

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants