feat: Split toggle - show input/output as separate nodes

- Toggle button next to 'Merge Nodes' in toolbar
- When active: skips merge rules, each PipeWire node shown separately
- Green=output, red=input (no confusing duplex merging)
- Defaults to off (merged mode)
This commit is contained in:
joren
2026-03-30 01:17:10 +02:00
parent c586f63e8a
commit 7f214fc2d4

View File

@@ -42,6 +42,7 @@
let showProfileDialog = $state(false); let showProfileDialog = $state(false);
let showRuleDialog = $state(false); let showRuleDialog = $state(false);
let showVirtualMenu = $state(false); let showVirtualMenu = $state(false);
let splitNodes = $state(false);
let showNetworkDialog = $state<{ type: string } | null>(null); let showNetworkDialog = $state<{ type: string } | null>(null);
let netHost = $state('127.0.0.1'); let netHost = $state('127.0.0.1');
let netPort = $state('4713'); let netPort = $state('4713');
@@ -138,7 +139,8 @@
// Filter hidden nodes // Filter hidden nodes
let visible = n.filter(nd => !isNodeHidden(nd.name)); let visible = n.filter(nd => !isNodeHidden(nd.name));
// Merge nodes by prefix // Merge nodes by prefix (unless split mode)
if (!splitNodes) {
const merged = new Map<string, typeof visible[0]>(); const merged = new Map<string, typeof visible[0]>();
for (const nd of visible) { for (const nd of visible) {
const mergedName = getMergedName(nd.name); const mergedName = getMergedName(nd.name);
@@ -146,7 +148,6 @@
if (existing) { if (existing) {
existing.port_ids = [...new Set([...existing.port_ids, ...nd.port_ids])]; existing.port_ids = [...new Set([...existing.port_ids, ...nd.port_ids])];
existing.name = mergedName; existing.name = mergedName;
// If merged node has both input and output ports, mark as duplex
const mergedInPorts = existing.port_ids.map(pid => portMap.get(pid)).filter((p): p is Port => !!p && p.mode === 'input'); const mergedInPorts = existing.port_ids.map(pid => portMap.get(pid)).filter((p): p is Port => !!p && p.mode === 'input');
const mergedOutPorts = existing.port_ids.map(pid => portMap.get(pid)).filter((p): p is Port => !!p && p.mode === 'output'); const mergedOutPorts = existing.port_ids.map(pid => portMap.get(pid)).filter((p): p is Port => !!p && p.mode === 'output');
if (mergedInPorts.length > 0 && mergedOutPorts.length > 0) { if (mergedInPorts.length > 0 && mergedOutPorts.length > 0) {
@@ -157,6 +158,7 @@
} }
} }
visible = Array.from(merged.values()); visible = Array.from(merged.values());
}
const out = visible.filter(nd => nd.mode === 'output'); const out = visible.filter(nd => nd.mode === 'output');
const inp = visible.filter(nd => nd.mode === 'input'); const inp = visible.filter(nd => nd.mode === 'input');
@@ -464,6 +466,7 @@
<!-- Dialogs --> <!-- Dialogs -->
<button onclick={() => { showHideDialog = !showHideDialog; showMergeDialog = false; showProfileDialog = false; showRuleDialog = false; }} title="Node hiding rules">Hide Nodes</button> <button onclick={() => { showHideDialog = !showHideDialog; showMergeDialog = false; showProfileDialog = false; showRuleDialog = false; }} title="Node hiding rules">Hide Nodes</button>
<button onclick={() => { showMergeDialog = !showMergeDialog; showHideDialog = false; showProfileDialog = false; showRuleDialog = false; }} title="Node merging rules">Merge Nodes</button> <button onclick={() => { showMergeDialog = !showMergeDialog; showHideDialog = false; showProfileDialog = false; showRuleDialog = false; }} title="Node merging rules">Merge Nodes</button>
<button class="toggle" class:active={splitNodes} onclick={() => { splitNodes = !splitNodes; }} title="Show input/output as separate nodes">Split</button>
<button onclick={() => { showRuleDialog = !showRuleDialog; showHideDialog = false; showMergeDialog = false; showProfileDialog = false; }} title="Manage patchbay rules">Rules</button> <button onclick={() => { showRuleDialog = !showRuleDialog; showHideDialog = false; showMergeDialog = false; showProfileDialog = false; }} title="Manage patchbay rules">Rules</button>
<button onclick={() => { showProfileDialog = !showProfileDialog; showHideDialog = false; showMergeDialog = false; showRuleDialog = false; }} title="Save/load profiles">Profiles</button> <button onclick={() => { showProfileDialog = !showProfileDialog; showHideDialog = false; showMergeDialog = false; showRuleDialog = false; }} title="Save/load profiles">Profiles</button>
<button onclick={() => { showVirtualMenu = !showVirtualMenu; showHideDialog = false; showMergeDialog = false; showProfileDialog = false; showRuleDialog = false; }} title="Add virtual device">+ Add</button> <button onclick={() => { showVirtualMenu = !showVirtualMenu; showHideDialog = false; showMergeDialog = false; showProfileDialog = false; showRuleDialog = false; }} title="Add virtual device">+ Add</button>