Skip to content

feat: Direct links to Relationship Views#2547

Merged
davydkov merged 1 commit intolikec4:mainfrom
ckeller42:feat/relationship-view-linking
Jan 14, 2026
Merged

feat: Direct links to Relationship Views#2547
davydkov merged 1 commit intolikec4:mainfrom
ckeller42:feat/relationship-view-linking

Conversation

@ckeller42
Copy link
Copy Markdown
Collaborator

@ckeller42 ckeller42 commented Jan 13, 2026

  • Added support for deep linking directly to relationship views via URL parameter.
  • Added ?relationships={elementFqn} URL parameter to automatically open the Relationship Browser for a specific element on page load.
  • Added "Copy Link" button to the Relationship Browser to easily share these direct views.
image

Checklist

  • I've thoroughly read the latest contribution guidelines.
  • I've rebased my branch onto main before creating this PR.
  • My commit messages follow conventional spec
  • [NA ] I've added tests to cover my changes (if applicable).
  • I've verified that all new and existing tests have passed locally for mobile, tablet, and desktop screen sizes.
  • [NA] My change requires documentation updates.
  • [NA ] I've updated the documentation accordingly.

Summary by CodeRabbit

  • New Features
    • Added copy-to-clipboard functionality to create and share direct links to relationship views with visual confirmation feedback
    • Relationship views can now be accessed and shared via URL parameters, enabling users to send teammates direct links to specific relationship perspectives

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

@ckeller42 ckeller42 requested a review from davydkov January 13, 2026 14:02
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Jan 13, 2026

⚠️ No Changeset found

Latest commit: 94a6697

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Copy Markdown
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: 0

🧹 Nitpick comments (2)
packages/likec4/app/src/pages/ViewReact.tsx (1)

90-123: Consider edge case: relationships param changes after initialization.

The current implementation only processes the URL parameter when the initialized event fires. If a user navigates to a URL with a different relationships param after the diagram is already initialized, the browser won't open because the initialized event won't fire again.

This may be intentional (URL param only works on initial load), but if the feature should support navigation to relationship links while the diagram is already open, consider also checking for param changes via a useEffect.

💡 Potential enhancement (if supporting post-init navigation is desired)
+  // Handle initial load via initialized event
   useOnDiagramEvent('initialized', () => {
-    if (!relationships) {
-      return
-    }
-
-    // Prevent re-processing the same FQN
-    if (processedRef.current === relationships) {
-      return
-    }
-    processedRef.current = relationships
-
-    diagram.openRelationshipsBrowser(relationships)
-
-    // Clear the relationships parameter from URL after opening
-    void router.buildAndCommitLocation({
-      search: (current: Record<string, unknown>) => {
-        const { relationships: _, ...rest } = current
-        return rest
-      },
-      viewTransition: false,
-    })
+    processRelationshipsParam()
   })
+
+  const processRelationshipsParam = () => {
+    if (!relationships || processedRef.current === relationships) {
+      return
+    }
+    processedRef.current = relationships
+    diagram.openRelationshipsBrowser(relationships)
+    void router.buildAndCommitLocation({
+      search: (current: Record<string, unknown>) => {
+        const { relationships: _, ...rest } = current
+        return rest
+      },
+      viewTransition: false,
+    })
+  }
packages/likec4/app/src/routes/__root.tsx (1)

41-46: Consider validating FQN format for URL search parameters.

The asFqn function accepts any non-empty string without validating the FQN format. FQNs are dot-separated identifiers (e.g., cloud.aws.s3), where each segment must follow the identifier pattern /[_]*[a-zA-Z][-\w]*/. Adding basic format validation here would catch malformed FQNs earlier in the flow before they reach the relationship browser.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3def890 and f94f11c.

📒 Files selected for processing (4)
  • packages/diagram/src/overlays/relationships-browser/RelationshipsBrowser.tsx
  • packages/likec4/app/src/pages/ViewEditor.tsx
  • packages/likec4/app/src/pages/ViewReact.tsx
  • packages/likec4/app/src/routes/__root.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx,json,yaml,yml}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use pnpm fmt with dprint for code formatting; do not use Prettier or eslint for formatting

Files:

  • packages/likec4/app/src/routes/__root.tsx
  • packages/diagram/src/overlays/relationships-browser/RelationshipsBrowser.tsx
  • packages/likec4/app/src/pages/ViewEditor.tsx
  • packages/likec4/app/src/pages/ViewReact.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use oxlint with type-aware rules for linting; run pnpm lint for checking and pnpm lint:fix for auto-fixing

Files:

  • packages/likec4/app/src/routes/__root.tsx
  • packages/diagram/src/overlays/relationships-browser/RelationshipsBrowser.tsx
  • packages/likec4/app/src/pages/ViewEditor.tsx
  • packages/likec4/app/src/pages/ViewReact.tsx
🧬 Code graph analysis (2)
packages/likec4/app/src/pages/ViewEditor.tsx (1)
packages/likec4/app/src/pages/ViewReact.tsx (1)
  • OpenRelationshipBrowserFromUrl (90-123)
packages/likec4/app/src/pages/ViewReact.tsx (4)
packages/diagram/src/index.ts (3)
  • LikeC4Diagram (4-4)
  • useDiagram (44-44)
  • useOnDiagramEvent (46-46)
packages/diagram/src/hooks/useDiagram.ts (2)
  • useDiagram (14-14)
  • useOnDiagramEvent (51-77)
packages/diagram/src/search/hooks.ts (1)
  • useSearch (9-16)
packages/core/src/model/LikeC4Model.ts (1)
  • relationships (431-433)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: checks / ʦ typescript
  • GitHub Check: checks / 🛠️ build
  • GitHub Check: checks / 🔬 tests
  • GitHub Check: checks / ⊞ windows build
🔇 Additional comments (12)
packages/diagram/src/overlays/relationships-browser/RelationshipsBrowser.tsx (3)

3-4: LGTM!

The new imports for clipboard functionality and icons are appropriate for the CopyLinkButton feature.


203-214: LGTM!

Good organization wrapping both buttons in a Group with consistent gap spacing. The close button retains its original behavior.


286-327: The hash detection using currentUrl.hash.startsWith('#/') is actually appropriate for this codebase. The app uses TanStack Router with optional hash-based routing (e.g., /#/view), and this pattern correctly identifies when the router is in hash mode. Non-route anchors like #section-anchor won't match this pattern and will correctly fall through to the standard browser history branch. The implementation already handles the edge case mentioned and doesn't require additional robustness checks.

packages/likec4/app/src/pages/ViewReact.tsx (3)

1-12: LGTM!

Imports are properly updated to support the new OpenRelationshipBrowserFromUrl functionality.


81-81: LGTM!

Component is correctly placed inside the LikeC4Diagram context where it has access to diagram hooks.


133-133: LGTM!

The explicit Record<string, unknown> type annotation improves type safety for the search callback parameter.

packages/likec4/app/src/pages/ViewEditor.tsx (2)

8-8: LGTM!

Good reuse of the OpenRelationshipBrowserFromUrl component from ViewReact.tsx, ensuring consistent behavior between the view and editor pages.


85-85: LGTM!

Component is correctly placed inside the LikeC4Diagram context, maintaining consistency with ViewReact.tsx.

packages/likec4/app/src/routes/__root.tsx (4)

1-1: LGTM!

The Fqn type import is appropriately added to support the new relationships parameter.


48-53: LGTM!

The SearchParams type is properly extended with the relationships parameter. Adding explicit | undefined to existing optional properties improves type clarity and consistency.


80-82: LGTM!

The validation logic correctly applies asFqn when a relationships value is present, consistent with the pattern used for other search parameters.


91-91: LGTM!

Setting relationships: undefined as the default strip value ensures the parameter is removed from the URL when not needed, keeping URLs clean.

@ckeller42 ckeller42 marked this pull request as draft January 13, 2026 14:15
@ckeller42 ckeller42 force-pushed the feat/relationship-view-linking branch 2 times, most recently from a7c3a6f to ca44f38 Compare January 13, 2026 14:24
 - Added support for deep linking directly to relationship views via URL parameter.
 - Added ?relationships={elementFqn} URL parameter to automatically open the Relationship Browser for a specific element on page load.
 - Added "Copy Link" button to the Relationship Browser to easily share these direct views.
@ckeller42 ckeller42 force-pushed the feat/relationship-view-linking branch from ca44f38 to 94a6697 Compare January 13, 2026 16:54
@likec4 likec4 deleted a comment from coderabbitai bot Jan 13, 2026
@ckeller42
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 13, 2026

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 13, 2026

📝 Walkthrough

Walkthrough

A URL-shareable relationships browser feature is introduced. Users can copy shareable links to specific relationship views via a new CopyLinkButton component. The application routes now support a relationships URL parameter, and ViewReact detects and opens the relationships browser when this parameter is present.

Changes

Cohort / File(s) Summary
Relationships Browser UI
packages/diagram/src/overlays/relationships-browser/RelationshipsBrowser.tsx
Adds CopyLinkButton component with URL builder (buildRelationshipUrl) supporting hash-based and history-style routing. Implements clipboard handling via useClipboard with visual feedback (Link/Check/Alert icons), error state management, and custom Tooltip wrapper. Replaces standalone close button with a Group containing CopyLinkButton and close ActionIcon.
URL-based Browser Opening
packages/likec4/app/src/pages/ViewReact.tsx, packages/likec4/app/src/pages/ViewEditor.tsx
Introduces OpenRelationshipBrowserFromUrl component that detects relationships URL parameter, opens the browser, and strips the parameter from URL. Implements lifecycle guards (isMounted, isInitialized, isProcessing) to prevent race conditions. ViewEditor integrates the new component into the render tree alongside existing dynamic variant listener.
Route Configuration
packages/likec4/app/src/routes/__root.tsx
Extends SearchParams type to include relationships?: Fqn parameter. Adds internal asFqn validator for URL parameter normalization and updates Route.validateSearch to parse the relationships parameter. Initializes relationships: undefined by default in route middlewares.

Sequence Diagram

sequenceDiagram
    participant User
    participant Browser
    participant ViewReact
    participant Router
    participant RelationshipsBrowser
    participant Clipboard

    User->>Browser: Navigate with ?relationships=ElementFQN
    Browser->>Router: Parse URL search params
    Router->>ViewReact: Detect relationships param via OpenRelationshipBrowserFromUrl
    ViewReact->>ViewReact: Validate initialization state
    ViewReact->>RelationshipsBrowser: Open browser for element
    ViewReact->>Router: Clear relationships param from URL
    
    User->>RelationshipsBrowser: Click CopyLinkButton
    RelationshipsBrowser->>RelationshipsBrowser: Build shareable URL (buildRelationshipUrl)
    RelationshipsBrowser->>Clipboard: Copy URL to clipboard
    RelationshipsBrowser->>User: Show success feedback (Check icon)
    
    User->>Browser: Share copied URL
    Browser->>Router: Navigate with relationships param
    Note over ViewReact: Flow repeats for new user
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes


🐰 With whiskers twitched and button pressed,
Copy links for relationships blessed,
URLs built with care so fine,
Share the diagram with yours and mine! 🔗✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: Direct links to Relationship Views' clearly and concisely describes the main feature addition—enabling direct URL-based linking to relationship views.
Description check ✅ Passed The description is mostly complete with key feature details and checklist items marked appropriately; however, the NA marking for 'tests to cover changes' is ambiguous given the substantial feature additions across multiple files.
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.

✨ Finishing touches
  • 📝 Generate docstrings

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
Copy Markdown
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: 0

🧹 Nitpick comments (2)
packages/diagram/src/overlays/relationships-browser/RelationshipsBrowser.tsx (2)

294-303: Consider importing the shared Tooltip component to avoid duplication.

This Tooltip configuration is identical to the one in packages/diagram/src/likec4diagram/custom/nodes/toolbar/_shared.tsx (lines 9-18). Consider importing from the shared location to maintain a single source of truth.

♻️ Suggested refactor
-const Tooltip = MantineTooltip.withProps({
-  color: 'dark',
-  fz: 'xs',
-  openDelay: 400,
-  closeDelay: 150,
-  label: '',
-  children: null,
-  offset: 4,
-  withinPortal: false,
-})
+import { Tooltip } from '../../likec4diagram/custom/nodes/toolbar/_shared'

409-419: Minor: Redundant withinPortal={false} prop.

Line 410 sets withinPortal={false}, but this is already defined in the Tooltip defaults on line 302. This prop can be removed.

♻️ Suggested fix
-    <Tooltip label={buttonState.tooltip} withArrow position="top" withinPortal={false}>
+    <Tooltip label={buttonState.tooltip} withArrow position="top">
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3def890 and 94a6697.

📒 Files selected for processing (4)
  • packages/diagram/src/overlays/relationships-browser/RelationshipsBrowser.tsx
  • packages/likec4/app/src/pages/ViewEditor.tsx
  • packages/likec4/app/src/pages/ViewReact.tsx
  • packages/likec4/app/src/routes/__root.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx,json,yaml,yml}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use pnpm fmt with dprint for code formatting; do not use Prettier or eslint for formatting

Files:

  • packages/likec4/app/src/pages/ViewEditor.tsx
  • packages/diagram/src/overlays/relationships-browser/RelationshipsBrowser.tsx
  • packages/likec4/app/src/pages/ViewReact.tsx
  • packages/likec4/app/src/routes/__root.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use oxlint with type-aware rules for linting; run pnpm lint for checking and pnpm lint:fix for auto-fixing

Files:

  • packages/likec4/app/src/pages/ViewEditor.tsx
  • packages/diagram/src/overlays/relationships-browser/RelationshipsBrowser.tsx
  • packages/likec4/app/src/pages/ViewReact.tsx
  • packages/likec4/app/src/routes/__root.tsx
🧬 Code graph analysis (3)
packages/likec4/app/src/pages/ViewEditor.tsx (1)
packages/likec4/app/src/pages/ViewReact.tsx (1)
  • OpenRelationshipBrowserFromUrl (91-159)
packages/diagram/src/overlays/relationships-browser/RelationshipsBrowser.tsx (2)
packages/diagram/src/likec4diagram/custom/nodes/toolbar/_shared.tsx (1)
  • Tooltip (10-19)
e2e/bootstrap.mjs (1)
  • url (42-42)
packages/likec4/app/src/pages/ViewReact.tsx (4)
packages/diagram/src/index.ts (4)
  • LikeC4Diagram (4-4)
  • useDiagram (44-44)
  • useOnDiagramEvent (46-46)
  • useUpdateEffect (68-68)
packages/diagram/src/hooks/useDiagram.ts (2)
  • useDiagram (14-14)
  • useOnDiagramEvent (51-77)
packages/diagram/src/search/hooks.ts (1)
  • useSearch (9-16)
packages/core/src/types/_aux.ts (2)
  • Fqn (154-154)
  • Fqn (240-240)
🔇 Additional comments (8)
packages/likec4/app/src/routes/__root.tsx (2)

41-54: LGTM! Clean validator implementation.

The asFqn validator follows the established pattern of other validators in this file. Trimming and returning undefined for empty strings is appropriate for URL parameter handling.


88-90: LGTM! Consistent integration with existing search params.

The relationships parameter is integrated consistently with the existing padding, theme, and dynamic parameters using the same conditional spreading pattern and middleware configuration.

Also applies to: 99-99

packages/diagram/src/overlays/relationships-browser/RelationshipsBrowser.tsx (2)

310-326: LGTM! Robust URL building with good routing support.

The function correctly handles both hash-based (/#/view/name) and history-style routing, preserving base paths and existing search parameters. The trailing slash cleanup on line 318 is a nice touch for URL consistency.


333-420: Well-implemented clipboard functionality with proper error handling.

The CopyLinkButton component handles edge cases well:

  • Secure context check for clipboard API compatibility
  • Error state management with timeout-based clearing
  • Visual feedback for success/failure states
  • Proper cleanup of timeouts on unmount

The aria-label on line 415 is a good accessibility addition.

packages/likec4/app/src/pages/ViewReact.tsx (3)

91-159: Well-structured lifecycle management for URL-driven browser opening.

The implementation handles the initialization timing correctly:

  • Waits for diagram initialization via useOnDiagramEvent('initialized', ...)
  • Uses useUpdateEffect to handle parameter changes (skipping initial mount)
  • Guards against unmount with isMountedRef checks throughout async operations
  • Prevents duplicate processing with isProcessingRef and processedRef

The cleanup effect properly resets all refs to prevent stale state.


169-172: LGTM! Improved type safety for search callback.

Changing from implicit any to explicit Record<string, unknown> improves type safety and makes the code more maintainable.


102-125: No action needed — openRelationshipsBrowser returns void and is synchronous.

The method signature at packages/diagram/src/likec4diagram/state/diagram-api.ts:66 explicitly returns void: openRelationshipsBrowser(fqn: Fqn<A>): void. The implementation sends an event to a state machine and completes synchronously; awaiting it is neither possible nor necessary. This is the correct pattern used throughout the codebase.

Likely an incorrect or invalid review comment.

packages/likec4/app/src/pages/ViewEditor.tsx (1)

8-8: LGTM! Consistent feature integration.

The OpenRelationshipBrowserFromUrl component is correctly imported and placed alongside ListenForDynamicVariantChange, maintaining consistency with the ViewReact.tsx implementation. This ensures the deep linking feature works in both the React view and editor view.

Also applies to: 84-85

@ckeller42 ckeller42 marked this pull request as ready for review January 14, 2026 09:34
@davydkov
Copy link
Copy Markdown
Member

Good one! Thank you for contribution!

@davydkov davydkov merged commit b653661 into likec4:main Jan 14, 2026
15 of 23 checks passed
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