fix: replace Proxmox cluster handles with configurable side points
The Proxmox node had two always-on cluster-left/cluster-right handles that rendered independently of the new per-side connection-point config — so a configured side stacked on top of the forced handle (e.g. 0 config still showed 1; 2 config left the forced one in the middle). - ProxmoxGroupNode: remove the always-on cluster handles in both container and non-container modes; cluster links now use the normal per-side points - add migrateClusterHandles(nodes, edges): on load, remap any edge still bound to cluster-left/right onto the left/right slot-0 point and set that node's side count to at least 1, so existing links survive (edge 'cluster' type/style kept). Applied on API load, standalone load, LiveView, and YAML import - App: new cluster links are made by choosing the Cluster edge type in the edge modal (drop the cluster-handle auto-default) ha-relevant: yes
This commit is contained in:
@@ -27,7 +27,7 @@ import { useThemeStore } from '@/stores/themeStore'
|
||||
import { THEMES } from '@/utils/themes'
|
||||
import { nodeTypes } from '@/components/canvas/nodes/nodeTypes'
|
||||
import { edgeTypes } from '@/components/canvas/edges/edgeTypes'
|
||||
import { deserializeApiNode, deserializeApiEdge, type ApiNode, type ApiEdge } from '@/utils/canvasSerializer'
|
||||
import { deserializeApiNode, deserializeApiEdge, migrateClusterHandles, type ApiNode, type ApiEdge } from '@/utils/canvasSerializer'
|
||||
import { computeCollapseInfo, rewireEdgesForCollapse } from '@/utils/collapseFilter'
|
||||
import { liveviewApi } from '@/api/client'
|
||||
import * as standaloneStorage from '@/utils/standaloneStorage'
|
||||
@@ -88,10 +88,11 @@ function LiveViewCanvas() {
|
||||
const savedTheme = res.data.viewport?.theme_id
|
||||
if (savedTheme) setTheme(savedTheme)
|
||||
if (res.data.custom_style) setCustomStyle(res.data.custom_style as CustomStyleDef)
|
||||
loadCanvas(
|
||||
const migrated = migrateClusterHandles(
|
||||
(apiNodes as ApiNode[]).map((n) => deserializeApiNode(n, proxmoxMap)),
|
||||
(apiEdges as ApiEdge[]).map(deserializeApiEdge),
|
||||
)
|
||||
loadCanvas(migrated.nodes, migrated.edges)
|
||||
setViewState('ready')
|
||||
})
|
||||
.catch((err) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createElement, useEffect } from 'react'
|
||||
import { Handle, Position, NodeResizer, useUpdateNodeInternals, type NodeProps, type Node } from '@xyflow/react'
|
||||
import { NodeResizer, useUpdateNodeInternals, type NodeProps, type Node } from '@xyflow/react'
|
||||
import { Layers } from 'lucide-react'
|
||||
import type { NodeData } from '@/types'
|
||||
import { resolveNodeColors } from '@/utils/nodeColors'
|
||||
@@ -23,34 +23,15 @@ export function ProxmoxGroupNode(props: NodeProps<Node<NodeData>>) {
|
||||
const theme = THEMES[activeTheme]
|
||||
const colors = resolveNodeColors(data, activeTheme)
|
||||
|
||||
// Render as a regular node when container mode is disabled
|
||||
// Render as a regular node when container mode is disabled. Cluster links now
|
||||
// use the configurable per-side connection points (see BaseNode / SideHandles).
|
||||
if (data.container_mode === false) {
|
||||
const proxmoxAccent = theme.colors.nodeAccents.proxmox.border
|
||||
return (
|
||||
<>
|
||||
<BaseNode {...props} icon={Layers} />
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Left}
|
||||
id="cluster-left"
|
||||
title="Same cluster"
|
||||
style={{ background: proxmoxAccent, borderColor: `${proxmoxAccent}88`, width: 6, height: 6 }}
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Right}
|
||||
id="cluster-right"
|
||||
title="Same cluster"
|
||||
style={{ background: proxmoxAccent, borderColor: `${proxmoxAccent}88`, width: 6, height: 6 }}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
return <BaseNode {...props} icon={Layers} />
|
||||
}
|
||||
|
||||
const statusColor = theme.colors.statusColors[data.status]
|
||||
const isOnline = data.status === 'online'
|
||||
const glow = colors.border
|
||||
const proxmoxAccent = theme.colors.nodeAccents.proxmox.border
|
||||
const resolvedIcon = resolveNodeIcon(Layers, data.custom_icon)
|
||||
|
||||
return (
|
||||
@@ -151,22 +132,6 @@ export function ProxmoxGroupNode(props: NodeProps<Node<NodeData>>) {
|
||||
handleBorder={theme.colors.handleBorder}
|
||||
labelColor={theme.colors.nodeSubtextColor}
|
||||
/>
|
||||
|
||||
{/* Cluster handles */}
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Left}
|
||||
id="cluster-left"
|
||||
title="Same cluster"
|
||||
style={{ background: proxmoxAccent, borderColor: `${proxmoxAccent}88`, width: 6, height: 6 }}
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Right}
|
||||
id="cluster-right"
|
||||
title="Same cluster"
|
||||
style={{ background: proxmoxAccent, borderColor: `${proxmoxAccent}88`, width: 6, height: 6 }}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -114,10 +114,20 @@ describe('ProxmoxGroupNode', () => {
|
||||
expect(sourceHandles.length).toBe(1)
|
||||
})
|
||||
|
||||
it('renders cluster handles in both modes', () => {
|
||||
it('no longer renders the always-on cluster handles (#243)', () => {
|
||||
const { container: groupC } = renderNode({})
|
||||
expect(groupC.querySelectorAll('[title="Same cluster"]').length).toBeGreaterThanOrEqual(2)
|
||||
expect(groupC.querySelectorAll('[title="Same cluster"]').length).toBe(0)
|
||||
const { container: nodeC } = renderNode({ container_mode: false })
|
||||
expect(nodeC.querySelectorAll('[title="Same cluster"]').length).toBeGreaterThanOrEqual(2)
|
||||
expect(nodeC.querySelectorAll('[title="Same cluster"]').length).toBe(0)
|
||||
})
|
||||
|
||||
it('renders configurable left/right handles only when counts > 0', () => {
|
||||
const { container: none } = renderNode({ container_mode: false })
|
||||
expect(none.querySelectorAll('.react-flow__handle-left.source').length).toBe(0)
|
||||
expect(none.querySelectorAll('.react-flow__handle-right.source').length).toBe(0)
|
||||
|
||||
const { container: set } = renderNode({ container_mode: false, left_handles: 1, right_handles: 2 })
|
||||
expect(set.querySelectorAll('.react-flow__handle-left.source').length).toBe(1)
|
||||
expect(set.querySelectorAll('.react-flow__handle-right.source').length).toBe(2)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user