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.
58 lines
1.9 KiB
JavaScript
58 lines
1.9 KiB
JavaScript
// Page-header controls shared by every per-frame page (Photos/
|
|
// Configuration/Calendar/Whiteboard/Stats): the frame-name pencil-edit,
|
|
// living outside the tab structure since it applies regardless of which
|
|
// tab is open. Depends on window.FRAME_API (set per-page) 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_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_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();
|
|
});
|
|
})();
|