${esc(item.device_id)}(() => { "use strict"; const boot = window.UI_BUILDER_BOOT || {}; if (boot.mode !== "runtime") return; const selectedDeviceStorage = "hockey.vmix.selected_device"; const state = { devices: [], activeDeviceId: "", activeDeviceIds: [], selectedDeviceId: localStorage.getItem(selectedDeviceStorage) || "", root: null, panelOpen: false, loading: false, timer: null, message: "", messageError: false, testOpenDeviceId: "", testCommand: { input: "", selectedName: "", value: "" }, }; const esc = (value) => String(value ?? "") .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); async function api(path, options = {}) { const response = await fetch(path, { cache: "no-store", credentials: "same-origin", ...options, headers: { ...(options.body ? { "Content-Type": "application/json" } : {}), ...(options.headers || {}), }, }); let payload = {}; try { payload = await response.json(); } catch (_) {} if (!response.ok) { const detail = typeof payload.detail === "string" ? payload.detail : payload.detail?.message || `HTTP ${response.status}`; throw new Error(detail); } return payload; } function runtimeData() { return window.UIBuilderRuntime?.getData?.() || {}; } function currentGameId() { const data = runtimeData(); return String(data.hockey?.selected_game?.external_id || data.hockey?.selected_game?.id || "").trim(); } function currentSessionToken() { return String(runtimeData().hockey?.operator_session?.token || "").trim(); } function activeDevices() { return state.devices.filter((item) => item.paired_to_me && item.active_for_account); } function selectedDevice() { return state.devices.find((item) => ( item.device_id === state.selectedDeviceId && item.paired_to_me && item.active_for_account )) || null; } function selectLocalDevice(deviceId) { state.selectedDeviceId = String(deviceId || "").trim(); if (state.selectedDeviceId) localStorage.setItem(selectedDeviceStorage, state.selectedDeviceId); else localStorage.removeItem(selectedDeviceStorage); window.dispatchEvent(new CustomEvent("hockey:agent-selected", { detail: { device_id: state.selectedDeviceId }, })); } async function bindSelectedDevice(deviceId) { selectLocalDevice(deviceId); const token = currentSessionToken(); if (!token) return null; return api(`/api/hockey/agents/devices/${encodeURIComponent(deviceId)}/select-session`, { method: "POST", body: JSON.stringify({ session_token: token }), }); } function setMessage(text, error = false) { state.message = text || ""; state.messageError = error; render(); if (text) setTimeout(() => { if (state.message === text) { state.message = ""; render(); } }, 3500); } function ensureRoot() { if (state.root?.isConnected) return state.root; const root = document.createElement("section"); root.className = "hockey-agent-ui"; root.innerHTML = `
`; const toolbar = document.querySelector(".runtime-topbar .runtime-actions"); if (toolbar) toolbar.prepend(root); else document.body.appendChild(root); root.querySelector("[data-agent-toggle]")?.addEventListener("click", () => { state.panelOpen = !state.panelOpen; render(); }); root.addEventListener("click", onClick); root.addEventListener("input", (event) => { const field = event.target.closest("[data-agent-test-field]"); if (!field) return; const key = field.dataset.agentTestField; if (key === "input") state.testCommand.input = field.value; if (key === "selectedName") state.testCommand.selectedName = field.value; if (key === "value") state.testCommand.value = field.value; }); state.root = root; return root; } async function onClick(event) { const button = event.target.closest("button[data-agent-action]"); if (!button || state.loading) return; const action = button.dataset.agentAction; const deviceId = button.dataset.deviceId || ""; if (!deviceId) return; state.loading = true; render(); try { if (action === "pair") { await api(`/api/hockey/agents/devices/${encodeURIComponent(deviceId)}/pair`, { method: "POST", body: JSON.stringify({ make_active: true }), }); await bindSelectedDevice(deviceId); setMessage("Agent прикреплён и выбран для этой панели"); } else if (action === "activate") { await api(`/api/hockey/agents/devices/${encodeURIComponent(deviceId)}/activate`, { method: "POST" }); await bindSelectedDevice(deviceId); setMessage("Agent включён и выбран для этой панели"); } else if (action === "select") { await bindSelectedDevice(deviceId); setMessage("Эта панель теперь управляет выбранным Agent"); } else if (action === "deactivate") { await api(`/api/hockey/agents/devices/${encodeURIComponent(deviceId)}/deactivate`, { method: "POST" }); if (state.selectedDeviceId === deviceId) selectLocalDevice(""); setMessage("Получение данных для Agent остановлено"); } else if (action === "test-toggle") { state.testOpenDeviceId = state.testOpenDeviceId === deviceId ? "" : deviceId; } else if (action === "test-set-text") { const input = state.testCommand.input.trim(); const selectedName = state.testCommand.selectedName.trim(); if (!input || !selectedName) throw new Error("Заполните Input и имя текстового поля"); const result = await api(`/api/hockey/agents/devices/${encodeURIComponent(deviceId)}/test-set-text`, { method: "POST", body: JSON.stringify({ input, selected_name: selectedName, value: state.testCommand.value }), }); setMessage(`SetText выполнен · матч ${result.match_id}`); } else if (action === "unpair") { if (!window.confirm("Отвязать этот agent от вашего аккаунта?")) return; await api(`/api/hockey/agents/devices/${encodeURIComponent(deviceId)}/pair`, { method: "DELETE" }); if (state.testOpenDeviceId === deviceId) state.testOpenDeviceId = ""; if (state.selectedDeviceId === deviceId) selectLocalDevice(""); setMessage("Agent отвязан"); } await load(true); } catch (error) { setMessage(error.message || "Ошибка Agent", true); } finally { state.loading = false; render(); } } async function load(force = false) { if (state.loading && !force) return; try { const payload = await api("/api/hockey/agents/devices"); state.devices = Array.isArray(payload.devices) ? payload.devices : []; state.activeDeviceId = String(payload.active_device_id || ""); state.activeDeviceIds = Array.isArray(payload.active_device_ids) ? payload.active_device_ids.map(String) : (state.activeDeviceId ? [state.activeDeviceId] : []); const selectedStillValid = state.devices.some((item) => ( item.device_id === state.selectedDeviceId && item.paired_to_me && item.active_for_account )); if (!selectedStillValid) { const mine = activeDevices(); if (mine.length === 1) selectLocalDevice(mine[0].device_id); else if (state.selectedDeviceId) selectLocalDevice(""); } const editingTest = Boolean(state.root?.querySelector("[data-agent-test-form]")?.contains(document.activeElement)); if (!editingTest) render(); } catch (error) { if (!String(error.message || "").includes("401")) console.warn("Could not load vMix agents", error); } } function deviceCard(item) { const online = Boolean(item.online); const vmix = Boolean(item.vmix_connected); const mine = Boolean(item.paired_to_me); const active = Boolean(item.active_for_account); const selectedHere = state.selectedDeviceId === item.device_id; const free = item.pair_state === "free"; const busy = item.pair_state === "busy"; const currentMatch = String(item.current_match_id || ""); let actions = ""; if (free && online) { actions = ``; } else if (mine) { const canTest = active && selectedHere && online && vmix && currentMatch; actions = ` ${active ? 'Получает данные' : ``} ${active ? (selectedHere ? 'Эта панель' : ``) : ""} ${canTest ? `` : ""} ${active ? `` : ""} `; } else if (busy) { actions = 'Занято другим оператором'; } const testForm = state.testOpenDeviceId === item.device_id ? `${esc(item.device_id)}