[feat](trx-frontend-http): lay the scheduler controls out in one row
CI / lint (push) Successful in 2m17s
CI / test (push) Successful in 8m12s
CI / frontend (push) Failing after 29s
CI / reuse (push) Successful in 3s

The controls were a column — release, then the step buttons, then the
status line, then the entry on air last — which read bottom-up and left
the entry that is actually transmitting furthest from the buttons that
change it.  They now run left to right: step through the entries, hand
the rig back, then the current entry behind a separator.

The separator is a pseudo-element on the current-entry block rather than
an element of its own, because that block is display-toggled whenever
fewer than two entries are active; a standalone rule would be left
hanging with nothing after it.

No ids move, so the enable/disable logic in the scheduler plugin and the
release polling in vchan bind exactly as before.  The smoke test asserts
the row's order, which is also what keeps the separator in place.

Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
sjg
2026-08-03 20:58:31 +02:00
parent b2fbcb318d
commit 92fbdb692c
3 changed files with 44 additions and 15 deletions
@@ -367,23 +367,25 @@ SPDX-License-Identifier: GPL-2.0-or-later
<div class="advanced-radio-body"> <div class="advanced-radio-body">
<div class="scheduler-control-row" style="display:none"> <div class="scheduler-control-row" style="display:none">
<div class="scheduler-release-wrap"> <div class="scheduler-release-wrap">
<button id="scheduler-release-btn" type="button">Release to Scheduler</button> <div class="scheduler-action-row">
<div class="scheduler-step-controls"> <div class="scheduler-step-controls">
<button id="scheduler-prev-btn" type="button">Previous Entry</button> <button id="scheduler-prev-btn" type="button">Previous Entry</button>
<button id="scheduler-next-btn" type="button">Next Entry</button> <button id="scheduler-next-btn" type="button">Next Entry</button>
</div> </div>
<div id="scheduler-release-status" class="scheduler-release-status">Scheduler is controlling the rig.</div> <button id="scheduler-release-btn" type="button">Release to Scheduler</button>
<div id="scheduler-cycle-status" class="interleave-ring-wrap" style="display:none;"> <div id="scheduler-cycle-status" class="interleave-ring-wrap" style="display:none;">
<svg class="interleave-ring" viewBox="0 0 36 36"> <svg class="interleave-ring" viewBox="0 0 36 36">
<circle class="interleave-ring-bg" cx="18" cy="18" r="15.915" /> <circle class="interleave-ring-bg" cx="18" cy="18" r="15.915" />
<circle class="interleave-ring-fill" id="interleave-ring-fill" cx="18" cy="18" r="15.915" <circle class="interleave-ring-fill" id="interleave-ring-fill" cx="18" cy="18" r="15.915"
stroke-dasharray="100" stroke-dashoffset="100" /> stroke-dasharray="100" stroke-dashoffset="100" />
</svg> </svg>
<div class="interleave-ring-text"> <div class="interleave-ring-text">
<div class="interleave-ring-label" id="interleave-active-name">--</div> <div class="interleave-ring-label" id="interleave-active-name">--</div>
<div class="interleave-ring-sub" id="interleave-countdown">--</div> <div class="interleave-ring-sub" id="interleave-countdown">--</div>
</div>
</div> </div>
</div> </div>
<div id="scheduler-release-status" class="scheduler-release-status">Scheduler is controlling the rig.</div>
</div> </div>
</div> </div>
</div> </div>
@@ -575,6 +575,24 @@ input.status-input, select.status-input { width: 100%; padding: 0.45rem 0.5rem;
border-color: var(--accent-yellow); border-color: var(--accent-yellow);
color: var(--accent-yellow); color: var(--accent-yellow);
} }
/* One row, read left to right: step through the entries, hand the rig back,
then — fenced off — the entry actually on air. */
.scheduler-action-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.35rem;
}
/* The rule belongs to the current-entry block, so it leaves with it: the
block is display-toggled whenever fewer than two entries are active. */
.scheduler-action-row #scheduler-cycle-status::before {
content: "";
align-self: stretch;
width: 1px;
min-height: 1.7rem;
margin-inline: 0.3rem;
background: color-mix(in srgb, var(--border-light) 70%, transparent);
}
.scheduler-step-controls { .scheduler-step-controls {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@@ -196,6 +196,15 @@ try {
await page.locator("summary", { hasText: "Audio controls" }).click(); await page.locator("summary", { hasText: "Audio controls" }).click();
assert.equal(await page.locator("#rx-audio-btn").count(), 1); assert.equal(await page.locator("#rx-audio-btn").count(), 1);
// Scheduler controls read left to right: step, hand back, then the entry on
// air. The separator is drawn by the current-entry block, so it can only sit
// in the right place if that block is last.
const schedulerRow = await page.evaluate(() => [...document.querySelectorAll(".scheduler-action-row > *")]
.map((el) => el.id || [...el.children].map((c) => c.id).join("+")));
assert.deepEqual(schedulerRow,
["scheduler-prev-btn+scheduler-next-btn", "scheduler-release-btn", "scheduler-cycle-status"],
`scheduler control order is ${JSON.stringify(schedulerRow)}`);
// The footer status pill colours its dot from data-state, so a hint written // The footer status pill colours its dot from data-state, so a hint written
// straight to textContent would leave the dot stuck on the previous state. // straight to textContent would leave the dot stuck on the previous state.
const hint = await page.evaluate(() => { const hint = await page.evaluate(() => {