Files
espresso_frame/server/app/templates/frame_layout.html
T
tfaour fcf3aec4c0
Build and push server image / test (push) Successful in 36s
Firmware build check / build-check (push) Successful in 2m4s
Build and push server image / build-and-push (push) Successful in 3m12s
Build and push server image / deploy (push) Successful in 58s
Move button actions to per-widget config, add hold-for-global-action
Next/back button assignment moves from a frame-level "Button
assignments" card into each widget's own gear-icon dialog, prefilled
with a sane default at creation (photos/calendar -> advance/back,
whiteboard/weather -> check_now, others -> none). At most one binding
per (widget, button) now -- cross-widget execution order never
mattered since each widget's action only touches its own state.

New firmware capability: holding NEXT or BACK past a configurable
duration (min 3s, server-side default) triggers a frame-wide action
instead of the per-widget short-press one -- cycling saved layouts,
refreshing all widgets, or freezing/unfreezing every photo widget (see
app/global_actions.py). Firmware next/back checks gain the same
hold-duration polling the combo button already had; the threshold
comes from the previous wake's /frame/config fetch (persisted in NVS),
since this wake's button decision happens before that request.

Not done here: firmware/version.txt is intentionally left unbumped --
this hasn't been built or hardware-tested (no ESP-IDF toolchain in this
environment), so no firmware release build should be triggered yet.
2026-07-27 22:09:33 +00:00

84 lines
3.8 KiB
HTML

{% extends "app_base.html" %}
{% block title %}{{ frame.name or "Frame" }} · Layout{% endblock %}
{% block page_title %}{% include "_frame_name_edit.html" %}{% endblock %}
{% block device_status %}{% include "_device_status_bar.html" %}{% endblock %}
{% block tabs %}{% include "_frame_tabs.html" %}{% endblock %}
{% block content %}
<div id="control-banner" class="control-banner" style="display: none;">
<span id="control-holder"></span>
<button type="button" id="take-control" class="btn-inline">Take control</button>
</div>
<div class="layout">
<div class="main-col">
<section class="card">
<div class="card-title-row">
<h2 class="card-title">Widgets</h2>
<button type="button" id="clear-all-widgets" class="secondary btn-inline">Clear all</button>
</div>
<p class="sub">Drag a widget to move it, drag its bottom-right corner
to resize it -- like arranging widgets on a phone's home screen.
Widgets can't overlap. Click a widget's gear icon for its own
settings (which album, which calendars, etc.).</p>
<div id="widget-canvas-wrap">
<div id="widget-canvas"></div>
</div>
<p class="sub" id="widget-canvas-empty-hint" style="display: none; margin-top: 10px;">
Nothing placed yet -- add a widget below.</p>
</section>
<section class="card" style="margin-top: 20px;">
<h2 class="card-title">Add a widget</h2>
<div id="add-widget-buttons" class="checkbox-row" style="gap: 10px; flex-wrap: wrap;"></div>
<p class="sub" id="add-widget-hint" style="margin-top: 8px;"></p>
</section>
<section class="card" style="margin-top: 20px;">
<h2 class="card-title">Saved layouts</h2>
<p class="sub">Save this whole arrangement -- widgets, their settings, and button
assignments -- under a name, then switch back to it any time. Saved layouts are
yours across every frame you control with a matching orientation, not just this one.</p>
<div class="saved-layout-add">
<input type="text" id="saved-layout-name" placeholder="Layout name" maxlength="60">
<button type="button" id="save-layout-btn" class="secondary">Save current as...</button>
</div>
<ul class="saved-layout-list" id="saved-layout-list" style="margin-top: 14px;"></ul>
<p class="sub" id="saved-layout-empty-hint" style="margin-top: 10px;">No saved layouts yet.</p>
</section>
</div>
</div>
<dialog id="widget-dialog">
<button type="button" id="widget-dialog-close" class="icon-btn" aria-label="Close" title="Close">&times;</button>
<div id="widget-dialog-body"><p class="sub">Loading...</p></div>
<div class="dialog-result"></div>
</dialog>
<div id="result"></div>
{% endblock %}
{% block scripts %}
<script>
window.FRAME_BASE_API = window.FRAME_API = {{ ("/api/frames/" ~ frame.id) | tojson }};
window.FRAME_ID = {{ frame.id | tojson }};
</script>
<script src="/static/device_status_bar.js"></script>
<script src="/static/frame_header.js"></script>
<script src="/static/widget_dialog_photos.js"></script>
<script src="/static/queue.js"></script>
<script src="/static/widget_dialog_calendar.js"></script>
<script src="/static/widget_dialog_whiteboard.js"></script>
<script src="/static/widget_dialog_tasks.js"></script>
<script src="/static/widget_dialog_static.js"></script>
<script src="/static/widget_dialog_text.js"></script>
<script src="/static/widget_dialog_weather.js"></script>
<script src="/static/widget_dialog_battery.js"></script>
<script src="/static/widget_dialog_border.js"></script>
<script src="/static/widget_dialog_button_actions.js"></script>
<script src="/static/frame_layout.js"></script>
<script src="/static/saved_layouts.js"></script>
{% endblock %}