Files
espresso_frame/server/app/static/frame_header.js
T
tfaour 9c8a87e90d
Build and push server image / test (push) Successful in 22s
Build and push server image / build-and-push (push) Successful in 1m59s
Build and push server image / deploy (push) Successful in 48s
Add a live preview thumbnail next to the frame name
Small header thumbnail showing exactly what the frame is currently
displaying -- same widget compositor /frame/image uses, handed back as
a plain PNG (image_pipeline.render_panel/render_placeholder gain an
as_png option) instead of packed native-panel bytes. New session-authed
GET /api/frames/{id}/preview exposes it; click-to-refresh plus a slow
60s poll on the frame header so it doesn't hammer Immich/calendar
sources just for a header thumbnail.
2026-07-24 15:55:59 -04:00

77 lines
2.7 KiB
JavaScript

// Page-header controls shared by every per-frame page (Layout/
// Configuration/Stats): the frame-name pencil-edit, living outside the
// tab structure since it applies regardless of which tab is open.
// Depends on window.FRAME_BASE_API (a stable frame-level base set by
// every page -- unlike window.FRAME_API, which the Layout page's
// widget dialogs repoint to a widget-scoped base while one is open) and
// common.js's showStatus/apiError.
(function () {
var view = document.getElementById('frame-name-view');
var editRow = document.getElementById('frame-name-edit-row');
var pencil = document.getElementById('frame-name-pencil');
var input = document.getElementById('frame-name-input');
var textEl = document.getElementById('frame-name-text');
var saveBtn = document.getElementById('frame-name-save');
var cancelBtn = document.getElementById('frame-name-cancel');
if (!view || !window.FRAME_BASE_API) return;
function openEdit() {
input.value = textEl.textContent.trim();
view.style.display = 'none';
editRow.style.display = 'inline-flex';
input.focus();
input.select();
}
function closeEdit() {
editRow.style.display = 'none';
view.style.display = 'inline-flex';
}
pencil.addEventListener('click', openEdit);
cancelBtn.addEventListener('click', closeEdit);
async function save() {
var name = input.value.trim();
if (!name || name === textEl.textContent.trim()) {
closeEdit();
return;
}
try {
const resp = await fetch(`${window.FRAME_BASE_API}/config`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ name }),
});
if (!resp.ok) throw new Error(await apiError(resp));
textEl.textContent = name;
closeEdit();
showStatus(true, 'Renamed.');
} catch (e) {
showStatus(false, e.message);
}
}
saveBtn.addEventListener('click', save);
input.addEventListener('keydown', function (e) {
if (e.key === 'Enter') save();
if (e.key === 'Escape') closeEdit();
});
})();
// Live "how it's displaying" thumbnail. A real composite render (same
// pipeline /frame/image uses), not a cached snapshot, so it's on a slow
// poll and also click-to-refresh rather than something tighter like the
// 10s device-status poll -- no need to hit Immich/calendar/whiteboard
// sources that often just for a header thumbnail.
(function () {
var thumb = document.getElementById('frame-preview-thumb');
if (!thumb || !window.FRAME_BASE_API) return;
function refresh() {
thumb.src = `${window.FRAME_BASE_API}/preview?t=${Date.now()}`;
}
thumb.addEventListener('click', refresh);
refresh();
setInterval(refresh, 60000);
})();