Skip to content

[ENG-1547] Relation creation via drag handle (Obsidian)#909

Open
trangdoan982 wants to merge 7 commits intomainfrom
eng-1547-implement-relation-creation-via-drag-handles-in-tldraw
Open

[ENG-1547] Relation creation via drag handle (Obsidian)#909
trangdoan982 wants to merge 7 commits intomainfrom
eng-1547-implement-relation-creation-via-drag-handles-in-tldraw

Conversation

@trangdoan982
Copy link
Copy Markdown
Member

@trangdoan982 trangdoan982 commented Mar 23, 2026

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

Summary

  • Clicking an existing persisted relation arrow now shows the relation type dropdown at the arrow midpoint
  • Selecting a new type updates the arrow visuals (color, label) and persists the change to relations.json in-place (preserving id, created, author, etc.)
  • Added updateRelationType() helper to relationsStore.ts
  • Handles edge cases: same-type re-selection dismisses without changes, Escape/click-outside closes without modifying, dropdown doesn't re-open after creation or edit flows

Test plan

  • Create a relation via drag handles — existing flow still works, dropdown closes after selection
  • Select a new type — arrow updates (color, label) and relations.json type field updated in-place
  • Escape/click outside — dropdown closes, arrow disappear
  • Relation Overlay updated correctly after relation is created
image

🤖 Generated with Claude Code


Open with Devin

@linear
Copy link
Copy Markdown

linear bot commented Mar 23, 2026

@supabase
Copy link
Copy Markdown

supabase bot commented Mar 23, 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 ↗︎.

devin-ai-integration[bot]

This comment was marked as resolved.

@trangdoan982 trangdoan982 force-pushed the eng-1547-implement-relation-creation-via-drag-handles-in-tldraw branch from 75c8668 to 0ebe9ce Compare March 26, 2026 03:47
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
devin-ai-integration[bot]

This comment was marked as resolved.

graphite-app[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@trangdoan982 trangdoan982 changed the title [ENG-1547] Add relation type editing via click on existing arrows [ENG-1547] Relation creation via drag handle (Obsidian) Mar 27, 2026
@trangdoan982 trangdoan982 requested a review from mdroidian March 27, 2026 19:43
Copy link
Copy Markdown
Member

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

  1. CI is failing.

  2. This PR is ~50% over the line change limit. I understand this might be required (or ideal) in this case, that being said, would you mind creating a Loom video walking through this code and code choices?

The biggest concern I have doing a quick review was the code possibly not being as DRY as possible. I worry we might be duplicating work, specifically in the relation/arrow drag handling logic and potential relations checking.

onClick={(e) => e.stopPropagation()}
>
<div
style={{
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

tailwind?

>
<div
style={{
padding: "4px 8px",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

tailwind?

key={rt.id}
onClick={() => handleSelect(rt.id)}
style={{
display: "flex",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

tailwind?

>
<span
style={{
width: "8px",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

tailwind?


for (const relationType of plugin.settings.relationTypes) {
// Check if there's a discourse relation that matches this pair
const isValid = plugin.settings.discourseRelations.some(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are we doing this anywhere else in the plugin? Could we DRY this?

}, [arrow, onDismiss]);

// Get valid relation types based on source/target node types
const validRelationTypes = useMemo(() => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are we doing this anywhere else in the plugin? Could we DRY this?

const dy = point.y - currentShape.y;

// Check for a target shape under the cursor
const target = editor.getShapeAtPoint(point, {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this work being duplicated in the relationShape/relationUtil?

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Devin Review found 3 new potential issues.

View 13 additional findings in Devin Review.

Open in Devin Review

Comment on lines +14 to +19
export const getRelationDirection = (
discourseRelations: DiscourseRelation[],
relationTypeId: string,
sourceNodeTypeId: string,
targetNodeTypeId: string,
): { direct: boolean; reverse: boolean } => {
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.

🔴 AGENTS.md violation: getRelationDirection has 4 positional params instead of using object destructuring

The AGENTS.md style guide mandates: "Use named parameters (object destructuring) when a function has more than 2 parameters." getRelationDirection takes 4 positional parameters (discourseRelations, relationTypeId, sourceNodeTypeId, targetNodeTypeId) without using object destructuring.

Prompt for agents
In apps/obsidian/src/components/canvas/utils/relationTypeUtils.ts, refactor getRelationDirection (lines 14-41) to use object destructuring for its parameters. Change the signature from:

export const getRelationDirection = (
  discourseRelations: DiscourseRelation[],
  relationTypeId: string,
  sourceNodeTypeId: string,
  targetNodeTypeId: string,
): { direct: boolean; reverse: boolean } => {

to:

export const getRelationDirection = ({
  discourseRelations,
  relationTypeId,
  sourceNodeTypeId,
  targetNodeTypeId,
}: {
  discourseRelations: DiscourseRelation[];
  relationTypeId: string;
  sourceNodeTypeId: string;
  targetNodeTypeId: string;
}): { direct: boolean; reverse: boolean } => {

Then update all call sites:
- apps/obsidian/src/components/canvas/utils/relationTypeUtils.ts lines 55-60 (getValidRelationTypesForNodePair)
- apps/obsidian/src/components/canvas/shapes/DiscourseRelationShape.tsx lines 1102-1107 (updateRelationTextForDirection)
- apps/obsidian/src/components/canvas/shapes/DiscourseRelationShape.tsx lines 1137-1142 (isValidNodeConnection)
Open in Devin Review

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

Comment on lines +47 to +51
export const getValidRelationTypesForNodePair = (
settings: RelationTypeSettings,
sourceNodeTypeId: string,
targetNodeTypeId: string,
): { id: string; label: string; color: string }[] => {
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.

🔴 AGENTS.md violation: getValidRelationTypesForNodePair has 3 positional params instead of using object destructuring

The AGENTS.md style guide mandates: "Use named parameters (object destructuring) when a function has more than 2 parameters." getValidRelationTypesForNodePair takes 3 positional parameters (settings, sourceNodeTypeId, targetNodeTypeId) without using object destructuring.

Prompt for agents
In apps/obsidian/src/components/canvas/utils/relationTypeUtils.ts, refactor getValidRelationTypesForNodePair (lines 47-72) to use object destructuring. Change the signature from positional parameters to a single destructured object parameter. Then update the call site at apps/obsidian/src/components/canvas/overlays/RelationTypeDropdown.tsx lines 59-63.
Open in Devin Review

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

Comment on lines +77 to +80
export const hasValidRelationTypeForNodePair = (
settings: RelationTypeSettings,
sourceNodeTypeId: string,
targetNodeTypeId: string,
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.

🔴 AGENTS.md violation: hasValidRelationTypeForNodePair has 3 positional params instead of using object destructuring

The AGENTS.md style guide mandates: "Use named parameters (object destructuring) when a function has more than 2 parameters." hasValidRelationTypeForNodePair takes 3 positional parameters (settings, sourceNodeTypeId, targetNodeTypeId) without using object destructuring.

Prompt for agents
In apps/obsidian/src/components/canvas/utils/relationTypeUtils.ts, refactor hasValidRelationTypeForNodePair (lines 77-90) to use object destructuring. Change the signature from positional parameters to a single destructured object parameter. Then update the call site at apps/obsidian/src/components/canvas/overlays/DragHandleOverlay.tsx lines 299-303.
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.

2 participants