Files
SonicForgeStudio/app/static/vst_gui.html
T

133 lines
5.2 KiB
HTML

<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>VST GUI</title>
<style>
:root { color-scheme: dark; }
* { box-sizing: border-box; }
body { margin: 0; font-family: system-ui, -apple-system, "Segoe UI", sans-serif; background: #16161a; color: #e4e4e7; }
header { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; background: #1e1e24; border-bottom: 1px solid #2d2d35; position: sticky; top: 0; z-index: 10; }
header h1 { font-size: 13px; margin: 0; font-weight: 600; color: #7dd3fc; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
header .meta { font-size: 10px; color: #71717a; margin-top: 2px; }
#params { padding: 10px 12px; }
.param { display: grid; grid-template-columns: minmax(0,1fr) 60px; align-items: center; gap: 8px; padding: 6px 0; border-bottom: 1px solid #26262d; }
.param .title { font-size: 11px; color: #d4d4d8; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.param .val { font-size: 10px; font-family: ui-monospace, monospace; color: #7dd3fc; text-align: right; }
input[type=range] { width: 100%; accent-color: #0ea5e9; }
#status { padding: 8px 12px; font-size: 11px; color: #a1a1aa; }
#status.err { color: #f87171; }
</style>
</head>
<body>
<header>
<div>
<h1 id="title">VST GUI</h1>
<div class="meta" id="meta"></div>
</div>
<button id="closeBtn" style="background:#3f3f46;border:1px solid #52525b;color:#f4f4f5;font-size:11px;padding:4px 10px;border-radius:4px;cursor:pointer;">Đóng</button>
</header>
<div id="params"></div>
<div id="status">Đang tải tham số…</div>
<script>
(function () {
const qs = new URLSearchParams(window.location.search);
const trackId = qs.get('track') || '';
const pluginId = qs.get('plugin') || '';
const el = (id) => document.getElementById(id);
el('title').textContent = 'VST GUI - ' + pluginId;
el('meta').textContent = 'track=' + trackId;
const invoke = (cmd, args) => {
if (window.__TAURI__ && window.__TAURI__.core) {
return window.__TAURI__.core.invoke(cmd, args);
}
return Promise.reject(new Error('Tauri core unavailable'));
};
let params = [];
function render() {
const box = el('params');
box.innerHTML = '';
if (!params.length) {
el('status').textContent = 'Plugin không có tham số (hoặc chưa nạp).';
return;
}
el('status').textContent = params.length + ' tham số — kéo slider để đổi giá trị.';
params.forEach((p) => {
const row = document.createElement('div');
row.className = 'param';
const title = document.createElement('div');
title.className = 'title';
title.textContent = p.title || ('Param ' + p.param_id);
title.title = title.textContent;
const val = document.createElement('div');
val.className = 'val';
const range = document.createElement('input');
range.type = 'range';
range.min = 0;
range.max = 1;
range.step = 0.001;
range.value = Math.min(1, Math.max(0, p.value || 0));
val.textContent = (p.value || 0).toFixed(3);
let dragging = false;
range.addEventListener('input', () => {
val.textContent = Number(range.value).toFixed(3);
});
range.addEventListener('change', () => {
invoke('set_vst_param', { trackId: trackId, pluginId: pluginId, paramId: p.param_id, value: Number(range.value) })
.catch((err) => { el('status').className = 'err'; el('status').textContent = 'set_vst_param lỗi: ' + (err && (err.message || err)); });
});
row.appendChild(title);
row.appendChild(val);
row.appendChild(range);
box.appendChild(row);
});
}
// Sync 2 chiều: native editor đổi param → Rust emit vst_param_changed → cập nhật slider.
function onParamChanged(e) {
const d = e.payload || {};
if (d.param_id === undefined) return;
const p = params.find((x) => x.param_id === d.param_id);
if (p) {
p.value = d.value;
const ranges = document.querySelectorAll('input[type=range]');
const idx = params.indexOf(p);
if (ranges[idx]) ranges[idx].value = Math.min(1, Math.max(0, d.value));
const vals = document.querySelectorAll('.val');
if (vals[idx]) vals[idx].textContent = Number(d.value).toFixed(3);
}
}
function init() {
invoke('get_vst_params', { trackId: trackId, pluginId: pluginId }).then((list) => {
params = list || [];
render();
}).catch((err) => {
el('status').className = 'err';
el('status').textContent = 'get_vst_params lỗi: ' + (err && (err.message || err)) + ' — plugin chưa mở được (kiểm tra log Rust / bridge DLL).';
});
}
el('closeBtn').addEventListener('click', () => {
invoke('close_vst_editor', { trackId: trackId, pluginId: pluginId }).catch(() => {});
window.close();
});
window.addEventListener('beforeunload', () => {
invoke('close_vst_editor', { trackId: trackId, pluginId: pluginId }).catch(() => {});
});
if (window.__TAURI__ && window.__TAURI__.event) {
window.__TAURI__.event.listen('vst_param_changed', onParamChanged).catch(() => {});
}
init();
})();
</script>
</body>
</html>