Widget system Phase 2: full cutover to widget-based rendering
Build and push server image / test (push) Successful in 49s
Build and push server image / build-and-push (push) Successful in 1m56s

device.py's mode-keyed dispatch is replaced by a real compositor:
load a frame's widgets, compute pixel rects via app/grid.py, render
each through its widget module, and composite with render_panel.
Physical NEXT/BACK buttons now execute each frame's assigned
FrameButtonAction rows instead of one hardcoded per-mode action.

api_frames.py, manage.py, and common.py's build_manage_content are
repointed to read/write the frame's widget config rows instead of
the old Frame columns, and every settings page (Photos/Calendar/
Whiteboard tabs) now pre-fills its form from the same widget config
the write endpoints actually save to -- previously the read and
write sides would have silently diverged. The old mode selector and
photo-inlay checkbox are removed along with their now-inert wiring;
arbitrary widget placement subsumes what the fixed inlay split did.

Ships together with Phase 1 (per-type render/action modules) since
splitting the read/write cutover across deploys would have left
settings changes with no visible effect.
This commit is contained in:
2026-07-24 09:26:28 -04:00
parent f48daa71c8
commit 37bd657299
26 changed files with 1070 additions and 791 deletions
+2 -2
View File
@@ -2,8 +2,8 @@
<a href="/frames/{{ frame.id }}" class="{% if active_tab == 'photos' %}active{% endif %}">Photos</a>
<a href="/frames/{{ frame.id }}/config" class="{% if active_tab == 'config' %}active{% endif %}">Configuration</a>
<a href="/frames/{{ frame.id }}/calendar"
class="{% if active_tab == 'calendar' %}active{% endif %} {% if frame.mode != 'calendar' %}tab-disabled{% endif %}">Calendar</a>
class="{% if active_tab == 'calendar' %}active{% endif %} {% if not has_calendar_widget %}tab-disabled{% endif %}">Calendar</a>
<a href="/frames/{{ frame.id }}/whiteboard"
class="{% if active_tab == 'whiteboard' %}active{% endif %} {% if frame.mode != 'whiteboard' %}tab-disabled{% endif %}">Whiteboard</a>
class="{% if active_tab == 'whiteboard' %}active{% endif %} {% if not has_whiteboard_widget %}tab-disabled{% endif %}">Whiteboard</a>
<a href="/frames/{{ frame.id }}/stats" class="{% if active_tab == 'stats' %}active{% endif %}">Stats</a>
</nav>