Skip to content

Conversation

@trangdoan982
Copy link
Collaborator

@trangdoan982 trangdoan982 commented Oct 29, 2025

https://www.loom.com/share/059b956c0ef84c59aa5668d6ae7dfb76

Summary by CodeRabbit

  • New Features
    • Introduced keyboard shortcuts for opening linked files: Shift+Click opens in the sidebar, Cmd/Ctrl+Click opens in a new tab.
    • Added hover button on canvas elements to open linked files directly in the sidebar with one click.

@linear
Copy link

linear bot commented Oct 29, 2025

@supabase
Copy link

supabase bot commented Oct 29, 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 ↗︎.

@trangdoan982
Copy link
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 29, 2025

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 29, 2025

📝 Walkthrough

Walkthrough

The PR refactors file-opening behavior in the Obsidian canvas system. It extracts file-opening logic into utility functions, implements keyboard-modifier-based routing (Shift+Click for sidebar, Cmd/Ctrl+Click for new tab), and adds a hover-triggered UI button on canvas nodes to open linked files in the sidebar.

Changes

Cohort / File(s) Summary
Canvas view component
apps/obsidian/src/components/canvas/TldrawViewComponent.tsx
Refactored click-open behavior to use keyboard modifiers: Shift+Click opens in sidebar via openFileInSidebar, Cmd/Ctrl+Click opens in new tab via openFileInNewTab. Replaced direct workspace leaf manipulation with helper function calls.
Canvas node shape
apps/obsidian/src/components/canvas/shapes/DiscourseNodeShape.tsx
Added isHovered state tracking, container styling update (added relative), and hover-triggered "Open in sidebar" button with SVG icon. Button click invokes handleOpenInSidebar with error handling for missing files.
File-opening utilities
apps/obsidian/src/components/canvas/utils/openFileUtils.ts
New module exporting openFileInSidebar (opens file in right split or vertical split leaf) and openFileInNewTab (opens file in new tab leaf); both set the leaf active after opening.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant TldrawView as TldrawViewComponent
    participant Utils as openFileUtils
    participant Obsidian as Obsidian App

    User->>TldrawView: Click with Shift key
    activate TldrawView
    TldrawView->>TldrawView: Debounce & compute openInNewTab = false
    TldrawView->>Utils: openFileInSidebar(app, file)
    activate Utils
    Utils->>Obsidian: Get right leaf / vertical split
    Utils->>Obsidian: Open file in leaf
    Utils->>Obsidian: Set leaf active
    Utils-->>TldrawView: Promise resolved
    deactivate Utils
    deactivate TldrawView

    User->>TldrawView: Click with Cmd/Ctrl key
    activate TldrawView
    TldrawView->>TldrawView: Debounce & compute openInNewTab = true
    TldrawView->>Utils: openFileInNewTab(app, file)
    activate Utils
    Utils->>Obsidian: Get tab leaf
    Utils->>Obsidian: Open file in leaf
    Utils->>Obsidian: Set leaf active
    Utils-->>TldrawView: Promise resolved
    deactivate Utils
    deactivate TldrawView
Loading
sequenceDiagram
    actor User
    participant NodeShape as DiscourseNodeShape
    participant Utils as openFileUtils
    participant Toast as Toast Feedback

    User->>NodeShape: Hover over shape
    activate NodeShape
    NodeShape->>NodeShape: Set isHovered = true
    NodeShape->>NodeShape: Render "Open in sidebar" button
    deactivate NodeShape

    User->>NodeShape: Click "Open in sidebar" button
    activate NodeShape
    NodeShape->>NodeShape: Call handleOpenInSidebar()
    alt src or cache exists
        NodeShape->>Utils: openFileInSidebar(app, file)
        activate Utils
        Utils-->>NodeShape: File opened
        deactivate Utils
        NodeShape->>Toast: Show success message
    else error
        NodeShape->>Toast: Show error message
    end
    deactivate NodeShape
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Utility extraction: New openFileUtils.ts module introduces two promise-returning functions that interact with Obsidian's leaf and split APIs; requires verification of correct leaf retrieval and activation logic.
  • Keyboard modifier routing: Click handler logic in TldrawViewComponent now branches based on modifier keys; verify debounce timing and that error handling paths remain intact.
  • Hover state management: DiscourseNodeShape introduces useValue hook for hover detection and new button rendering; check that hover state doesn't interfere with existing node interactions and that SVG icon renders correctly.

Possibly related PRs

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "[ENG-968] Obsidian - Open node functionalities" is directly related to the main changes in the changeset. The modifications across three files consistently implement new functionality to open canvas nodes in different ways: a new sidebar button on hover for DiscourseNodeShape, updated click-handling behavior (Shift+Click for sidebar, Cmd/Ctrl+Click for new tab) in TldrawViewComponent, and new utility functions to support these open actions. The title accurately captures the primary objective of adding node opening capabilities without attempting to enumerate all implementation details, which aligns with expectations for PR titles.

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

🧹 Nitpick comments (3)
apps/obsidian/src/components/canvas/shapes/DiscourseNodeShape.tsx (3)

129-135: Consider a more descriptive debug label.

The first parameter of useValue is a debug label. While "is hovered" is clear, it could be more specific to this shape, such as discourse-node-${shape.id}-hovered for easier debugging when multiple shapes are tracked.


240-278: Consider using Tailwind utilities for all styling.

The button mixes inline styles with Tailwind classes, which reduces consistency and makes the styles harder to maintain. Consider moving inline styles to Tailwind utilities or a CSS module.

Example refactor using Tailwind utilities:

-            className="absolute left-1 top-1 z-10 flex items-center justify-center rounded bg-white/90 p-1 shadow-sm transition-all duration-200 hover:bg-white"
-            style={{
-              width: "24px",
-              height: "24px",
-              border: "1px solid rgba(0, 0, 0, 0.1)",
-              pointerEvents: "auto",
-              cursor: "pointer",
-            }}
+            className="absolute left-1 top-1 z-10 flex h-6 w-6 cursor-pointer items-center justify-center rounded border border-black/10 bg-white/90 p-1 shadow-sm transition-all duration-200 hover:bg-white"
+            style={{ pointerEvents: "auto" }}
             title="Open in sidebar"

Note: Keep pointerEvents: "auto" in inline styles if it's needed to override tldraw's canvas event handling.


263-276: Consider extracting the SVG icon to a constant.

The inline SVG could be extracted to the ~/icons module (similar to WHITE_LOGO_SVG) for better reusability and maintainability.

Example:

In ~/icons.ts or similar:

export const SIDEBAR_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><line x1="15" y1="3" x2="15" y2="21"/></svg>`;

Then use dangerouslySetInnerHTML or a proper SVG component.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d803704 and 6ddb1bf.

📒 Files selected for processing (3)
  • apps/obsidian/src/components/canvas/TldrawViewComponent.tsx (3 hunks)
  • apps/obsidian/src/components/canvas/shapes/DiscourseNodeShape.tsx (4 hunks)
  • apps/obsidian/src/components/canvas/utils/openFileUtils.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/main.mdc)

**/*.{ts,tsx}: Prefer type over interface
Use explicit return types for functions
Avoid any types when possible

Files:

  • apps/obsidian/src/components/canvas/utils/openFileUtils.ts
  • apps/obsidian/src/components/canvas/shapes/DiscourseNodeShape.tsx
  • apps/obsidian/src/components/canvas/TldrawViewComponent.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/obsidian/src/components/canvas/utils/openFileUtils.ts
  • apps/obsidian/src/components/canvas/shapes/DiscourseNodeShape.tsx
  • apps/obsidian/src/components/canvas/TldrawViewComponent.tsx
apps/obsidian/**

📄 CodeRabbit inference engine (.cursor/rules/obsidian.mdc)

Use the Obsidian style guide from help.obsidian.md/style-guide and docs.obsidian.md/Developer+policies for all code in the Obsidian plugin

Files:

  • apps/obsidian/src/components/canvas/utils/openFileUtils.ts
  • apps/obsidian/src/components/canvas/shapes/DiscourseNodeShape.tsx
  • apps/obsidian/src/components/canvas/TldrawViewComponent.tsx
🔇 Additional comments (3)
apps/obsidian/src/components/canvas/TldrawViewComponent.tsx (1)

203-281: LGTM! Clean refactoring of file-opening behavior.

The changes successfully extract file-opening logic into utility functions and add support for modifier keys:

  • Shift+Click opens in sidebar
  • Cmd/Ctrl+Click (metaKey) opens in new tab

The debounce logic, error handling, and user feedback are all preserved. When both modifiers are pressed, metaKey takes precedence, which is reasonable behavior.

apps/obsidian/src/components/canvas/utils/openFileUtils.ts (2)

3-18: Verify the fallback behavior for empty right sidebar.

The current logic uses getRightLeaf(false), which returns null if the right sidebar has no leaves. In that case, it falls back to creating a vertical split (line 14), which may not be in the right sidebar.

Consider whether the intended behavior is:

  1. Always open in the right sidebar (even if empty) → use getRightLeaf(true) or check rightSplit existence
  2. Only use right sidebar if it already has leaves → current implementation is correct

If the goal is to always use the right sidebar when available:

 export const openFileInSidebar = async (app: App, file: TFile): Promise<void> => {
   const rightSplit = app.workspace.rightSplit;
-  const rightLeaf = app.workspace.getRightLeaf(false);
+  const rightLeaf = app.workspace.getRightLeaf(true);
 
   if (rightLeaf) {
     if (rightSplit && rightSplit.collapsed) {
       rightSplit.expand();
     }
     await rightLeaf.openFile(file);
     app.workspace.setActiveLeaf(rightLeaf);
-  } else {
-    const leaf = app.workspace.getLeaf("split", "vertical");
-    await leaf.openFile(file);
-    app.workspace.setActiveLeaf(leaf);
   }
 };

Alternatively, verify the current behavior matches the expected UX through testing.


20-24: LGTM! Clean implementation.

The function correctly opens a file in a new tab and activates it. The explicit return type and arrow function follow the coding guidelines.

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.

I couldn't get Ctrl click to open in a new tab, I think it might have to do with the onClick being called to open in sidebar.

<button
onClick={(e) => {
e.stopPropagation();
void handleOpenInSidebar();
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like we might be calling handleOpenInSidebar() in two places? Here an onMount pointer_up?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

you have to do Cmd + Click to open in a new tab to open file in a new tab

Copy link
Collaborator

Choose a reason for hiding this comment

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

  • cmd+click works for me it opens up a new tab
  • Button click opens up in sidebar

From my investigation it is the case that both places are handling different cases, in onMount we are handling shift and cmd click on the node shape and here we are handling the button click

@trangdoan982 trangdoan982 force-pushed the eng-968-obsidian-open-node-button-in-sidebar-for-canvas-nodes branch from 1afe53b to 3f65cb2 Compare November 3, 2025 21:47
@trangdoan982 trangdoan982 requested a review from sid597 November 3, 2025 21:53
@trangdoan982
Copy link
Collaborator Author

@sid597 can you help me review this PR since Michael is away this week?

@sid597
Copy link
Collaborator

sid597 commented Nov 5, 2025

@trangdoan982 Yeah I will review it, first need to figure out how to setup and test dg plugin in obsidian

Copy link
Collaborator

@sid597 sid597 left a comment

Choose a reason for hiding this comment

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

Works for me and LGTM!

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.

👍

@trangdoan982 trangdoan982 merged commit 0b7966f into main Nov 18, 2025
4 checks passed
@github-project-automation github-project-automation bot moved this to Done in General Nov 18, 2025
@trangdoan982 trangdoan982 deleted the eng-968-obsidian-open-node-button-in-sidebar-for-canvas-nodes branch November 18, 2025 01:31
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.

4 participants