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
+5 -6
View File
@@ -2,7 +2,6 @@
{% block title %}{{ frame.name or "Frame" }} · Photos{% endblock %}
{% block page_title %}{% include "_frame_name_edit.html" %}{% endblock %}
{% block mode_picker %}{% include "_frame_mode_picker.html" %}{% endblock %}
{% block device_status %}{% include "_device_status_bar.html" %}{% endblock %}
{% block tabs %}{% include "_frame_tabs.html" %}{% endblock %}
@@ -20,26 +19,26 @@
<form id="photos-form">
<label>Album
<select id="album_id">
{% if frame.album_id %}<option value="{{ frame.album_id }}" selected>(current selection -- Load Albums to change)</option>{% endif %}
{% if photo_cfg and photo_cfg.album_id %}<option value="{{ photo_cfg.album_id }}" selected>(current selection -- Load Albums to change)</option>{% endif %}
</select>
</label>
<label>Upcoming photos to show
<select id="queue_target_len">
{% for n in [5, 10, 15, 20, 25, 30, 40, 50] %}
<option value="{{ n }}" {% if frame.queue_target_len == n %}selected{% endif %}>{{ n }}</option>
<option value="{{ n }}" {% if photo_cfg and photo_cfg.queue_target_len == n %}selected{% endif %}>{{ n }}</option>
{% endfor %}
</select>
</label>
<label>Order
<select id="order">
<option value="sequential" {% if frame.order == "sequential" %}selected{% endif %}>Sequential</option>
<option value="shuffle" {% if frame.order == "shuffle" %}selected{% endif %}>Shuffle</option>
<option value="sequential" {% if not photo_cfg or photo_cfg.order == "sequential" %}selected{% endif %}>Sequential</option>
<option value="shuffle" {% if photo_cfg and photo_cfg.order == "shuffle" %}selected{% endif %}>Shuffle</option>
</select>
</label>
<label>Display mode
<select id="display_mode">
{% for mode, label in display_mode_labels.items() %}
<option value="{{ mode }}" {% if frame.display_mode == mode %}selected{% endif %}>{{ label }}</option>
<option value="{{ mode }}" {% if photo_cfg and photo_cfg.display_mode == mode %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</label>