Skip to content

[ENG-1108] Make discourse tool lock#709

Open
trangdoan982 wants to merge 1 commit intomainfrom
eng-1108-make-tool-lock-work-for-discourse-tool-in-roam
Open

[ENG-1108] Make discourse tool lock#709
trangdoan982 wants to merge 1 commit intomainfrom
eng-1108-make-tool-lock-work-for-discourse-tool-in-roam

Conversation

@trangdoan982
Copy link
Collaborator

@trangdoan982 trangdoan982 commented Jan 16, 2026

Summary by CodeRabbit

Release Notes

  • New Features
    • Improved canvas tool selection behavior: when exiting selection mode, the application now automatically restores your previously used drawing tool for faster workflow continuity.

✏️ Tip: You can customize this high-level summary in your review settings.

@linear
Copy link

linear bot commented Jan 16, 2026

@supabase
Copy link

supabase bot commented Jan 16, 2026

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

@trangdoan982
Copy link
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 16, 2026

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 16, 2026

📝 Walkthrough

Walkthrough

Adds sticky tool functionality that tracks user tool selections and automatically restores the last "sticky" tool (discourse nodes, relations, actions) when users select the default selection tool, with tracking through refs and context propagation.

Changes

Cohort / File(s) Summary
Sticky Tool State Management
apps/roam/src/components/canvas/Tldraw.tsx
Added useCallback and useValue hooks; created stickyToolIds memoized list combining discourse nodes, relation names, and actions; introduced toolSelectionRef to track lastStickyToolId and lastExplicitToolId; implemented handleToolSelected callback to record explicit selections and update sticky tool state
Context Signature Expansion
apps/roam/src/components/canvas/Tldraw.tsx
Modified InsideEditorAndUiContext component to accept stickyToolIds (string[]) and toolSelectionRef props; wired these into the context tree; added effect to watch currentToolId and restore sticky tool when selecting "select" unless recent explicit selection occurred
Tool Selection Event Plumbing
apps/roam/src/components/canvas/uiOverrides.tsx
Added optional onToolSelected callback parameter to createUiOverrides; wired callback invocations before tool state updates for discourse-tool, select tool, per-node tools, relation tools, and action-derived tools

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main objective of the changeset: implementing sticky tool behavior for the discourse tool to make it 'lock' (remain selected) when switching to other tools.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

Comment @coderabbitai help to get the list of available commands and usage tips.

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: 1

🤖 Fix all issues with AI agents
In `@apps/roam/src/components/canvas/Tldraw.tsx`:
- Around line 763-787: The effect that restores lastStickyToolId should validate
that the stored ID is still available in stickyToolIds before calling
editor.setCurrentTool; in the useEffect referencing currentToolId, editor,
stickyToolIds, and toolSelectionRef, add a guard that ensures
toolSelectionRef.current.lastStickyToolId exists and is included in
stickyToolIds (and not equal to "select") before invoking
editor.setCurrentTool(lastStickyTool), so stale/removed tool IDs are not
attempted to be restored.
🧹 Nitpick comments (1)
apps/roam/src/components/canvas/Tldraw.tsx (1)

279-303: Add an explicit return type to the new callback.

This keeps the new handleToolSelected aligned with the TS return-type guideline.

♻️ Proposed fix
-  const handleToolSelected = useCallback(
-    (toolId: string) => {
+  const handleToolSelected = useCallback(
+    (toolId: string): void => {
       toolSelectionRef.current.lastExplicitToolId = toolId;
       if (stickyToolIds.includes(toolId)) {
         toolSelectionRef.current.lastStickyToolId = toolId;
       }
     },
     [stickyToolIds],
   );
As per coding guidelines, please add explicit return types for new functions.

Comment on lines +763 to +787
const currentToolId = useValue(
"currentToolId",
() => editor.getCurrentToolId(),
[editor],
);

useEffect(() => {
if (stickyToolIds.includes(currentToolId)) {
toolSelectionRef.current.lastStickyToolId = currentToolId;
return;
}

if (currentToolId !== "select") {
return;
}

if (toolSelectionRef.current.lastExplicitToolId === "select") return;

const lastStickyTool = toolSelectionRef.current.lastStickyToolId;
if (!lastStickyTool) return;
if (lastStickyTool === "select") return;
if (editor.getCurrentToolId() !== "select") return;

editor.setCurrentTool(lastStickyTool);
}, [currentToolId, editor, stickyToolIds, toolSelectionRef]);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n apps/roam/src/components/canvas/Tldraw.tsx | sed -n '750,800p'

Repository: DiscourseGraphs/discourse-graph

Length of output: 2024


🏁 Script executed:

# First, let's understand the stickyToolIds and how it's populated
rg "stickyToolIds" apps/roam/src/components/canvas/Tldraw.tsx -B 3 -A 3

Repository: DiscourseGraphs/discourse-graph

Length of output: 1856


🏁 Script executed:

# Check what version of tldraw is being used
cat apps/roam/package.json | grep -A 5 -B 5 "tldraw"

Repository: DiscourseGraphs/discourse-graph

Length of output: 931


🏁 Script executed:

# Look for how tools are managed and if there's any validation
rg "setCurrentTool" apps/roam/src/components/canvas/Tldraw.tsx -B 2 -A 2

Repository: DiscourseGraphs/discourse-graph

Length of output: 239


🏁 Script executed:

rg "setCurrentTool" apps/roam/src/components/canvas/Tldraw.tsx -B 5 -A 5

Repository: DiscourseGraphs/discourse-graph

Length of output: 562


🏁 Script executed:

# Check if there's any try-catch or error handling around tool setting
rg "try|catch" apps/roam/src/components/canvas/Tldraw.tsx | head -20

Repository: DiscourseGraphs/discourse-graph

Length of output: 405


🏁 Script executed:

# Look for any validation of tool IDs in the codebase
rg "isValidTool|validateTool|validToolIds" apps/roam/src/

Repository: DiscourseGraphs/discourse-graph

Length of output: 57


🏁 Script executed:

# Check the type definitions or usage of setCurrentTool to understand its behavior
rg "editor\." apps/roam/src/components/canvas/Tldraw.tsx | grep -i "tool"

Repository: DiscourseGraphs/discourse-graph

Length of output: 210


🏁 Script executed:

# Check if there's any documentation or type info about setCurrentTool in node_modules or imports
rg "setCurrentTool" --type ts -A 10 | head -30

Repository: DiscourseGraphs/discourse-graph

Length of output: 2381


🏁 Script executed:

# Look for how handleToolSelected validates the toolId
rg "handleToolSelected" apps/roam/src/components/canvas/Tldraw.tsx -B 2 -A 10

Repository: DiscourseGraphs/discourse-graph

Length of output: 807


🏁 Script executed:

# Check what happens when we look at the full context of tool ID management
sed -n '600,800p' apps/roam/src/components/canvas/Tldraw.tsx | grep -A 20 -B 5 "stickyToolIds"

Repository: DiscourseGraphs/discourse-graph

Length of output: 2677


🌐 Web query:

tldraw 2.3.0 setCurrentTool invalid tool id error handling

💡 Result:

Short answer: in tldraw v2.3.0 editor.setCurrentTool(id) does not expose a documented thrown exception for an unknown/invalid tool id — the API is documented as taking a string id and returning this, and the docs show using it for deep transitions (e.g. "select.eraser.pointing"). If you pass an id that isn't in the editor's state chart it will not succeed in switching to that state (effectively a no-op) — there's no documented runtime error case to rely on. [1][2]

Recommended handling

  • Validate before calling: check the tool id exists in your editor tooling (your UI overrides / tools map or the StateNode ids you registered) and only call setCurrentTool with a known id.
  • After calling, you can verify the switch with editor.getCurrentToolId() and handle a fallback if it didn't change.
  • If you need dynamic tools, use editor.setTool(...) / editor.removeTool(...) to add/remove tools at runtime (available on recent SDK releases). [2][3]

References

  • Editor API (setCurrentTool, getCurrentToolId). [1]
  • Tools / setCurrentTool usage (deep transitions example). [2]
  • Dynamic tools (setTool / removeTool) docs. [3]

References:
[1] tldraw Editor reference — setCurrentTool / getCurrentToolId.
[2] tldraw Tools docs (examples showing editor.setCurrentTool usage).
[3] tldraw example: Dynamic tools with setTool/removeTool.


🏁 Script executed:

# Check if there are any other guard patterns in the codebase for tool validation
rg "includes.*Tool" apps/roam/src/components/canvas/ -A 2 -B 2

Repository: DiscourseGraphs/discourse-graph

Length of output: 438


Add guard to prevent restoring invalid tool IDs.

When allNodes changes and a node type is removed, stickyToolIds updates accordingly, but lastStickyToolId remains in the ref. If a user previously selected a tool that's no longer in stickyToolIds, attempting to restore it with setCurrentTool will silently fail (no exception, just a no-op). Add the guard to validate the stored tool is still available before attempting to restore it.

🐛 Suggested guard
     const lastStickyTool = toolSelectionRef.current.lastStickyToolId;
     if (!lastStickyTool) return;
+    if (!stickyToolIds.includes(lastStickyTool)) return;
     if (lastStickyTool === "select") return;
     if (editor.getCurrentToolId() !== "select") return;

     editor.setCurrentTool(lastStickyTool);
🤖 Prompt for AI Agents
In `@apps/roam/src/components/canvas/Tldraw.tsx` around lines 763 - 787, The
effect that restores lastStickyToolId should validate that the stored ID is
still available in stickyToolIds before calling editor.setCurrentTool; in the
useEffect referencing currentToolId, editor, stickyToolIds, and
toolSelectionRef, add a guard that ensures
toolSelectionRef.current.lastStickyToolId exists and is included in
stickyToolIds (and not equal to "select") before invoking
editor.setCurrentTool(lastStickyTool), so stale/removed tool IDs are not
attempted to be restored.

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