тест 2

This commit is contained in:
2026-08-24 16:25:46 +03:00
parent c82ec2cbfb
commit f9df04bd00
6 changed files with 81 additions and 23 deletions

View File

@@ -4163,6 +4163,16 @@ function startCustomTooltips() {
};
if (!target.Input || !target.SelectedName) return [];
if (action === "stop" || action === "pause") {
// BUILD104: never reseed immediately after StopCountdown. vMix has already
// frozen its native countdown at the exact frame it received StopCountdown;
// a following SetCountdown from the browser can be slightly older and makes
// the on-air clock visibly jump backwards.
return [
{ Function: "StopCountdown", ...target },
];
}
if (action === "set") {
// Explicit edits/reset still need to write the Runtime value while stopped.
return [
{ Function: "StopCountdown", ...target },
{ Function: "SetCountdown", ...target, Value: currentValue },
@@ -4749,7 +4759,7 @@ function startCustomTooltips() {
commands.push(...vmixCountdownSyncCommands(input, selectedName, valueProvider, "stop"));
} else if (["timer_reset", "timer_set_time", "timer_add_time", "timer_subtract_time"].includes(eventName)) {
commands.push(...vmixCountdownSyncCommands(
input, selectedName, valueProvider, timerState.running ? "start" : "pause"
input, selectedName, valueProvider, timerState.running ? "start" : "set"
));
}
}
@@ -5019,7 +5029,7 @@ function startCustomTooltips() {
return `${String(step?.id || "")}:${side}:${String(target?.id || "")}`;
}
async function rebalanceVmixPenaltyTargets({ force = false, hideUnused = true } = {}) {
async function rebalanceVmixPenaltyTargets({ force = false, hideUnused = true, preservePausedCountdown = false } = {}) {
const outCommands = [];
const stopCommands = [];
const setCommands = [];
@@ -5077,10 +5087,13 @@ function startCustomTooltips() {
runCommands.push({ Function: "StartCountdown", Input: target.input, SelectedName: target.selected_name });
}
} else if (force || assignmentChanged || previous?.running !== false) {
// Freeze the title and seed the exact Runtime value, but never run a
// prepared/paused penalty until the web event itself is running.
// BUILD104: a normal Pause of a prepared/paused penalty freezes the native vMix countdown only.
// Re-seeding after StopCountdown can visibly roll the clock back.
// Explicit reset/set-time/assignment changes may still seed a value.
stopCommands.push({ Function: "StopCountdown", Input: target.input, SelectedName: target.selected_name });
setCommands.push({ Function: "SetCountdown", Input: target.input, SelectedName: target.selected_name, Value: () => vmixCountdownValue(entry.event.remainingMs) });
if (!preservePausedCountdown || assignmentChanged) {
setCommands.push({ Function: "SetCountdown", Input: target.input, SelectedName: target.selected_name, Value: () => vmixCountdownValue(entry.event.remainingMs) });
}
}
} else {
assignedMirrorKeys.add(eventKey);
@@ -7220,8 +7233,11 @@ function openTimerQuickEditor(focusActionId = "") {
persistHockeyBoard(component, board, true);
refreshHockeyBoardNodes(component);
if (options.syncVmix !== false && state.activeHockeyVmixTimerSteps.size && ["start", "pause", "reset", "set_time"].includes(command)) {
rebalanceVmixPenaltyTargets({ force: true, hideUnused: true })
.catch((error) => console.error("Penalty countdown state sync error", error));
rebalanceVmixPenaltyTargets({
force: true,
hideUnused: true,
preservePausedCountdown: command === "pause",
}).catch((error) => console.error("Penalty countdown state sync error", error));
}
return true;
}
@@ -13852,7 +13868,7 @@ function renderHockeyPenaltyDashboard(node, component, runtime) {
<div class="shortcut-finish-action-list">${timerFinishActionRows(step)}</div>
<div class="shortcut-vmix-inventory-note"><span>${escapeHtml(shortcutInventoryLabel())}</span><small>Для каждого countdown теперь обязательно выбирается конкретный Text / SelectedName. Это исключает отправку времени в первый текстовый элемент по умолчанию.</small></div>
<p class="shortcut-step-note">Режим <b>Countdown vMix</b>: веб-таймер является главным. Start сразу меняет состояние Runtime и независимо отправляет <code>SetCountdown → StartCountdown</code> в vMix; Pause/Stop сразу останавливают Runtime и независимо отправляют <code>StopCountdown → SetCountdown</code>. Ошибка Agent не блокирует управление таймером. Каждую секунду значение в vMix не передаётся. <b>Text mirror</b> оставлен только для совместимости со старыми титрами. Для верхнего счёта используется одна penalty-плашка: при реальном большинстве она показывает ближайшее изменение численного состава; при чистом равном обоюдном удалении сама по себе не появляется.</p>
<p class="shortcut-step-note">Режим <b>Countdown vMix</b>: веб-таймер является главным. Start сразу меняет состояние Runtime и независимо отправляет <code>SetCountdown → StartCountdown</code> в vMix; Pause/Stop сразу останавливают Runtime и независимо отправляют только <code>StopCountdown</code>, без повторной установки времени. Ошибка Agent не блокирует управление таймером. Каждую секунду значение в vMix не передаётся. <b>Text mirror</b> оставлен только для совместимости со старыми титрами. Для верхнего счёта используется одна penalty-плашка: при реальном большинстве она показывает ближайшее изменение численного состава; при чистом равном обоюдном удалении сама по себе не появляется.</p>
</div>`;
} else if (step.type === "delay") {
body.innerHTML = `<div class="shortcut-step-grid"><label>Задержка, мс<input type="number" min="0" max="10000" step="10" data-step-field="milliseconds" value="${Number(step.milliseconds) || 0}"></label></div>`;