Initial commit: pwweb - PipeWire WebUI
C++ backend with SSE streaming (reuses qpwgraph PipeWire callbacks). Svelte frontend with custom SVG canvas for port-level connections. Features: - Live PipeWire graph via SSE - Drag output->input port to connect - Double-click or select+Delete to disconnect - Node positions saved to localStorage - Pan (drag bg) and zoom (scroll) - Port type coloring (audio=green, midi=red, video=blue)
This commit is contained in:
64
frontend/src/lib/stores.ts
Normal file
64
frontend/src/lib/stores.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { writable, derived, get } from 'svelte/store';
|
||||
import type { Node, Port, Link, GraphMessage } from './types';
|
||||
import { subscribe, connectPorts, disconnectPorts } from './ws';
|
||||
|
||||
// Raw graph stores
|
||||
export const nodes = writable<Node[]>([]);
|
||||
export const ports = writable<Port[]>([]);
|
||||
export const links = writable<Link[]>([]);
|
||||
export const connected = writable(false);
|
||||
|
||||
// Port lookup map
|
||||
export const portById = derived(ports, ($ports) => {
|
||||
const map = new Map<number, Port>();
|
||||
for (const p of $ports) map.set(p.id, p);
|
||||
return map;
|
||||
});
|
||||
|
||||
// Node lookup map
|
||||
export const nodeById = derived(nodes, ($nodes) => {
|
||||
const map = new Map<number, Node>();
|
||||
for (const n of $nodes) map.set(n.id, n);
|
||||
return map;
|
||||
});
|
||||
|
||||
// Initialize: fetch via REST immediately, then subscribe to WS for updates
|
||||
let unsubscribe: (() => void) | null = null;
|
||||
|
||||
function applyGraph(graph: GraphMessage) {
|
||||
nodes.set(graph.nodes);
|
||||
ports.set(graph.ports);
|
||||
links.set(graph.links);
|
||||
connected.set(true);
|
||||
}
|
||||
|
||||
export async function initGraph() {
|
||||
if (unsubscribe) return;
|
||||
|
||||
// 1. Fetch initial state via REST API (works immediately, no WS needed)
|
||||
try {
|
||||
const res = await fetch('/api/graph');
|
||||
if (res.ok) {
|
||||
const graph: GraphMessage = await res.json();
|
||||
applyGraph(graph);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[graph] REST fetch failed:', e);
|
||||
}
|
||||
|
||||
// 2. Subscribe to WebSocket for live updates
|
||||
unsubscribe = subscribe((graph: GraphMessage) => {
|
||||
applyGraph(graph);
|
||||
});
|
||||
}
|
||||
|
||||
export function destroyGraph() {
|
||||
if (unsubscribe) {
|
||||
unsubscribe();
|
||||
unsubscribe = null;
|
||||
connected.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Re-export connection functions
|
||||
export { connectPorts, disconnectPorts };
|
||||
Reference in New Issue
Block a user