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.
92 lines
4.4 KiB
HTML
92 lines
4.4 KiB
HTML
{% extends "app_base.html" %}
|
|
|
|
{% block title %}{{ frame.name or "Frame" }} · Whiteboard{% 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>
|
|
|
|
{% if not has_whiteboard_widget %}
|
|
<div class="info-box">This frame doesn't have a <strong>Whiteboard</strong> widget on
|
|
screen yet -- settings below won't show up anywhere until one is added.</div>
|
|
{% endif %}
|
|
|
|
<div class="layout">
|
|
<div class="main-col">
|
|
<section class="card">
|
|
<h2 class="card-title">Whiteboard source</h2>
|
|
<p class="sub">Renders a Nextcloud Whiteboard (or any Excalidraw
|
|
scene) fetched over WebDAV -- credentials set up in
|
|
<a href="/settings">Settings</a>.</p>
|
|
|
|
<div id="whiteboard-current-source">
|
|
{% if whiteboard_source %}
|
|
<p class="sub" style="margin-top: 10px;">
|
|
Currently showing <strong>{{ whiteboard_source.url }}</strong>
|
|
using <strong>{{ whiteboard_source.display_name }}</strong>'s
|
|
WebDAV account.
|
|
<button type="button" class="btn-inline secondary" id="whiteboard-source-clear">Clear</button>
|
|
</p>
|
|
{% else %}
|
|
<p class="sub" style="margin-top: 10px;">No whiteboard configured yet.</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if viewer_has_webdav_creds %}
|
|
<form id="whiteboard-source-form" style="margin-top: 16px;">
|
|
<label><span id="whiteboard-source-form-label">{{ "Change to one of your own files" if whiteboard_source else "Use one of your own files" }}</span>
|
|
<input type="text" id="whiteboard-url-input"
|
|
placeholder="https://cloud.example.com/remote.php/dav/files/you/Boards/family.whiteboard"
|
|
value="{{ whiteboard_source.url if whiteboard_source and whiteboard_source.user_id == user.id else '' }}">
|
|
</label>
|
|
<p class="sub" style="margin-top: 4px;">The direct WebDAV URL
|
|
to the specific file -- in Nextcloud's Files app, this is
|
|
the file's path under
|
|
<code>remote.php/dav/files/<your-username>/</code>.
|
|
<button type="button" class="btn-inline secondary" id="whiteboard-browse-toggle">Browse...</button></p>
|
|
|
|
<div id="whiteboard-browser" style="display: none; margin-top: 8px; border: 1px solid var(--border-color, #ccc); border-radius: 6px; padding: 8px;">
|
|
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 6px;">
|
|
<button type="button" class="btn-inline secondary" id="whiteboard-browse-up" disabled>Up</button>
|
|
<span class="sub" id="whiteboard-browse-current" style="word-break: break-all;"></span>
|
|
</div>
|
|
<ul id="whiteboard-browse-list" style="list-style: none; margin: 0; padding: 0; max-height: 260px; overflow-y: auto;"></ul>
|
|
<p class="sub" id="whiteboard-browse-error" style="display: none; color: var(--error-color, #c00);"></p>
|
|
</div>
|
|
|
|
<button type="submit">Save</button>
|
|
</form>
|
|
{% else %}
|
|
<p class="sub" style="margin-top: 10px;">Set up WebDAV credentials
|
|
in <a href="/settings">Settings</a> first to point this frame at
|
|
one of your own files.</p>
|
|
{% endif %}
|
|
</section>
|
|
</div>
|
|
|
|
<div class="side-col">
|
|
<section class="card">
|
|
<h2 class="card-title">Preview</h2>
|
|
<p class="sub">How this frame's whiteboard currently renders.</p>
|
|
<img class="preview-img" id="whiteboard-preview" alt="Whiteboard preview">
|
|
<button type="button" class="secondary" id="whiteboard-preview-refresh">Refresh now</button>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="result"></div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>window.FRAME_API = {{ ("/api/frames/" ~ frame.id) | tojson }};</script>
|
|
<script src="/static/device_status_bar.js"></script>
|
|
<script src="/static/frame_header.js"></script>
|
|
<script src="/static/frame_whiteboard.js"></script>
|
|
{% endblock %}
|