Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9228b94
[feat] TransformPane - Viewport synchronization layer for Vue nodes (…
christian-byrne Aug 6, 2025
b8ca5d5
Update locales [skip ci]
invalid-email-address Aug 7, 2025
39d6f5c
Update locales [skip ci]
invalid-email-address Aug 8, 2025
3a29a20
Add vue node feature flag (#4927)
benceruleanlu Aug 12, 2025
6f07f6d
feat: Implement CRDT-based layout system for Vue nodes (#4959)
christian-byrne Aug 17, 2025
7dbbb75
[chore] Extract link rendering out of LGraphCanvas (#4994)
benceruleanlu Aug 18, 2025
b7b9bb3
feat: Add slot registration and spatial indexing for hit detection
benceruleanlu Aug 20, 2025
8206534
Revert "feat: Add slot registration and spatial indexing for hit dete…
benceruleanlu Aug 20, 2025
3dd3ea4
feat: Add slot registration and spatial indexing for hit detection
benceruleanlu Aug 20, 2025
d7dd3a7
relocate slot update to layoutstore
benceruleanlu Aug 21, 2025
db287d1
Revert "relocate slot update to layoutstore"
benceruleanlu Aug 21, 2025
3640366
add useSlotLayoutSync
benceruleanlu Aug 21, 2025
2123795
feat: Extend Layout Store with CRDT support for links and reroutes
benceruleanlu Aug 21, 2025
2a2e623
Scuffed diff, change to dirty later
benceruleanlu Aug 21, 2025
612be1d
Fix reroute move desync
benceruleanlu Aug 21, 2025
7f9f079
Terrible reroute fixes
benceruleanlu Aug 21, 2025
023af03
Use LinkId for LinkLayout
benceruleanlu Aug 22, 2025
5a8d41d
refactor: Remove unused duplicate layout type files
benceruleanlu Aug 22, 2025
a58162e
refactor: Extract layout source strings into LayoutSource enum
benceruleanlu Aug 22, 2025
afddeaa
refactor: Unify CRDT layout operations under type-safe entity bases
benceruleanlu Aug 22, 2025
9d5f983
Fix initial link seeding
benceruleanlu Aug 22, 2025
c11dcd2
fix: Fix reroute hit detection and type consistency issues
benceruleanlu Aug 22, 2025
c20e2e6
Add debug logs
benceruleanlu Aug 23, 2025
ec9620d
Add missing reroute path
benceruleanlu Aug 23, 2025
736fd35
cleanup
benceruleanlu Aug 25, 2025
77b7d05
feat: Implement event-driven link layout sync
benceruleanlu Aug 25, 2025
a5fc834
feat: Implement DOM-based slot registration with unified position system
benceruleanlu Aug 27, 2025
3b5a3d7
Remove unused files
benceruleanlu Aug 29, 2025
7d7e967
Remove duplicated markdown file
benceruleanlu Aug 29, 2025
350faad
Remove duplicated files and address knip concerns
benceruleanlu Aug 29, 2025
745d8d7
Merge branch 'vue-nodes-migration' into bl-move-hit-detection-v2
benceruleanlu Sep 1, 2025
6aac753
Remove outdated test
benceruleanlu Sep 1, 2025
5771e5d
warning comment
benceruleanlu Sep 1, 2025
a5471aa
Update test snapshots
benceruleanlu Sep 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add debug logs
  • Loading branch information
benceruleanlu committed Aug 29, 2025
commit c20e2e6b5e109d33a645304d6502c98b68cffa87
22 changes: 22 additions & 0 deletions src/renderer/core/layout/operations/LayoutMutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* Provides a clean API for layout operations that are CRDT-ready.
* Operations are synchronous and applied directly to the store.
*/
import log from 'loglevel'

import { layoutStore } from '@/renderer/core/layout/store/LayoutStore'
import {
type LayoutMutations,
Expand All @@ -14,6 +16,8 @@ import {
type Size
} from '@/renderer/core/layout/types'

const logger = log.getLogger('LayoutMutations')

class LayoutMutationsImpl implements LayoutMutations {
/**
* Set the current mutation source
Expand Down Expand Up @@ -161,6 +165,11 @@ class LayoutMutationsImpl implements LayoutMutations {
targetNodeId: string,
targetSlot: number
): void {
logger.debug('Creating link:', {
linkId: Number(linkId),
from: `${sourceNodeId}[${sourceSlot}]`,
to: `${targetNodeId}[${targetSlot}]`
})
layoutStore.applyOperation({
type: 'createLink',
entity: 'link',
Expand All @@ -179,6 +188,7 @@ class LayoutMutationsImpl implements LayoutMutations {
* Delete a link
*/
deleteLink(linkId: string | number): void {
logger.debug('Deleting link:', Number(linkId))
layoutStore.applyOperation({
type: 'deleteLink',
entity: 'link',
Expand All @@ -198,6 +208,12 @@ class LayoutMutationsImpl implements LayoutMutations {
parentId?: string | number,
linkIds: (string | number)[] = []
): void {
logger.debug('Creating reroute:', {
rerouteId: Number(rerouteId),
position,
parentId: parentId != null ? Number(parentId) : undefined,
linkCount: linkIds.length
})
layoutStore.applyOperation({
type: 'createReroute',
entity: 'reroute',
Expand All @@ -215,6 +231,7 @@ class LayoutMutationsImpl implements LayoutMutations {
* Delete a reroute
*/
deleteReroute(rerouteId: string | number): void {
logger.debug('Deleting reroute:', Number(rerouteId))
layoutStore.applyOperation({
type: 'deleteReroute',
entity: 'reroute',
Expand All @@ -233,6 +250,11 @@ class LayoutMutationsImpl implements LayoutMutations {
position: Point,
previousPosition: Point
): void {
logger.debug('Moving reroute:', {
rerouteId: Number(rerouteId),
from: previousPosition,
to: position
})
layoutStore.applyOperation({
type: 'moveReroute',
entity: 'reroute',
Expand Down
54 changes: 54 additions & 0 deletions src/renderer/core/layout/store/LayoutStore.ts
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to delete things like slots in the handleDeleteNode?

handleDeleteNode(nodeId: string) {
  this.ynodes.delete(nodeId)

  // MISSING: Clean up associated slots
  const nodeSlots = this.slotSpatialIndex.query(/* node bounds */)
  nodeSlots.forEach(slot => {
    if (slot.nodeId === nodeId) {
      this.slotSpatialIndex.remove(slot)
      this.yslots.delete(slot.id)
    }
  })
}

Copy link
Member Author

Choose a reason for hiding this comment

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

which handleDeleteNode?

this is the verbatim for LayoutStore that I see:

  private handleDeleteNode(
    operation: DeleteNodeOperation,
    change: LayoutChange
  ): void {
    if (!this.ynodes.has(operation.nodeId)) return

    this.ynodes.delete(operation.nodeId)
    this.nodeRefs.delete(operation.nodeId)
    this.nodeTriggers.delete(operation.nodeId)

    // Remove from spatial index
    this.spatialIndex.remove(operation.nodeId)

    // Clean up associated slot layouts
    this.deleteNodeSlotLayouts(operation.nodeId)

    change.type = 'delete'
    change.nodeIds.push(operation.nodeId)
  }

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Uses Yjs for efficient local state management and future collaboration.
* CRDT ensures conflict-free operations for both single and multi-user scenarios.
*/
import log from 'loglevel'
import { type ComputedRef, type Ref, computed, customRef } from 'vue'
import * as Y from 'yjs'

Expand Down Expand Up @@ -38,6 +39,8 @@ import {
} from '@/renderer/core/layout/types'
import { SpatialIndexManager } from '@/renderer/core/spatial/SpatialIndex'

const logger = log.getLogger('LayoutStore')

class LayoutStoreImpl implements LayoutStore {
// Yjs document and shared data structures
private ydoc = new Y.Doc()
Expand Down Expand Up @@ -384,6 +387,15 @@ class LayoutStoreImpl implements LayoutStore {
updateSlotLayout(key: string, layout: SlotLayout): void {
const existing = this.slotLayouts.get(key)

if (!existing) {
logger.debug('Adding slot:', {
nodeId: layout.nodeId,
type: layout.type,
index: layout.index,
bounds: layout.bounds
})
}

if (existing) {
// Update spatial index
this.slotSpatialIndex.update(key, layout.bounds)
Expand Down Expand Up @@ -429,6 +441,14 @@ class LayoutStoreImpl implements LayoutStore {
updateRerouteLayout(rerouteId: RerouteId, layout: RerouteLayout): void {
const existing = this.rerouteLayouts.get(rerouteId)

if (!existing) {
logger.debug('Adding reroute layout:', {
rerouteId,
position: layout.position,
bounds: layout.bounds
})
}

if (existing) {
// Update spatial index
this.rerouteSpatialIndex.update(String(rerouteId), layout.bounds) // Spatial index uses strings
Expand Down Expand Up @@ -498,6 +518,15 @@ class LayoutStoreImpl implements LayoutStore {
}

const existing = this.linkSegmentLayouts.get(key)
if (!existing) {
logger.debug('Adding link segment:', {
linkId,
rerouteId,
bounds: layout.bounds,
hasPath: !!layout.path
})
}

if (existing) {
// Update spatial index
this.linkSegmentSpatialIndex.update(key, layout.bounds)
Expand Down Expand Up @@ -543,6 +572,14 @@ class LayoutStoreImpl implements LayoutStore {
}
const candidateKeys = this.linkSegmentSpatialIndex.query(searchArea)

if (candidateKeys.length > 0) {
logger.debug('Checking link segments at point:', {
point,
candidateCount: candidateKeys.length,
tolerance: hitWidth
})
}

// Precise hit test only on candidates
for (const key of candidateKeys) {
const segmentLayout = this.linkSegmentLayouts.get(key)
Expand All @@ -559,6 +596,11 @@ class LayoutStoreImpl implements LayoutStore {
)

if (hit) {
logger.debug('Link segment hit:', {
linkId: segmentLayout.linkId,
rerouteId: segmentLayout.rerouteId,
point
})
return {
linkId: segmentLayout.linkId,
rerouteId: segmentLayout.rerouteId
Expand Down Expand Up @@ -625,6 +667,13 @@ class LayoutStoreImpl implements LayoutStore {
}
const candidateRerouteKeys = this.rerouteSpatialIndex.query(searchArea)

if (candidateRerouteKeys.length > 0) {
logger.debug('Checking reroutes at point:', {
point,
candidateCount: candidateRerouteKeys.length
})
}

// Check precise distance for candidates
for (const rerouteKey of candidateRerouteKeys) {
const rerouteId = Number(rerouteKey) as RerouteId // Convert string key back to numeric
Expand All @@ -635,6 +684,11 @@ class LayoutStoreImpl implements LayoutStore {
const distance = Math.sqrt(dx * dx + dy * dy)

if (distance <= rerouteLayout.radius) {
logger.debug('Reroute hit:', {
rerouteId: rerouteLayout.id,
position: rerouteLayout.position,
distance
})
return rerouteLayout
}
}
Expand Down