Merge feature/node-aliases into master
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
setAutoPin, setAutoDisconnect,
|
||||
saveProfile, loadProfile, deleteProfile,
|
||||
setNodeVolume, setNodeMute,
|
||||
setAlias,
|
||||
createNullSink, createLoopback, loadModule,
|
||||
getQuantum, setQuantum,
|
||||
} 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 nodeContextMenu = $state<{ x: number; y: number; nodeId: number; nodeName: string } | null>(null);
|
||||
let showPropsDialog = $state<number | null>(null); // node ID or null
|
||||
let renameDialog = $state<{ pwName: string } | null>(null);
|
||||
let renameInput = $state('');
|
||||
|
||||
// Filters
|
||||
let showAudio = $state(true);
|
||||
@@ -128,6 +131,11 @@
|
||||
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
|
||||
let graphNodes = $derived.by(() => {
|
||||
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 + 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">
|
||||
{nd.nick || nd.name}
|
||||
{displayName(nd)}
|
||||
</text>
|
||||
<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}]
|
||||
@@ -680,8 +688,13 @@
|
||||
|
||||
{#if nodeContextMenu}
|
||||
<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={() => {
|
||||
renameDialog = { pwName: nodeContextMenu!.nodeName };
|
||||
renameInput = $patchbay.aliases?.[nodeContextMenu!.nodeName] ?? '';
|
||||
nodeContextMenu = null;
|
||||
}}>Rename</button>
|
||||
<button onclick={() => {
|
||||
fetch('/api/destroy-node', {
|
||||
method: 'POST',
|
||||
@@ -699,7 +712,7 @@
|
||||
{#if nd}
|
||||
<div class="dialog">
|
||||
<div class="dialog-header">
|
||||
<span>Properties: {nd.nick || nd.name}</span>
|
||||
<span>Properties: {displayName(nd)}</span>
|
||||
<button class="close" onclick={() => { showPropsDialog = null; }}>X</button>
|
||||
</div>
|
||||
<div class="dialog-body">
|
||||
@@ -730,6 +743,31 @@
|
||||
{/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 -->
|
||||
{#if showHideDialog}
|
||||
<div class="dialog">
|
||||
|
||||
@@ -19,6 +19,7 @@ export const patchbay = writable<PatchbayState>({
|
||||
pinned_connections: [],
|
||||
hide_rules: [],
|
||||
merge_rules: [],
|
||||
aliases: {},
|
||||
});
|
||||
|
||||
// Port/node lookups
|
||||
@@ -82,7 +83,7 @@ export async function initGraph() {
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data && data.profiles) {
|
||||
patchbay.set(data as PatchbayState);
|
||||
patchbay.set({ ...data, aliases: data.aliases ?? {} } as PatchbayState);
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
@@ -357,6 +358,20 @@ export function deleteProfile(name: string) {
|
||||
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
|
||||
export async function setNodeVolume(nodeId: number, volume: number) {
|
||||
try {
|
||||
|
||||
@@ -85,4 +85,5 @@ export interface PatchbayState {
|
||||
pinned_connections: number[];
|
||||
hide_rules: string[];
|
||||
merge_rules: string[];
|
||||
aliases: Record<string, string>; // PW node name → custom display name
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user