feat: buffer/latency control + non-selectable node text
Buffer Control:
- GET /api/quantum - reads current graph quantum via pw-metadata
- POST /api/quantum {quantum:N} - sets quantum via pw-metadata
- Toolbar dropdown with presets: 32, 64, 128, 256, 512, 1024, 2048, 4096
- Loads current quantum on startup
Text Selection Fix:
- Added user-select: none to wrap and canvas CSS
- Node text no longer gets selected when dragging
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
saveProfile, loadProfile, deleteProfile,
|
saveProfile, loadProfile, deleteProfile,
|
||||||
setNodeVolume, setNodeMute,
|
setNodeVolume, setNodeMute,
|
||||||
createNullSink, createLoopback, loadModule,
|
createNullSink, createLoopback, loadModule,
|
||||||
|
getQuantum, setQuantum,
|
||||||
} from '../lib/stores';
|
} from '../lib/stores';
|
||||||
import type { Node, Port, Link } from '../lib/types';
|
import type { Node, Port, Link } from '../lib/types';
|
||||||
|
|
||||||
@@ -50,6 +51,7 @@
|
|||||||
|
|
||||||
// Positions
|
// Positions
|
||||||
let nodePositions = $state<Record<string, { x: number; y: number }>>({});
|
let nodePositions = $state<Record<string, { x: number; y: number }>>({});
|
||||||
|
let currentQuantum = $state(0);
|
||||||
const POS_KEY = 'pwweb_positions';
|
const POS_KEY = 'pwweb_positions';
|
||||||
|
|
||||||
function loadPositions() {
|
function loadPositions() {
|
||||||
@@ -420,7 +422,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => { initGraph(); loadPositions(); });
|
onMount(() => { initGraph(); loadPositions(); getQuantum().then(q => { currentQuantum = q; }); });
|
||||||
onDestroy(() => { destroyGraph(); });
|
onDestroy(() => { destroyGraph(); });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -460,6 +462,15 @@
|
|||||||
<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>
|
||||||
|
<span class="sep"></span>
|
||||||
|
<label class="quantum-label">Buffer:
|
||||||
|
<select class="quantum-select" onchange={(e) => { const q = Number((e.target as HTMLSelectElement).value); if (q > 0) { currentQuantum = q; setQuantum(q); } }}>
|
||||||
|
<option value="0" selected={currentQuantum === 0}>default</option>
|
||||||
|
{#each [32, 64, 128, 256, 512, 1024, 2048, 4096] as q}
|
||||||
|
<option value={q} selected={currentQuantum === q}>{q}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
<span class="stats">{$nodes.length}N {$ports.length}P {$links.length}L {#if $patchbay.pinned_connections.length > 0}{$patchbay.pinned_connections.length}p{/if}</span>
|
<span class="stats">{$nodes.length}N {$ports.length}P {$links.length}L {#if $patchbay.pinned_connections.length > 0}{$patchbay.pinned_connections.length}p{/if}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -821,8 +832,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.wrap { width: 100%; height: 100vh; background: #14141e; position: relative; overflow: hidden; }
|
.wrap { width: 100%; height: 100vh; background: #14141e; position: relative; overflow: hidden; user-select: none; -webkit-user-select: none; }
|
||||||
.canvas { width: 100%; height: 100%; display: block; cursor: default; position: absolute; top: 0; left: 0; z-index: 1; }
|
.canvas { width: 100%; height: 100%; display: block; cursor: default; position: absolute; top: 0; left: 0; z-index: 1; user-select: none; -webkit-user-select: none; }
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
position: absolute; top: 0; left: 0; right: 0; z-index: 10;
|
position: absolute; top: 0; left: 0; right: 0; z-index: 10;
|
||||||
@@ -859,6 +870,12 @@
|
|||||||
.toolbar button:hover, .toggle:hover { background: #3a3a4e; }
|
.toolbar button:hover, .toggle:hover { background: #3a3a4e; }
|
||||||
.toggle.active { background: #1a3a2a; border-color: #4a9; color: #6c9; }
|
.toggle.active { background: #1a3a2a; border-color: #4a9; color: #6c9; }
|
||||||
|
|
||||||
|
.quantum-label { font-size: 10px; color: #888; display: flex; align-items: center; gap: 4px; }
|
||||||
|
.quantum-select {
|
||||||
|
padding: 1px 4px; background: #1a1a2e; border: 1px solid #444;
|
||||||
|
color: #aaa; font-size: 10px; border-radius: 3px; font-family: monospace;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
.stats { margin-left: auto; color: #555; }
|
.stats { margin-left: auto; color: #555; }
|
||||||
|
|
||||||
/* Context menu */
|
/* Context menu */
|
||||||
|
|||||||
@@ -441,4 +441,25 @@ export async function unloadModule(moduleId: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quantum (buffer size) control
|
||||||
|
export async function getQuantum(): Promise<number> {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/quantum');
|
||||||
|
const data = await res.json();
|
||||||
|
return data.quantum || 0;
|
||||||
|
} catch { return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setQuantum(quantum: number) {
|
||||||
|
try {
|
||||||
|
await fetch('/api/quantum', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ quantum }),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[api] set-quantum failed:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export { connectPorts, disconnectPorts };
|
export { connectPorts, disconnectPorts };
|
||||||
|
|||||||
@@ -603,6 +603,42 @@ void WebServer::setupRoutes() {
|
|||||||
}
|
}
|
||||||
res.set_header("Access-Control-Allow-Origin", "*");
|
res.set_header("Access-Control-Allow-Origin", "*");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m_http.Options("/api/quantum", cors_handler);
|
||||||
|
|
||||||
|
// Get current quantum: GET /api/quantum
|
||||||
|
m_http.Get("/api/quantum", [](const httplib::Request &, httplib::Response &res) {
|
||||||
|
int quantum = 0;
|
||||||
|
FILE *fp = popen("pw-metadata 0 default.clock.quantum 2>/dev/null | grep -oP \"value:'\\K[0-9]+\"", "r");
|
||||||
|
if (fp) {
|
||||||
|
char buf[32] = {};
|
||||||
|
if (fgets(buf, sizeof(buf), fp))
|
||||||
|
quantum = atoi(buf);
|
||||||
|
pclose(fp);
|
||||||
|
}
|
||||||
|
char out[64];
|
||||||
|
snprintf(out, sizeof(out), "{\"quantum\":%d}", quantum);
|
||||||
|
res.set_content(out, "application/json");
|
||||||
|
res.set_header("Access-Control-Allow-Origin", "*");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set quantum: POST /api/quantum {"quantum":256}
|
||||||
|
m_http.Post("/api/quantum", [](const httplib::Request &req, httplib::Response &res) {
|
||||||
|
int quantum = 0;
|
||||||
|
if (sscanf(req.body.c_str(), "{\"quantum\":%d}", &quantum) == 1 && quantum > 0) {
|
||||||
|
char cmd[128];
|
||||||
|
snprintf(cmd, sizeof(cmd), "pw-metadata 0 default.clock.quantum %d 2>/dev/null", quantum);
|
||||||
|
int ret = system(cmd);
|
||||||
|
(void)ret;
|
||||||
|
char out[64];
|
||||||
|
snprintf(out, sizeof(out), "{\"ok\":true,\"quantum\":%d}", quantum);
|
||||||
|
res.set_content(out, "application/json");
|
||||||
|
} else {
|
||||||
|
res.status = 400;
|
||||||
|
res.set_content("{\"error\":\"invalid json\"}", "application/json");
|
||||||
|
}
|
||||||
|
res.set_header("Access-Control-Allow-Origin", "*");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// end of web_server.cpp
|
// end of web_server.cpp
|
||||||
|
|||||||
Reference in New Issue
Block a user