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,25 +139,26 @@
// 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)
const merged = new Map<string, typeof visible[0]>(); if (!splitNodes) {
for (const nd of visible) { const merged = new Map<string, typeof visible[0]>();
const mergedName = getMergedName(nd.name); for (const nd of visible) {
const existing = merged.get(mergedName); const mergedName = getMergedName(nd.name);
if (existing) { const existing = merged.get(mergedName);
existing.port_ids = [...new Set([...existing.port_ids, ...nd.port_ids])]; if (existing) {
existing.name = mergedName; existing.port_ids = [...new Set([...existing.port_ids, ...nd.port_ids])];
// If merged node has both input and output ports, mark as duplex existing.name = mergedName;
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) {
existing.mode = 'duplex'; existing.mode = 'duplex';
}
} else {
merged.set(mergedName, { ...nd, name: mergedName });
} }
} else {
merged.set(mergedName, { ...nd, name: mergedName });
} }
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>