mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-01 16:58:06 +00:00
The durations grid looped over display.display_durations, which nothing has
ever populated (verified {} on a real production install) - so the page
rendered no duration fields at all. Worse, its inputs posted bare mode
names, which save_main_config's endswith('_duration') filter silently
dropped: the page was broken in both directions, unnoticed because it was
also unreachable (previous commit).
- pages_v3._load_durations_partial now builds one entry per display mode of
every ENABLED plugin via plugin_manager.get_plugin_display_modes()
(falling back to the plugin id), overlaid with saved values, defaulting
to the display controller's 30s. Grouped per plugin, sorted by name.
Saved keys not owned by any enabled plugin stay visible under "Other
saved entries" instead of vanishing.
- durations.html renders the grouped inputs, named duration__<mode_key>
(mode keys are arbitrary, so they can't use the *_duration suffix
convention), with an explanatory empty state when no plugins are enabled.
- api_v3.save_main_config accepts the new duration__<mode> fields and
writes them into display.display_durations under the bare mode key -
exactly what the display controller reads
(display_durations.get(mode_key, 30)).
Validation: py_compile both blueprints; Jinja render with 3 groups asserts
grouped inputs, saved-value overlay, stale-entry group, empty state, and
div balance.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ
100 lines
4.9 KiB
HTML
100 lines
4.9 KiB
HTML
{% import 'v3/partials/_macros.html' as ui %}
|
||
<div class="bg-white rounded-lg shadow p-6">
|
||
<div class="border-b border-gray-200 pb-4 mb-6">
|
||
<h2 class="text-lg font-semibold text-gray-900">Rotation & Durations</h2>
|
||
<p class="mt-1 text-sm text-gray-600">Set the order plugins rotate on the display and how long each screen is shown. Durations are in seconds.</p>
|
||
</div>
|
||
|
||
{{ ui.settings_filter() }}
|
||
|
||
<form hx-post="/api/v3/config/main"
|
||
hx-ext="json-enc"
|
||
hx-headers='{"Content-Type": "application/json"}'
|
||
hx-swap="none"
|
||
hx-on:htmx:after-request="showNotification(event.detail.xhr.responseJSON?.message || 'Durations saved', event.detail.xhr.responseJSON?.status || 'success')"
|
||
class="space-y-6"
|
||
novalidate
|
||
onsubmit="fixInvalidNumberInputs(this); return true;">
|
||
|
||
<!-- Primary rotation order: drag to reorder which plugin shows first,
|
||
second, ... in the normal display rotation. Saved as
|
||
display.plugin_rotation_order and applied by the display
|
||
controller on startup and live plugin enable/disable. -->
|
||
<div class="bg-gray-50 rounded-lg p-4">
|
||
<h3 class="text-md font-medium text-gray-900 mb-1">Rotation Order</h3>
|
||
<p class="text-sm text-gray-600 mb-3">Drag plugins to set the order they rotate on the display. Each plugin's screens keep their own order within its turn. Takes effect after saving and restarting the display.</p>
|
||
<div id="rotation_plugin_order" class="space-y-2 bg-white rounded-lg p-3 border border-gray-200">
|
||
<p class="text-sm text-gray-500 italic">Loading plugins…</p>
|
||
</div>
|
||
<input type="hidden" id="rotation_plugin_order_value" name="plugin_rotation_order"
|
||
value='{{ main_config.display.get("plugin_rotation_order", [])|tojson }}'>
|
||
</div>
|
||
|
||
{% if duration_groups %}
|
||
<div class="bg-gray-50 rounded-lg p-4 space-y-5">
|
||
<div>
|
||
<h3 class="text-md font-medium text-gray-900 mb-1">Screen Durations</h3>
|
||
<p class="text-sm text-gray-600">How long each screen stays on before rotating to the next one, in seconds (5–600, default 30).</p>
|
||
</div>
|
||
{% for group in duration_groups %}
|
||
<div>
|
||
<h4 class="text-sm font-semibold text-gray-800 mb-2">{{ group.plugin_name }}</h4>
|
||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||
{% for mode in group.modes %}
|
||
<div class="form-group" id="setting-durations-{{ mode.key }}" data-setting-key="display.display_durations.{{ mode.key }}">
|
||
<label for="duration__{{ mode.key }}" class="block text-sm font-medium text-gray-700">
|
||
{{ mode.key | replace('_', ' ') | title }}{{ ui.help_tip('How long the ' ~ (mode.key | replace('_', ' ')) ~ ' screen stays on before rotating to the next one, in seconds.\nRange: 5–600. Currently ' ~ mode.value ~ 's.', mode.key | replace('_', ' ') | title) }}
|
||
</label>
|
||
<input type="number"
|
||
id="duration__{{ mode.key }}"
|
||
name="duration__{{ mode.key }}"
|
||
value="{{ mode.value }}"
|
||
min="5"
|
||
max="600"
|
||
class="form-control">
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% else %}
|
||
<div class="bg-gray-50 rounded-lg p-4">
|
||
<p class="text-sm text-gray-500 italic">No enabled plugins found — enable a plugin in the Plugin Manager to set its screen durations here.</p>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Submit Button -->
|
||
<div class="flex justify-end">
|
||
<button type="submit"
|
||
class="btn bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md">
|
||
<i class="fas fa-save mr-2"></i>
|
||
Save Durations
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
// Shared drag-and-drop plugin list (static/v3/js/widgets/plugin-order-list.js,
|
||
// same module the Vegas Scroll section uses).
|
||
function initRotationOrderList() {
|
||
if (!document.getElementById('rotation_plugin_order')) return;
|
||
if (!window.PluginOrderList) {
|
||
setTimeout(initRotationOrderList, 100);
|
||
return;
|
||
}
|
||
window.PluginOrderList.init({
|
||
containerId: 'rotation_plugin_order',
|
||
orderInputId: 'rotation_plugin_order_value'
|
||
});
|
||
}
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', initRotationOrderList);
|
||
} else {
|
||
initRotationOrderList();
|
||
}
|
||
}());
|
||
</script>
|