{ contextMenu = null; nodeContextMenu = null; }} />
{$connected ? 'Connected' : 'Disconnected'} {$nodes.length}N {$ports.length}P {$links.length}L {#if $patchbay.pinned_connections.length > 0}{$patchbay.pinned_connections.length}p{/if}
{#if showVirtualMenu}
Add Virtual Device
{/if} {#each graphLinks as link (link.id)} {@const outPos = getPortPos(link.output_port_id)} {@const inPos = getPortPos(link.input_port_id)} {#if outPos && inPos} {@const color = portColor(link.outPort.port_type)} {@const isSelected = selectedEdge === String(link.id)} {#if link.pinned} {/if} {/if} {/each} {#if connecting} {/if} {#each graphNodes as nd (nd.id)} {@const isSource = nd.mode === 'output'} {@const isSink = nd.mode === 'input'} {@const bg = isSource ? '#1a2a1a' : isSink ? '#2a1a1a' : '#1e1e2e'} {@const border = isSource ? '#4a9' : isSink ? '#a44' : '#555'} {@const headerBg = isSource ? '#263826' : isSink ? '#382626' : '#262638'} {nd.nick || nd.name} [{nd.node_type}] {#each nd.inPorts as port, i (port.id)} {@const py = nd.y + 22 + i * 16 + 8} {#if isPortVisible(port.port_type)} { hoveredPort = port.id; }} onmouseleave={() => { hoveredPort = null; }} /> {shortName(port.name)} {:else} {/if} {/each} {#each nd.outPorts as port, i (port.id)} {@const py = nd.y + 22 + i * 16 + 8} {#if isPortVisible(port.port_type)} {shortName(port.name)} { hoveredPort = port.id; }} onmouseleave={() => { hoveredPort = null; }} /> {:else} {/if} {/each} {nd.mute ? 'M' : 'm'} {Math.round(Math.max(0, Math.min(1, nd.volume)) * 100)}% {/each} {#if contextMenu} {/if} {#if nodeContextMenu} {/if} {#if showHideDialog}
Node Hiding Rules

Hide nodes matching pattern (regex or plain text).

{#each $patchbay.hide_rules as rule}
{rule}
{/each} {#if $patchbay.hide_rules.length === 0}
No hiding rules.
{/if}
{/if} {#if showMergeDialog}
Node Merging Rules

Merge nodes sharing a common name prefix into one block.

{#each $patchbay.merge_rules as rule}
{rule}
{/each} {#if $patchbay.merge_rules.length === 0}
No merge rules.
{/if}
{/if} {#if showProfileDialog}
Profile Management
{#each Object.entries($patchbay.profiles) as [name, profile]}
{name} ({profile.connections.length} rules)
{/each} {#if Object.keys($patchbay.profiles).length === 0}
No saved profiles.
{/if}
{/if} {#if showRuleDialog}
Rule Management
{#if $patchbay.active_profile && $patchbay.profiles[$patchbay.active_profile]} {@const profile = $patchbay.profiles[$patchbay.active_profile]}

Active profile: {profile.name} ({profile.connections.length} rules)

{#each profile.connections as rule, i}
{rule.output_node_name}:{shortName(rule.output_port_name)} => {rule.input_node_name}:{shortName(rule.input_port_name)} {rule.output_port_type} {#if rule.pinned}PIN{/if}
{/each}
{:else}
No active profile. Save a profile first via Profiles.
{/if}
{/if}