Merge feature/node-aliases into master
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
setAutoPin, setAutoDisconnect,
|
setAutoPin, setAutoDisconnect,
|
||||||
saveProfile, loadProfile, deleteProfile,
|
saveProfile, loadProfile, deleteProfile,
|
||||||
setNodeVolume, setNodeMute,
|
setNodeVolume, setNodeMute,
|
||||||
|
setAlias,
|
||||||
createNullSink, createLoopback, loadModule,
|
createNullSink, createLoopback, loadModule,
|
||||||
getQuantum, setQuantum,
|
getQuantum, setQuantum,
|
||||||
} from '../lib/stores';
|
} from '../lib/stores';
|
||||||
@@ -29,6 +30,8 @@
|
|||||||
let contextMenu = $state<{ x: number; y: number; linkId: number; outputPortId: number; inputPortId: number; pinned: boolean } | null>(null);
|
let contextMenu = $state<{ x: number; y: number; linkId: number; outputPortId: number; inputPortId: number; pinned: boolean } | null>(null);
|
||||||
let nodeContextMenu = $state<{ x: number; y: number; nodeId: number; nodeName: string } | null>(null);
|
let nodeContextMenu = $state<{ x: number; y: number; nodeId: number; nodeName: string } | null>(null);
|
||||||
let showPropsDialog = $state<number | null>(null); // node ID or null
|
let showPropsDialog = $state<number | null>(null); // node ID or null
|
||||||
|
let renameDialog = $state<{ pwName: string } | null>(null);
|
||||||
|
let renameInput = $state('');
|
||||||
|
|
||||||
// Filters
|
// Filters
|
||||||
let showAudio = $state(true);
|
let showAudio = $state(true);
|
||||||
@@ -128,6 +131,11 @@
|
|||||||
return nodeName;
|
return nodeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return custom alias, otherwise nick, otherwise PW name
|
||||||
|
function displayName(nd: { name: string; nick: string }): string {
|
||||||
|
return $patchbay.aliases?.[nd.name] || nd.nick || nd.name;
|
||||||
|
}
|
||||||
|
|
||||||
// Build computed layout
|
// Build computed layout
|
||||||
let graphNodes = $derived.by(() => {
|
let graphNodes = $derived.by(() => {
|
||||||
const n = $nodes;
|
const n = $nodes;
|
||||||
@@ -593,7 +601,7 @@
|
|||||||
<rect x={nd.x} y={nd.y} width={nd.width} height="22" rx="4" fill={headerBg} />
|
<rect x={nd.x} y={nd.y} width={nd.width} height="22" rx="4" fill={headerBg} />
|
||||||
<rect x={nd.x} y={nd.y + 16} width={nd.width} height="6" fill={headerBg} />
|
<rect x={nd.x} y={nd.y + 16} width={nd.width} height="6" fill={headerBg} />
|
||||||
<text x={nd.x + 6} y={nd.y + 15} font-size="10" font-family="monospace" fill="#ddd" font-weight="bold">
|
<text x={nd.x + 6} y={nd.y + 15} font-size="10" font-family="monospace" fill="#ddd" font-weight="bold">
|
||||||
{nd.nick || nd.name}
|
{displayName(nd)}
|
||||||
</text>
|
</text>
|
||||||
<text x={nd.x + nd.width - 6} y={nd.y + 15} font-size="9" font-family="monospace" fill="#777" text-anchor="end">
|
<text x={nd.x + nd.width - 6} y={nd.y + 15} font-size="9" font-family="monospace" fill="#777" text-anchor="end">
|
||||||
[{nd.node_type}]
|
[{nd.node_type}]
|
||||||
@@ -680,8 +688,13 @@
|
|||||||
|
|
||||||
{#if nodeContextMenu}
|
{#if nodeContextMenu}
|
||||||
<div class="ctx" style="left:{nodeContextMenu.x}px;top:{nodeContextMenu.y}px" role="menu">
|
<div class="ctx" style="left:{nodeContextMenu.x}px;top:{nodeContextMenu.y}px" role="menu">
|
||||||
<div class="ctx-title">{nodeContextMenu.nodeName}</div>
|
<div class="ctx-title">{$patchbay.aliases?.[nodeContextMenu.nodeName] || nodeContextMenu.nodeName}</div>
|
||||||
<button onclick={() => { showPropsDialog = nodeContextMenu!.nodeId; nodeContextMenu = null; }}>Properties</button>
|
<button onclick={() => { showPropsDialog = nodeContextMenu!.nodeId; nodeContextMenu = null; }}>Properties</button>
|
||||||
|
<button onclick={() => {
|
||||||
|
renameDialog = { pwName: nodeContextMenu!.nodeName };
|
||||||
|
renameInput = $patchbay.aliases?.[nodeContextMenu!.nodeName] ?? '';
|
||||||
|
nodeContextMenu = null;
|
||||||
|
}}>Rename</button>
|
||||||
<button onclick={() => {
|
<button onclick={() => {
|
||||||
fetch('/api/destroy-node', {
|
fetch('/api/destroy-node', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -699,7 +712,7 @@
|
|||||||
{#if nd}
|
{#if nd}
|
||||||
<div class="dialog">
|
<div class="dialog">
|
||||||
<div class="dialog-header">
|
<div class="dialog-header">
|
||||||
<span>Properties: {nd.nick || nd.name}</span>
|
<span>Properties: {displayName(nd)}</span>
|
||||||
<button class="close" onclick={() => { showPropsDialog = null; }}>X</button>
|
<button class="close" onclick={() => { showPropsDialog = null; }}>X</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-body">
|
<div class="dialog-body">
|
||||||
@@ -730,6 +743,31 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<!-- Rename Node Dialog -->
|
||||||
|
{#if renameDialog}
|
||||||
|
<div class="dialog" style="right:auto;left:50%;top:50%;transform:translate(-50%,-50%);width:280px">
|
||||||
|
<div class="dialog-header">
|
||||||
|
<span>Rename Node</span>
|
||||||
|
<button class="close" onclick={() => { renameDialog = null; }}>X</button>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-body">
|
||||||
|
<p class="hint" style="word-break:break-all">{renameDialog.pwName}</p>
|
||||||
|
<div class="input-row">
|
||||||
|
<input
|
||||||
|
class="dlg-input"
|
||||||
|
bind:value={renameInput}
|
||||||
|
placeholder="Custom display name (leave blank to reset)"
|
||||||
|
onkeydown={(e) => { if (e.key === 'Enter') { setAlias(renameDialog!.pwName, renameInput); renameDialog = null; } }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="input-row" style="justify-content:flex-end;gap:6px">
|
||||||
|
<button onclick={() => { setAlias(renameDialog!.pwName, ''); renameDialog = null; }}>Reset</button>
|
||||||
|
<button onclick={() => { setAlias(renameDialog!.pwName, renameInput); renameDialog = null; }}>Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<!-- Hide Nodes Dialog -->
|
<!-- Hide Nodes Dialog -->
|
||||||
{#if showHideDialog}
|
{#if showHideDialog}
|
||||||
<div class="dialog">
|
<div class="dialog">
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export const patchbay = writable<PatchbayState>({
|
|||||||
pinned_connections: [],
|
pinned_connections: [],
|
||||||
hide_rules: [],
|
hide_rules: [],
|
||||||
merge_rules: [],
|
merge_rules: [],
|
||||||
|
aliases: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Port/node lookups
|
// Port/node lookups
|
||||||
@@ -82,7 +83,7 @@ export async function initGraph() {
|
|||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (data && data.profiles) {
|
if (data && data.profiles) {
|
||||||
patchbay.set(data as PatchbayState);
|
patchbay.set({ ...data, aliases: data.aliases ?? {} } as PatchbayState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
@@ -357,6 +358,20 @@ export function deleteProfile(name: string) {
|
|||||||
savePatchbayState();
|
savePatchbayState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Node aliases (custom display names, keyed by PW node name)
|
||||||
|
export function setAlias(pwName: string, alias: string) {
|
||||||
|
patchbay.update(pb => {
|
||||||
|
const aliases = { ...pb.aliases };
|
||||||
|
if (alias.trim()) {
|
||||||
|
aliases[pwName] = alias.trim();
|
||||||
|
} else {
|
||||||
|
delete aliases[pwName]; // empty string = remove alias
|
||||||
|
}
|
||||||
|
return { ...pb, aliases };
|
||||||
|
});
|
||||||
|
savePatchbayState();
|
||||||
|
}
|
||||||
|
|
||||||
// Volume control
|
// Volume control
|
||||||
export async function setNodeVolume(nodeId: number, volume: number) {
|
export async function setNodeVolume(nodeId: number, volume: number) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -85,4 +85,5 @@ export interface PatchbayState {
|
|||||||
pinned_connections: number[];
|
pinned_connections: number[];
|
||||||
hide_rules: string[];
|
hide_rules: string[];
|
||||||
merge_rules: string[];
|
merge_rules: string[];
|
||||||
|
aliases: Record<string, string>; // PW node name → custom display name
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user