Add a "Clear all" button to the Layout tab's widget canvas
Build and push server image / test (push) Successful in 23s
Build and push server image / build-and-push (push) Successful in 2m1s
Build and push server image / deploy (push) Successful in 54s

One request (DELETE /api/frames/{id}/widgets) removes every widget on
the frame in a single locked transaction, cascading their configs and
button-action bindings the same way single-widget delete already does.
Gated behind confirm() like the existing per-widget remove button, and
disabled when there's nothing to clear. Covered by the same owner/
unrelated-user/linked-but-not-controlling permission shape used
elsewhere (test_widget_placement.py).

Also fixes a real, pre-existing mobile bug this surfaced: the Layout
page's .layout grid used a bare `1fr` track on the <860px breakpoint
instead of `minmax(0, 1fr)` like the desktop rule already does, so a
wide enough descendant (previously nothing hit this; the new title-row
button did) would force the whole page into horizontal scroll on phone
widths. Verified before/after with the run-server driver's new
`viewport` command.
This commit is contained in:
Thomas Faour
2026-07-25 01:32:25 +00:00
parent 0e35735a2a
commit 9f3f4b6f62
6 changed files with 110 additions and 3 deletions
+18
View File
@@ -160,6 +160,23 @@ async function removeWidget(id) {
}
}
async function clearAllWidgets() {
const count = gridState ? gridState.widgets.length : 0;
if (!count) return;
const noun = count === 1 ? 'widget' : 'widgets';
if (!confirm(`Remove all ${count} ${noun} from this frame and start over? Their settings will be lost.`)) return;
try {
const resp = await fetch(`${window.FRAME_API}/widgets`, { method: 'DELETE' });
if (!resp.ok) throw new Error(await apiError(resp));
showStatus(true, 'Cleared.');
} catch (e) {
showStatus(false, e.message);
} finally {
loadWidgets();
}
}
document.getElementById('clear-all-widgets').addEventListener('click', clearAllWidgets);
async function addWidget(widgetType) {
try {
const resp = await fetch(`${window.FRAME_API}/widgets`, {
@@ -180,6 +197,7 @@ function renderCanvas() {
const canvas = document.getElementById('widget-canvas');
canvas.innerHTML = '';
document.getElementById('widget-canvas-empty-hint').style.display = gridState.widgets.length ? 'none' : '';
document.getElementById('clear-all-widgets').disabled = !gridState.widgets.length;
for (const widget of gridState.widgets) {
const box = document.createElement('div');