Skip to content

ENG-2037 Add tabbed node card context menu to Roam tldraw - #1272

Open
sid597 wants to merge 1 commit into
mainfrom
eng-2037-add-tabbed-node-card-context-menu-to-roam-tldraw
Open

ENG-2037 Add tabbed node card context menu to Roam tldraw#1272
sid597 wants to merge 1 commit into
mainfrom
eng-2037-add-tabbed-node-card-context-menu-to-roam-tldraw

Conversation

@sid597

@sid597 sid597 commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Overrides tldraw's StylePanel for Roam: selecting a single node card shows a Context tab (default) listing all discourse relations grouped by relation type, with +/− buttons that add or remove the related node on the canvas, and a Styling tab that reuses the stock styling controls. Regular tldraw shapes and multi-selection keep the default style panel. Adding a node also draws its existing relation arrows via createExistingRelations; removing deletes the node shape and its arrows — relation data in Roam is never modified.


Open in Devin Review

@linear-code

linear-code Bot commented Jul 31, 2026

Copy link
Copy Markdown

ENG-2037

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@supabase

supabase Bot commented Jul 31, 2026

Copy link
Copy Markdown

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 ↗︎.

@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
discourse-graph Skipped Skipped Jul 31, 2026 5:56am

Request Review

@sid597

sid597 commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Comment on lines +126 to +144
const toggleCanvasPresence = async ({
relatedUid,
text,
}: {
relatedUid: string;
text: string;
}) => {
setPendingUids((prev) => [...prev, relatedUid]);
try {
const existing = nodeShapesByUid.get(relatedUid);
if (existing) {
removeFromCanvas(existing);
} else {
await addToCanvas({ relatedUid, text });
}
} finally {
setPendingUids((prev) => prev.filter((u) => u !== relatedUid));
}
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Missing error handling in toggleCanvasPresence async function. If addToCanvas throws an error (e.g., network failure, missing data), it will be silently swallowed because:

  1. The try-finally block has no catch clause
  2. The onClick handler on line 185 uses void operator, discarding the rejected promise

This will leave users confused when add/remove operations fail without feedback.

Fix: Add a catch block to handle errors:

const toggleCanvasPresence = async ({
  relatedUid,
  text,
}: {
  relatedUid: string;
  text: string;
}) => {
  setPendingUids((prev) => [...prev, relatedUid]);
  try {
    const existing = nodeShapesByUid.get(relatedUid);
    if (existing) {
      removeFromCanvas(existing);
    } else {
      await addToCanvas({ relatedUid, text });
    }
  } catch (error) {
    dispatchToastEvent({
      id: "dg-context-tab-toggle-error",
      title: "Failed to update canvas",
      severity: "error",
    });
    console.error("Error toggling canvas presence:", error);
  } finally {
    setPendingUids((prev) => prev.filter((u) => u !== relatedUid));
  }
};
Suggested change
const toggleCanvasPresence = async ({
relatedUid,
text,
}: {
relatedUid: string;
text: string;
}) => {
setPendingUids((prev) => [...prev, relatedUid]);
try {
const existing = nodeShapesByUid.get(relatedUid);
if (existing) {
removeFromCanvas(existing);
} else {
await addToCanvas({ relatedUid, text });
}
} finally {
setPendingUids((prev) => prev.filter((u) => u !== relatedUid));
}
};
const toggleCanvasPresence = async ({
relatedUid,
text,
}: {
relatedUid: string;
text: string;
}) => {
setPendingUids((prev) => [...prev, relatedUid]);
try {
const existing = nodeShapesByUid.get(relatedUid);
if (existing) {
removeFromCanvas(existing);
} else {
await addToCanvas({ relatedUid, text });
}
} catch (error) {
dispatchToastEvent({
id: "dg-context-tab-toggle-error",
title: "Failed to update canvas",
severity: "error",
});
console.error("Error toggling canvas presence:", error);
} finally {
setPendingUids((prev) => prev.filter((u) => u !== relatedUid));
}
};

Spotted by Graphite

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment on lines +103 to +104
x: shape.x + shape.props.w + NEW_NODE_OFFSET_PX,
y: shape.y,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Related nodes added from the context list all land in the same spot, hiding each other

Every node added from the relation list is placed at one fixed position next to the selected card (x: shape.x + shape.props.w + NEW_NODE_OFFSET_PX at apps/roam/src/components/canvas/CustomStylePanel.tsx:103-104), so adding a second related node drops it exactly on top of the first one.
Impact: Users who add several related nodes see a single stack of overlapping cards and must drag them apart manually.

Fixed placement offset with no collision or fan-out logic

addToCanvas computes the new shape's position purely from the anchor shape (apps/roam/src/components/canvas/CustomStylePanel.tsx:98-117). It does not consider how many nodes were already added from the panel, nor whether other shapes already occupy that point, so N adds produce N shapes with identical x/y. A simple fix is to offset by the number of already placed siblings (e.g. stagger y by index or search for a free spot before creating).

Prompt for agents
In apps/roam/src/components/canvas/CustomStylePanel.tsx, addToCanvas always positions the newly created discourse node shape at shape.x + shape.props.w + NEW_NODE_OFFSET_PX, shape.y. When a user adds multiple related nodes from the Context tab, they are all created at that same point and visually stack on top of one another. Consider computing a placement that avoids collisions, e.g. stagger vertically based on how many shapes already occupy the target area (editor.getCurrentPageShapes / editor.getShapesAtPoint) or track the number of nodes added from this panel and offset accordingly.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


const NEW_NODE_OFFSET_PX = 80;

const ContextTabContent = ({ shape }: { shape: DiscourseNodeShape }) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 New panel code omits the explicit function return types the repo style guide requires

The newly added panel components and helpers are declared without explicit return types (for example const ContextTabContent = ({ shape }: { shape: DiscourseNodeShape }) => { at apps/roam/src/components/canvas/CustomStylePanel.tsx:28), which conflicts with the repository TypeScript rules.
Impact: The code diverges from the project's documented style, making future refactors harder to review.

Rule source and affected declarations

AGENTS.md and STYLE_GUIDE.md both state "Use explicit return types for functions". Affected declarations: ContextTabContent (apps/roam/src/components/canvas/CustomStylePanel.tsx:28), removeFromCanvas (:66), addToCanvas (:74), toggleCanvasPresence (:126), NodeCardPanelContent (:198), and CustomStylePanel (:226). Existing code in the same area follows the rule, e.g. SyncModeMenuSwitchItem returns ReactElement in apps/roam/src/components/canvas/uiOverrides.tsx:85-96.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant