Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions src/main/frontend/app/routes/studio/canvas/flow.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,17 @@ export const FlowConfig = {
MAX_HISTORY: 20, // Adjust this value as needed to limit the number of undo steps
LAYOUT_HORIZONTAL_OFFSET: 300,
LAYOUT_VERTICAL_OFFSET: 200,
ZOOM_THRESHOLD: 0.6,
}

/**
* Counter-scale factor for compact-mode labels so they stay readable when zoomed out.
*
* The React Flow viewport scales all node content by `zoom`, so a fixed font size shrinks on
* screen as you zoom out. Multiplying a label's font size by this factor keeps its on-screen
* size roughly constant. Only called in compact mode (zoom < {@link FlowConfig.ZOOM_THRESHOLD}),
* so the result is always >= 1, and the canvas `minZoom` bounds how large it can get.
*/
export function getCompactLabelScale(zoom: number): number {
return FlowConfig.ZOOM_THRESHOLD / zoom
}
6 changes: 3 additions & 3 deletions src/main/frontend/app/routes/studio/canvas/flow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@
logApiError('Failed to save XML', error as Error)
setIdle()
}
}, [])

Check warning on line 341 in src/main/frontend/app/routes/studio/canvas/flow.tsx

View workflow job for this annotation

GitHub Actions / Build & Run All Tests

React Hook useCallback has missing dependencies: 'setIdle', 'setSaved', and 'setSaving'. Either include them or remove the dependency array

const autosaveEnabled = useSettingsStore((s) => s.general.autoSave.enabled)
const autosaveDelay = useSettingsStore((s) => s.general.autoSave.delayMs)
Expand Down Expand Up @@ -481,7 +481,7 @@
const mouseEvent = event as MouseEvent
if (!connectionState.isValid) {
const zoom = reactFlow.getZoom()
if (zoom < 0.4 && sourceInfoReference.current.handleType === 'source') {
if (zoom < FlowConfig.ZOOM_THRESHOLD && sourceInfoReference.current.handleType === 'source') {
const { nodes } = useFlowStore.getState()
const sourceNode = nodes.find((node) => node.id === sourceInfoReference.current.nodeId)
if (sourceNode && isFrankNode(sourceNode)) {
Expand All @@ -500,7 +500,7 @@
(connection: Connection) => {
const zoom = reactFlow.getZoom()

if (zoom < 0.4 && connection.source) {
if (zoom < FlowConfig.ZOOM_THRESHOLD && connection.source) {
const { nodes } = useFlowStore.getState()
const sourceNode = nodes.find((node) => node.id === connection.source)

Expand Down Expand Up @@ -1220,7 +1220,7 @@
setParentId(null)
}

function addNodeAtPosition(

Check warning on line 1223 in src/main/frontend/app/routes/studio/canvas/flow.tsx

View workflow job for this annotation

GitHub Actions / Build & Run All Tests

Refactor this function to reduce its Cognitive Complexity from 17 to the 15 allowed
position: { x: number; y: number },
elementName: string,
sourceInfo?: { nodeId: string | null; handleId: string | null; handleType: 'source' | 'target' | null },
Expand Down Expand Up @@ -1262,7 +1262,7 @@
if (sourceInfo?.nodeId && sourceInfo.handleType === 'source') {
const sourceNode = flowStore.nodes.find((node) => node.id === sourceInfo.nodeId)

if (reactFlow.getZoom() < 0.4 && sourceNode && isFrankNode(sourceNode)) {
if (reactFlow.getZoom() < FlowConfig.ZOOM_THRESHOLD && sourceNode && isFrankNode(sourceNode)) {
if (edgeDropHandleType) {
const existingHandle = sourceNode.data.sourceHandles.find((handle) => handle.type === edgeDropHandleType)
if (existingHandle) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,11 @@ export default function ExitNodeComponent(properties: NodeProps<ExitNode>) {
const minNodeHeight = FlowConfig.EXIT_DEFAULT_HEIGHT
const gradientEnabled = useSettingsStore((state) => state.studio.gradient)
const zoom = useStore((state) => state.transform[2])
const isCompact = zoom < 0.4
const isCompact = zoom < FlowConfig.ZOOM_THRESHOLD

if (isCompact) {
return (
<ZoomedOutNode
subtype={properties.data.subtype}
name={properties.data.name}
attributes={properties.data.attributes}
colorVariable="--type-exit"
selected={properties.selected}
/>
<ZoomedOutNode subtype={properties.data.subtype} colorVariable="--type-exit" selected={properties.selected} />
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
} = useNodeContextStore()
const gradientEnabled = useSettingsStore((state) => state.studio.gradient)
const zoom = useStore((state) => state.transform[2])
const isCompact = zoom < 0.4
const isCompact = zoom < FlowConfig.ZOOM_THRESHOLD
const [isOverflowing, setIsOverflowing] = useState(false)

const frankElement = useMemo(() => {
Expand Down Expand Up @@ -346,7 +346,7 @@

addChild(properties.id, child)
},
[

Check warning on line 349 in src/main/frontend/app/routes/studio/canvas/nodetypes/frank-node.tsx

View workflow job for this annotation

GitHub Actions / Build & Run All Tests

React Hook useCallback has missing dependencies: 'setAttributes' and 'setNodeId'. Either include them or remove the dependency array
properties.id,
addChild,
setIsNewNode,
Expand Down Expand Up @@ -402,8 +402,6 @@
return (
<ZoomedOutNode
subtype={properties.data.subtype}
name={properties.data.name}
attributes={properties.data.attributes}
colorVariable={colorVariable}
selected={properties.selected}
showTargetHandle={properties.data.subtype !== 'Receiver'}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { Handle, Position } from '@xyflow/react'
import { FlowConfig } from '~/routes/studio/canvas/flow.config'
import { Handle, Position, useStore } from '@xyflow/react'
import { FlowConfig, getCompactLabelScale } from '~/routes/studio/canvas/flow.config'

const COMPACT_INITIALS_BOX_SIZE = 160
const COMPACT_PADDING_TOP = 8
const COMPACT_HANDLE_SIZE = 15
const COMPACT_HANDLE_GAP = 4
const COMPACT_LABEL_BASE_FONT_PX = 22

export type ZoomedOutNodeProps = {
subtype: string
name?: string
attributes?: Record<string, string>
colorVariable: string
selected?: boolean
showTargetHandle?: boolean
Expand All @@ -23,20 +22,19 @@ function getAbbreviation(subtype: string): string {

/**
* Compact representation of a node shown when the canvas is zoomed out far enough that the full
* node would be unreadable. Renders an initials box with the subtype, name attributes and the
* handles aligned under it.
* node would be unreadable. Renders an initials box with the subtype aligned under it.
*/
export default function ZoomedOutNode({
subtype,
name,
attributes,
colorVariable,
selected,
showTargetHandle = true,
sourceHandles = [],
width = FlowConfig.NODE_DEFAULT_WIDTH,
}: Readonly<ZoomedOutNodeProps>) {
const abbr = getAbbreviation(subtype)
const zoom = useStore((state) => state.transform[2])
const labelFontSize = `${COMPACT_LABEL_BASE_FONT_PX * getCompactLabelScale(zoom)}px`

const compactXOffsetPx = (width - COMPACT_INITIALS_BOX_SIZE) / 2 - COMPACT_HANDLE_SIZE - COMPACT_HANDLE_GAP
const compactHandleTop =
Expand Down Expand Up @@ -65,15 +63,9 @@ export default function ZoomedOutNode({
</span>
</div>

<span className="text-center text-3xl leading-snug font-semibold whitespace-nowrap">{subtype}</span>

{name && <span className="text-foreground-muted text-center text-3xl whitespace-nowrap">{name}</span>}
{attributes &&
Object.entries(attributes).map(([key, value]) => (
<span key={key} className="text-foreground-muted text-center text-2xl whitespace-nowrap">
{value || key}
</span>
))}
<span className="text-center leading-snug font-semibold whitespace-nowrap" style={{ fontSize: labelFontSize }}>

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.

Suggested change
<span className="text-center leading-snug font-semibold whitespace-nowrap" style={{ fontSize: labelFontSize }}>
<span className="text-center leading-snug font-medium whitespace-nowrap" style={{ fontSize: labelFontSize }}>

{subtype}
</span>
</div>

{showTargetHandle && (
Expand Down
Loading