Skip to content

Conversation

@mdroidian
Copy link
Contributor

@mdroidian mdroidian commented Aug 26, 2025

BETA, WIP
https://www.loom.com/share/01f8363289bf43a3a7546dc3ff60212a

Summary by CodeRabbit

  • New Features

    • Optional Discourse Context overlay on Canvas nodes (beta). Appears on hover in the node’s top-right and doesn’t interfere with canvas interactions.
    • Overlay now recognizes either a tag or a page UID, improving context accuracy.
  • Settings

    • Added “(BETA) Overlay in Canvas” toggle in Personal Settings (off by default). Preference is saved.
  • UX Improvements

    • Smoother, on-demand overlay mounting with unchanged loading and display states.

…ionality

- Introduced DISCOURSE_CONTEXT_OVERLAY_IN_CANVAS_KEY to manage the visibility of the Discourse Context overlay in the canvas.
- Updated HomePersonalSettings to include a checkbox for enabling/disabling the overlay feature.
- Integrated useMemo in DiscourseNodeUtil to optimize the overlay setting retrieval.
- Added conditional rendering for the DiscourseContextOverlay in the canvas based on the overlay state.
@linear
Copy link

linear bot commented Aug 26, 2025

@supabase
Copy link

supabase bot commented Aug 26, 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 ↗︎.

…handling

- Updated DiscourseContextOverlay to accept uid prop, allowing for more flexible tag resolution.
- Refactored getOverlayInfo call to utilize the new tag logic based on uid or tag.
- Adjusted DiscourseNodeUtil to pass uid instead of deriving tag from it, enhancing clarity and functionality.
@mdroidian
Copy link
Contributor Author

@CodeRabbit full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 26, 2025

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 26, 2025

📝 Walkthrough

Walkthrough

Adds a canvas overlay feature for Discourse nodes gated by a new user setting. The overlay mounts on pointer enter, renders DiscourseContextOverlay, and now accepts either tag, uid, or both. Tag resolution supports UID-based lookup. A new user setting key is introduced and surfaced in HomePersonalSettings.

Changes

Cohort / File(s) Summary
Discourse overlay props and data resolution
apps/roam/src/components/DiscourseContextOverlay.tsx
Updates component to accept { tag, id, uid } via a new union prop type. Resolves effective tag from uid (via getPageTitleByPageUid) or tag, computes tagUid (uid or getPageUidByPageTitle(tag)), and uses the resolved tag in getOverlayInfo(...). No rendering structure changes.
Canvas integration and mounting behavior
apps/roam/src/components/canvas/DiscourseNodeUtil.tsx
Integrates overlay into canvas nodes behind a setting. Adds overlayMounted state, mounts overlay on onPointerEnter, renders positioned overlay container with DiscourseContextOverlay (passing uid and generated id), and stops pointer events bubbling from the overlay.
User setting key
apps/roam/src/data/userSettings.ts
Adds exported DISCOURSE_CONTEXT_OVERLAY_IN_CANVAS_KEY = "discourse-context-overlay-in-canvas".
Settings UI
apps/roam/src/components/settings/HomePersonalSettings.tsx
Adds a Checkbox to toggle the new setting. Initializes with getSetting(key, false) and persists via setSetting(key, value). Labeled “(BETA) Overlay in Canvas” with description.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant CN as CanvasNode (Discourse)
  participant Set as Settings
  participant UI as DiscourseContextOverlay
  participant RG as Roam Graph
  participant DF as getOverlayInfo

  U->>CN: Pointer enter
  CN->>Set: getSetting(DISCOURSE_CONTEXT_OVERLAY_IN_CANVAS_KEY)
  alt setting enabled
    CN->>CN: overlayMounted = true
    CN->>UI: Mount with { uid, id }
    par Resolve tag/uid
      UI->>RG: getPageTitleByPageUid(uid)
      UI->>RG: getPageUidByPageTitle(tag?)  Note over UI,RG: Used if uid not provided
    end
    UI->>DF: getOverlayInfo(resolvedTag)
    DF-->>UI: Overlay data (results, refs, score)
    UI-->>CN: Render overlay
  else disabled
    CN-->>U: No overlay
  end

  Note over UI,CN: Overlay container stops pointer down propagation
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 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.

❤️ 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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

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

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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: 2

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/components/DiscourseContextOverlay.tsx (1)

76-99: Fix stale-closure bug and add early-exit guard in getInfo

getInfo closes over newTag/tagUid but they’re not in the dependency array, so the overlay can show stale data after the node’s uid/tag changes. Also add a short-circuit when resolution fails.

Apply this patch:

-  const getInfo = useCallback(
-    () =>
-      getOverlayInfo(newTag)
-        .then(({ refs, results }) => {
-          const discourseNode = findDiscourseNode(tagUid);
-          if (discourseNode) {
-            const attribute = getSettingValueFromTree({
-              tree: getBasicTreeByParentUid(discourseNode.type),
-              key: "Overlay",
-              defaultValue: "Overlay",
-            });
-            return deriveDiscourseNodeAttribute({
-              uid: tagUid,
-              attribute,
-            }).then((score) => {
-              setResults(results);
-              setRefs(refs);
-              setScore(score);
-            });
-          }
-        })
-        .finally(() => setLoading(false)),
-    [tag, setResults, setLoading, setRefs, setScore],
-  );
+  const getInfo = useCallback(() => {
+    if (!newTag || !tagUid) {
+      setResults([]);
+      setRefs(0);
+      setScore("-");
+      setLoading(false);
+      return Promise.resolve();
+    }
+    return getOverlayInfo(newTag)
+      .then(({ refs, results }) => {
+        const discourseNode = findDiscourseNode(tagUid);
+        if (discourseNode) {
+          const attribute = getSettingValueFromTree({
+            tree: getBasicTreeByParentUid(discourseNode.type),
+            key: "Overlay",
+            defaultValue: "Overlay",
+          });
+          return deriveDiscourseNodeAttribute({
+            uid: tagUid,
+            attribute,
+          }).then((score) => {
+            setResults(results);
+            setRefs(refs);
+            setScore(score);
+          });
+        }
+      })
+      .finally(() => setLoading(false));
+  }, [newTag, tagUid]);
🧹 Nitpick comments (6)
apps/roam/src/data/userSettings.ts (1)

8-9: New setting key looks good

Consistent naming and scoping with adjacent keys. Consider adding a short JSDoc describing where this key is consumed (canvas overlay) to aid discoverability.

apps/roam/src/components/settings/HomePersonalSettings.tsx (2)

17-23: Confirm intentional use of extensionSettings store vs extensionAPI.settings

Existing settings in this component persist via extensionAPI.settings, while the new overlay-in-canvas toggle uses getSetting/setSetting from extensionSettings. If this separation is intentional (e.g., because canvas code reads from extensionSettings), great—just confirming so we don’t end up with fragmented config stores. If not intentional, consider normalizing on one backing store.


182-201: UI wiring is correct; ensure live effect or communicate refresh requirement

Checkbox correctly initializes/persists to DISCOURSE_CONTEXT_OVERLAY_IN_CANVAS_KEY. Note: the current canvas reader uses a memoized read of this key and won’t react to changes until re-rendered. I’m proposing a small change in DiscourseNodeUtil.tsx to re-read on hover so the toggle takes effect immediately. If you prefer not to change the canvas code, append “Must refresh after editing” to the Description here to set expectations.

apps/roam/src/components/DiscourseContextOverlay.tsx (2)

104-107: Trim effect dependencies to avoid redundant invokes

refresh already depends on getInfo; including both can cause duplicate re-runs. Keep only getInfo.

-  useEffect(() => {
-    getInfo();
-  }, [refresh, getInfo]);
+  useEffect(() => {
+    getInfo();
+  }, [getInfo]);

116-139: Disable popover trigger when uid resolution fails

Prevent passing an undefined uid into ContextContent and avoid enabling the button in that state.

-          <ContextContent uid={tagUid} results={results} />
+          <ContextContent uid={tagUid ?? ""} results={results} />
...
-          minimal
-          disabled={loading}
+          minimal
+          disabled={loading || !tagUid}
apps/roam/src/components/canvas/DiscourseNodeUtil.tsx (1)

458-462: Make feature flag reactive on hover (read setting at interaction time)

Currently isOverlayEnabled is read once via useMemo([]) and won’t reflect changes until a full remount. Re-read on pointer enter (cheap) so the toggle applies immediately without requiring a refresh.

-    // eslint-disable-next-line react-hooks/rules-of-hooks
-    const isOverlayEnabled = useMemo(
-      () => getSetting(DISCOURSE_CONTEXT_OVERLAY_IN_CANVAS_KEY, false),
-      [],
-    );
+    // eslint-disable-next-line react-hooks/rules-of-hooks
+    const [isOverlayEnabled, setIsOverlayEnabled] = useState<boolean>(
+      () => getSetting(DISCOURSE_CONTEXT_OVERLAY_IN_CANVAS_KEY, false),
+    );
...
-        onPointerEnter={() => setOverlayMounted(true)}
+        onPointerEnter={() => {
+          setIsOverlayEnabled(
+            getSetting(DISCOURSE_CONTEXT_OVERLAY_IN_CANVAS_KEY, false),
+          );
+          setOverlayMounted(true);
+        }}

Also applies to: 516-516

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 19ebc63 and 7f91ddf.

📒 Files selected for processing (4)
  • apps/roam/src/components/DiscourseContextOverlay.tsx (2 hunks)
  • apps/roam/src/components/canvas/DiscourseNodeUtil.tsx (5 hunks)
  • apps/roam/src/components/settings/HomePersonalSettings.tsx (2 hunks)
  • apps/roam/src/data/userSettings.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-25T15:53:21.774Z
Learnt from: sid597
PR: DiscourseGraphs/discourse-graph#372
File: apps/roam/src/components/DiscourseNodeMenu.tsx:116-116
Timestamp: 2025-08-25T15:53:21.774Z
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/DiscourseContextOverlay.tsx
📚 Learning: 2025-06-17T23:37:45.289Z
Learnt from: maparent
PR: DiscourseGraphs/discourse-graph#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/components/DiscourseContextOverlay.tsx
🔇 Additional comments (2)
apps/roam/src/components/DiscourseContextOverlay.tsx (1)

81-89: Use of discourseNode.type as UID is correct here

getBasicTreeByParentUid(discourseNode.type) aligns with our convention that node.type stores the UID. Thanks for keeping that subtlety in mind.

apps/roam/src/components/canvas/DiscourseNodeUtil.tsx (1)

537-547: Overlay mount pattern is solid

Lazy-mount on hover, event suppression via onPointerDown, and unique id namespacing look good. No concerns.

@mdroidian mdroidian merged commit b776e72 into main Aug 26, 2025
5 checks passed
@github-project-automation github-project-automation bot moved this to Done in General Aug 26, 2025
@mdroidian mdroidian deleted the eng-788-add-discourse-context-overlay-to-tldraw-canvas branch August 26, 2025 06:37
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.

2 participants