Widget system Phase 4a: widget CRUD + grid placement UI
Build and push server image / test (push) Successful in 19s
Build and push server image / build-and-push (push) Successful in 2m32s

Adds the actual "Android home screen" placement experience: a new
Layout tab with a pointer-driven canvas for dragging/resizing widgets
and adding new ones from a type picker. Backed by a new
routers/api_widgets.py (create/move/delete), which re-validates
bounds, minimum footprint, and no-overlap server-side regardless of
what the client already checked. A widget added without an explicit
position lands in the first open space that fits it
(grid.find_open_rect), so users don't have to hunt for empty space
themselves.

Also fixes a real latent bug this surfaced: changing a frame's
orientation swaps the widget grid's long/short axis, which left
existing widget placements out of bounds on the new grid with no
render-time safeguard. Orientation changes now reset the layout to a
single full-panel widget (keeping the first widget's type, dropping
the rest), with a client-side confirm before it happens.
This commit is contained in:
2026-07-24 12:48:56 -04:00
parent 99069ba5fe
commit 5d4bb53b8a
11 changed files with 780 additions and 5 deletions
+21
View File
@@ -27,10 +27,31 @@ async function saveConfig() {
}
}
// Orientation swaps the widget grid's long/short axis (see
// grid.grid_dims), so an existing widget layout is usually left with
// out-of-bounds coordinates on the new grid -- the server resets it to
// one full-panel widget when this actually changes (see
// api_frames.py's api_config_save). Warn before that happens rather
// than silently losing whatever layout was on the Layout tab.
let lastSavedOrientation = document.getElementById('orientation').value;
document.getElementById('config-form').addEventListener('submit', async (e) => {
e.preventDefault();
const newOrientation = document.getElementById('orientation').value;
if (newOrientation !== lastSavedOrientation) {
const proceed = confirm(
"Changing orientation resets this frame's widget layout to a single " +
'full-panel widget -- any other widgets placed on the Layout tab will ' +
'be removed. Continue?'
);
if (!proceed) {
document.getElementById('orientation').value = lastSavedOrientation;
return;
}
}
try {
await saveConfig();
lastSavedOrientation = newOrientation;
showStatus(true, 'Saved.');
} catch (e) {
showStatus(false, e.message);