feat: meaningful labels by status
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
import { useCanvasStore } from '@/app/canvas/canvasStore'
|
||||
import { selectConnectionStatusForEdge } from '@/app/canvas/canvasStore.selectors'
|
||||
import { CONNECTION_STATUS_CLASS } from '@/lib/graph/connectionStatus'
|
||||
import { getConnectionLabelForTargetAndStatus } from '@/lib/graph/nodeRegistry'
|
||||
|
||||
const EDGE_STROKE_WIDTH = 2
|
||||
const DOT_MARKER_R = 1.5
|
||||
@@ -31,7 +32,10 @@ function AnimatedEdgeInner({
|
||||
)
|
||||
const statusClass = CONNECTION_STATUS_CLASS[connectionStatus]
|
||||
|
||||
const label = labelProp ?? (data as { connectionLabel?: string } | undefined)?.connectionLabel
|
||||
const targetType = (data as { targetType?: string } | undefined)?.targetType ?? ''
|
||||
const dataLabel = (data as { connectionLabel?: string } | undefined)?.connectionLabel
|
||||
const displayLabel =
|
||||
getConnectionLabelForTargetAndStatus(targetType, connectionStatus) ?? labelProp ?? dataLabel
|
||||
|
||||
const [edgePath, edgeLabelX, edgeLabelY] = getBezierPath({
|
||||
sourceX,
|
||||
@@ -92,7 +96,7 @@ function AnimatedEdgeInner({
|
||||
className={`animated-edge-path${statusClass ? ` ${statusClass}` : ''}`}
|
||||
interactionWidth={interactionWidth}
|
||||
/>
|
||||
{label != null && (
|
||||
{displayLabel != null && displayLabel !== '' && (
|
||||
<g transform={`translate(${edgeLabelX}, ${edgeLabelY})`} className="nodrag nopan">
|
||||
<rect
|
||||
x={-32}
|
||||
@@ -109,7 +113,7 @@ function AnimatedEdgeInner({
|
||||
dominantBaseline="middle"
|
||||
className="fill-foreground text-[10px] font-medium"
|
||||
>
|
||||
{label}
|
||||
{displayLabel}
|
||||
</text>
|
||||
</g>
|
||||
)}
|
||||
@@ -130,8 +134,10 @@ function edgePropsAreEqual(prev: EdgeProps, next: EdgeProps): boolean {
|
||||
prev.targetPosition === next.targetPosition &&
|
||||
prev.style === next.style &&
|
||||
prev.label === next.label &&
|
||||
(prev.data as { connectionLabel?: string } | undefined)?.connectionLabel ===
|
||||
(next.data as { connectionLabel?: string } | undefined)?.connectionLabel
|
||||
(prev.data as { connectionLabel?: string; targetType?: string } | undefined)?.connectionLabel ===
|
||||
(next.data as { connectionLabel?: string; targetType?: string } | undefined)?.connectionLabel &&
|
||||
(prev.data as { targetType?: string } | undefined)?.targetType ===
|
||||
(next.data as { targetType?: string } | undefined)?.targetType
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export function getAgentNodeDescriptor(): NodeTypeDescriptor {
|
||||
.allowedTargetTypes(['render'])
|
||||
.help(NODE_HELP.agent)
|
||||
.menu('Agent', <Bot className={ICON_CLASS} />)
|
||||
.connectionLabel('prompt/context')
|
||||
.connectionLabelByStatus({ default: 'prompt/context', updating: 'running', paused: 'pending', error: 'corrupted' })
|
||||
.withFullscreen()
|
||||
.sourceRenderingLogic(agentRenderingLogic)
|
||||
.outputMenuContent(() => null)
|
||||
|
||||
@@ -82,7 +82,7 @@ export function getConfigNodeDescriptor(): NodeTypeDescriptor {
|
||||
content: '@startuml\n\n@enduml\n',
|
||||
title: nodeId ?? '',
|
||||
}))
|
||||
.connectionLabel('adding input')
|
||||
.connectionLabelByStatus({ default: 'adding input', updating: 'pushing new form', paused: 'pending', error: 'corrupted' })
|
||||
.withFullscreen()
|
||||
.sourceRenderingLogic({
|
||||
defaultUpdateMode: 'auto',
|
||||
|
||||
@@ -20,7 +20,7 @@ export function getDataNodeDescriptor(): NodeTypeDescriptor {
|
||||
.allowedTargetTypes(['config', 'agent'])
|
||||
.help(NODE_HELP.data)
|
||||
.menu('Data', <Database className={ICON_CLASS} />)
|
||||
.connectionLabel('data source')
|
||||
.connectionLabelByStatus({ default: 'data source' })
|
||||
.withFullscreen()
|
||||
.build()
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export function getFunctionNodeDescriptor(): NodeTypeDescriptor {
|
||||
.help(NODE_HELP.function)
|
||||
.menu('Function', <Code2 className={ICON_CLASS} />)
|
||||
.getResetData(() => ({ body: '' }))
|
||||
.connectionLabel('adding input')
|
||||
.connectionLabelByStatus({ default: 'adding input', updating: 'running', paused: 'pending', error: 'corrupted' })
|
||||
.withFullscreen()
|
||||
.build()
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export function getRenderNodeDescriptor(): NodeTypeDescriptor {
|
||||
.allowedSourceTypes(['config', 'agent'])
|
||||
.help(NODE_HELP.render)
|
||||
.menu('Renderer', <Sparkles className={ICON_CLASS} />)
|
||||
.connectionLabel('rendering')
|
||||
.connectionLabelByStatus({ default: 'listening', updating: 'giving life', paused: 'pending', error: 'corrupted' })
|
||||
.withFullscreen()
|
||||
.build()
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import type React from 'react'
|
||||
import type { ConnectionStatus } from './connectionStatus'
|
||||
import { registerSourceRenderingLogic } from './sourceRenderingLogic'
|
||||
import type { SourceRenderingLogic } from './sourceRenderingLogic'
|
||||
|
||||
@@ -44,8 +45,8 @@ export type NodeTypeDescriptor = {
|
||||
getDefaultData?: (newId?: string) => Record<string, unknown>
|
||||
/** Optional: data for Reset action; if omitted, getDefaultData(nodeId) or defaultData is used. */
|
||||
getResetData?: (nodeId?: string) => Record<string, unknown>
|
||||
/** Optional: label shown on edge when this type is the target (e.g. "render", "add input"). */
|
||||
connectionLabel?: string
|
||||
/** Optional: label per connection status shown on edge when this type is the target. */
|
||||
connectionLabelByStatus?: Partial<Record<ConnectionStatus, string>>
|
||||
/** When true, double-clicking the node header opens a fullscreen dialog for this node. */
|
||||
supportsFullscreen?: boolean
|
||||
/**
|
||||
@@ -157,9 +158,21 @@ export function isConnectionAllowed(
|
||||
return true
|
||||
}
|
||||
|
||||
/** Edge label when target is of this type (for AnimatedEdge). */
|
||||
/** Edge label when target is of this type (default status only; for edge data fallback). */
|
||||
export function getConnectionLabelForTarget(targetType: string): string | undefined {
|
||||
return getNodeType(targetType)?.connectionLabel
|
||||
return getNodeType(targetType)?.connectionLabelByStatus?.['default']
|
||||
}
|
||||
|
||||
/** Edge label for target type and connection status; falls back to default when status not in map. */
|
||||
export function getConnectionLabelForTargetAndStatus(
|
||||
targetType: string,
|
||||
status: ConnectionStatus
|
||||
): string | undefined {
|
||||
const desc = getNodeType(targetType)
|
||||
if (!desc?.connectionLabelByStatus) return undefined
|
||||
const byStatus = desc.connectionLabelByStatus[status]
|
||||
if (byStatus !== undefined) return byStatus
|
||||
return desc.connectionLabelByStatus['default']
|
||||
}
|
||||
|
||||
/** Help entry for a node type. Use in NodeHelpPopover. */
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
import type React from 'react'
|
||||
import type { ConnectionStatus } from './connectionStatus'
|
||||
import type { NodeTypeDescriptor, NodeClassification, NodeHelpEntry } from './nodeRegistry'
|
||||
import type { SourceRenderingLogic } from './sourceRenderingLogic'
|
||||
|
||||
@@ -97,8 +98,8 @@ export class NodeTypeBuilder {
|
||||
return this
|
||||
}
|
||||
|
||||
connectionLabel(label: string): this {
|
||||
this.partial.connectionLabel = label
|
||||
connectionLabelByStatus(partial: Partial<Record<ConnectionStatus, string>>): this {
|
||||
this.partial.connectionLabelByStatus = partial
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user