Skip to content

Feature/vegas scroll mode - #214

Closed
ChuckBuilds wants to merge 2 commits into
mainfrom
feature/vegas-scroll-mode
Closed

Feature/vegas scroll mode#214
ChuckBuilds wants to merge 2 commits into
mainfrom
feature/vegas-scroll-mode

Conversation

@ChuckBuilds

@ChuckBuildsChuckBuilds commented Jan 28, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced Vegas Scroll Mode: enables continuous scrolling display with configurable speed, FPS, and buffer management.
    • Added 14+ new web UI widgets: color picker, date picker, day selector, email/password/text/number/URL inputs, radio group, slider, toggle switch, textarea, schedule picker, and time range controls for improved user interactions.
  • Refactor

    • Redesigned schedule and plugin configuration UI to use widget-based rendering for consistency.

✏️ Tip: You can customize this high-level summary in your review settings.

Chuckand others added 2 commits January 27, 2026 10:37
Add 15 new reusable widgets following the widget registry pattern:
- schedule-picker: composite widget for enable/mode/time configuration
- day-selector: checkbox group for days of the week
- time-range: paired start/end time inputs with validation
- text-input, number-input, textarea: enhanced text inputs
- toggle-switch, radio-group, select-dropdown: selection widgets
- slider, color-picker, date-picker: specialized inputs
- email-input, url-input, password-input: validated string inputs
Refactor schedule.html to use the new schedule-picker widget instead
of inline JavaScript. Add x-widget support in plugin_config.html for
all new widgets so plugins can use them via schema configuration.
Fix form submission for checkboxes by using hidden input pattern to
ensure unchecked state is properly sent via JSON-encoded forms.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement an opt-in Vegas ticker mode that composes all enabled plugin
content into a single continuous horizontal scroll. Includes a modular
package (src/vegas_mode/) with double-buffered streaming, 125 FPS
render pipeline using the existing ScrollHelper, live priority
interruption support, and a web UI for configuration with drag-drop
plugin ordering.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jan 28, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Introduces a comprehensive Vegas mode feature (continuous scrolling display) with new configuration, coordination, content adaptation, and rendering systems. Integrates Vegas mode into the display controller loop, adds plugin hooks for Vegas content, updates the configuration schema, implements web API support for Vegas settings, and introduces a new frontend widget system with 13+ specialized input components and template updates for Vegas UI configuration.

Changes

Cohort / File(s)Summary
Core Vegas Mode Implementation
src/vegas_mode/config.py, src/vegas_mode/coordinator.py, src/vegas_mode/plugin_adapter.py, src/vegas_mode/render_pipeline.py, src/vegas_mode/stream_manager.py, src/vegas_mode/__init__.py
Introduces a complete Vegas scroll mode package with configuration model, coordinator orchestration, plugin content adaptation, high-performance render pipeline with double-buffering and cycle management, dual-buffer stream manager with look-ahead prefetching, and package exports. Dense logic with state management, buffering, error handling, and timing controls (~2K lines).
Display Controller Integration
src/display_controller.py
Adds lazy-loading Vegas mode initialization, Vegas mode activation checks, and Vegas iteration branch in the main display loop with fallback to normal rotation on failure. Includes schedule check behavior tweak treating empty schedules as always active.
Plugin System Extensions
src/plugin_system/base_plugin.py
Adds two new public methods: get_vegas_content() for plugins to return PIL Images for Vegas mode, and get_vegas_content_type() to indicate content type ('static', 'multi', 'none').
Configuration Schema
config/config.template.json
Adds new display.vegas_scroll block with enabled, scroll_speed, separator_width, plugin_order, excluded_plugins, target_fps, and buffer_ahead fields.
Web API Support
web_interface/blueprints/api_v3.py
Extends save_main_config() to parse and serialize Vegas scroll settings from request data, including integer conversion for numeric fields and JSON handling for plugin arrays.
Frontend Widget System
web_interface/static/v3/js/widgets/color-picker.js, ...date-picker.js, ...day-selector.js, ...email-input.js, ...number-input.js, ...password-input.js, ...radio-group.js, ...select-dropdown.js, ...slider.js, ...text-input.js, ...textarea.js, ...time-range.js, ...toggle-switch.js, ...url-input.js
Introduces 14 new reusable form input widgets for LEDMatrixWidgets framework, each with render, getValue/setValue, validation, and change handlers. Supports various input types (text, number, color, date, time, select, etc.) with optional icons, validation feedback, and UI state management.
Template Updates
web_interface/templates/v3/base.html, web_interface/templates/v3/partials/display.html, web_interface/templates/v3/partials/plugin_config.html, web_interface/templates/v3/partials/schedule.html
Adds Vegas Scroll Mode UI section with scrolling speed, separator width, target FPS, buffer controls, and drag-and-drop plugin ordering. Integrates new widget system into plugin config and schedule templates; adds duplicate widget script imports and replaces inline schedule UI with widget-based rendering.

Sequence Diagram(s)

sequenceDiagram
participant DisplayCtrl as Display Controller
participant VegasCoord as Vegas Coordinator
participant StreamMgr as Stream Manager
participant PluginAdpt as Plugin Adapter
participant RenderPipe as Render Pipeline
participant Display as Display Device
DisplayCtrl->>VegasCoord: initialize(config, display_manager, plugin_manager)
VegasCoord->>StreamMgr: __init__(config, plugin_manager, plugin_adapter)
StreamMgr->>PluginAdpt: fetch initial plugin content
PluginAdpt-->>StreamMgr: buffered images
StreamMgr-->>VegasCoord: ready
VegasCoord->>RenderPipe: __init__(config, display_manager, stream_manager)
RenderPipe-->>VegasCoord: ready
loop Vegas Mode Active Loop
DisplayCtrl->>VegasCoord: run_iteration()
VegasCoord->>RenderPipe: should_recompose()
alt Need Content Update
RenderPipe->>StreamMgr: get_all_content_for_composition()
StreamMgr-->>RenderPipe: buffered segments
RenderPipe->>RenderPipe: compose_scroll_content()
end
loop Per-Frame Rendering (target FPS)
RenderPipe->>RenderPipe: render_frame()
RenderPipe->>RenderPipe: update scroll position
RenderPipe->>Display: render visible frame
end
alt Cycle Complete
RenderPipe->>StreamMgr: prefetch next content
PluginAdpt->>PluginAdpt: get_vegas_content() per plugin
StreamMgr-->>RenderPipe: next cycle content ready
RenderPipe->>RenderPipe: start_new_cycle()
end
end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~110 minutes


Possibly related PRs

Poem

🐰 Hop, scroll, and spin with Vegas flair,
Content streams through buffers with care,
Widgets dance across the web UI bright,
Cycling colors, from morning to night,
A scrolling spectacle, pure delight!

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ChuckBuilds
ChuckBuilds deleted the feature/vegas-scroll-mode branch January 28, 2026 13:26
ChuckBuilds pushed a commit that referenced this pull request May 24, 2026
py/flask-debug (#214):
- debug_web_manual.py: read debug mode from LEDMATRIX_FLASK_DEBUG env var
instead of hardcoded True
py/stack-trace-exposure (#216, #218):
- api_v3.py execute_system_action: remove subprocess stdout/stderr from
HTTP responses; log via logger instead
- api_v3.py get_git_version: validate output matches safe ref format
(^[a-zA-Z0-9._-]+$) before including in response
- api_v3.py: remove all remaining traceback.format_exc() dead variables
and print() debug calls (replaced with logger.debug/warning)
py/reflective-xss (#207, #208, #209, #210, #211, #212):
- api_v3.py: remove plugin_id from all error/success response messages
(uninstall, install, update, health, not-found responses)
- pages_v3.py load_partial: return static "Partial not found" message
instead of echoing partial_name
- pages_v3.py _load_starlark_config_partial: add app_id regex validation,
use static error messages instead of f-strings with app_id
py/path-injection (#187#206):
- pages_v3.py _load_plugin_config_partial: resolve plugins_base and
validate _plugin_dir with relative_to() before all file operations;
same for assets metadata directory
- pages_v3.py _load_starlark_config_partial: resolve starlark_base and
validate schema_file/config_file paths with relative_to()
- plugin_loader.py _find_plugin_directory: resolve plugins_dir and
validate strategy-2 candidates with relative_to()
- plugin_loader.py install_dependencies: resolve plugin_dir first, then
construct requirements_file and marker_path from resolved base
- plugin_loader.py load_module: resolve plugin_dir with strict=True and
validate entry_file with relative_to() before exec_module
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ChuckBuilds added a commit that referenced this pull request May 24, 2026
…ss install handler warning (#346)
* fix(web-ui): fix quick actions not firing, add toast feedback, suppress install handler warning
- base.html: add htmx:afterSettle listener to set data-loaded on tab
containers after HTMX swaps their content, preventing the overview
partial from being re-fetched (and handlers lost) on every tab switch
- base.html: call htmx.process() in loadOverviewDirect/loadPluginsDirect
fallbacks so buttons get HTMX handlers even if HTMX finished its
initial body scan before the fallback fetch completed
- overview.html + index.html (11 buttons): replace event.detail.xhr.responseJSON
(undefined in HTMX 1.9.x) with JSON.parse(event.detail.xhr.responseText)
so quick action toast notifications actually fire
- plugins_manager.js: add guarded htmx:afterSettle listener that only calls
attachInstallButtonHandler when #install-plugin-from-url is in the DOM,
eliminating the spurious console warning on non-plugin tab loads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(web-ui): ensure quick-action toasts always fire even on xhr/parse failure
Replace silent catch(e){} in all 11 hx-on:htmx:after-request handlers with a
pattern that sets default message/status before the try block and calls
showNotification(m,s) unconditionally after it, so a fallback toast is shown
whenever xhr is absent or responseText is not valid JSON.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(web-ui): show error toast on non-JSON 4xx/5xx quick-action responses
In the catch block of all 11 hx-on:htmx:after-request handlers, check
xhr.status >= 400 and downgrade s to 'error' so a failed action that
returns an HTML error page (or other non-JSON body) surfaces as an error
toast instead of the optimistic 'success'/'info' default.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(web-ui): guard setTimeout fallback for attachInstallButtonHandler
The 500ms fallback setTimeout was calling attachInstallButtonHandler()
unconditionally even when the plugins partial wasn't in the DOM, causing
a spurious console.warn on every page load. Add the same element-existence
check already present on the htmx:afterSettle listener.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix backup API 404s, hardware status 500, and HTMX loading race
- Add all backup API routes to api_v3.py: preview, list, export,
validate, restore (with plugin reinstall), download, delete
- Fix PermissionError on /hardware/status: return graceful 200 instead
of 500 when the status file is owned by a different user; also fix
root cause by writing the file world-readable (0o644) in display_manager
- Fix HTMX race: dispatch htmx:ready window event from HTMX onload
callback; loadTabContent now waits for that event instead of
immediately falling back to direct fetch (eliminating the
"HTMX not available" console warning on initial load)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Cancel HTMX fallback timers when htmx:ready fires
The 5-second setTimeout fallbacks for plugins and overview were firing
before the htmx:ready event arrived, logging spurious warnings. Each
timer now self-cancels via htmx:ready so the fallback only triggers
when HTMX genuinely fails to load.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Address review feedback: error leaks, ok:false, htmx:ready coverage
- Backup endpoints: replace raw str(e) in user-facing responses with a
generic message; full exception still logged via exc_info=True
- hardware/status: change ok:null to ok:false for PermissionError and
json.JSONDecodeError so the UI's hw.ok===false check triggers correctly
- base.html: dispatch htmx:ready from the fallback load path so any
deferred listeners fire on CDN-fallback loads too
- loadTabContent: also listen for htmx-load-failed so overview/wifi/plugins
fall back to direct fetch when HTMX is completely unavailable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Treat system-managed pip packages as satisfied for dependency marker
When a plugin's requirements.txt includes a package installed via the
system package manager (dnf/apt), pip fails with 'uninstall-no-record-file'
because it can't replace the system-tracked copy. The package is present
and functional, but the missing marker caused the install to be retried
on every service restart.
Detect this specific error pattern: if the only pip failure is
uninstall-no-record-file, write the .dependencies_installed marker and
log a warning instead of returning False, suppressing the repeated warning.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix uninstall-no-record-file detection condition
The previous check used a string replacement that left 'error:' in the
remaining text, causing the condition to always evaluate false. Simplify
to a direct substring check: if 'uninstall-no-record-file' appears in pip
stderr the affected package is installed at the system level and we write
the marker, suppressing the repeated warning on every restart.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Resolve CodeQL security findings in backup API
Path traversal (CWE-22):
- backup_download: switch from send_file(user-tainted-path) to
send_from_directory(_BACKUP_EXPORT_DIR, filename); Flask uses
werkzeug safe_join internally which CodeQL recognises as a sanitizer
- backup_delete: enumerate the export directory and match by name so
entry.unlink() operates on a filesystem-derived Path rather than one
constructed from user input; _safe_backup_path still guards first
Information exposure through exceptions (CWE-209):
- backup_validate: err_msg from validate_backup() can embed exception
strings containing temp-file paths; log the detail, return a generic
'Invalid or corrupted backup file' to the client
- Other backup endpoints: already fixed (str(e) -> generic message);
CodeQL alerts will clear on next scan
plugin_loader.py:185 (path traversal): false positive — requirements_file
is constructed from plugin_dir returned by find_plugin_directory() (a
filesystem scan), not from raw HTTP request input; no change needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix pre-existing information exposure in version and action endpoints
- get_system_version (alert #218): replaced str(e) with generic message;
exception still logged via logger.error(exc_info=True)
- execute_system_action (alert #216): removed str(e) and full
traceback.format_exc() from the HTTP response — the full stack trace
was being sent directly to clients; replaced with generic message and
proper logger.error call
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix remaining GitHub CodeQL security alerts
- py/stack-trace-exposure: Remove str(e) and traceback.format_exc() from
all HTTP responses across api_v3.py, pages_v3.py, and app.py; replace
with generic messages and logger.error(exc_info=True)
- py/reflective-xss: Escape partial_name via markupsafe.escape in the
load_partial 404 response
- py/path-injection: Add regex validation of plugin_id before filesystem
use in _load_plugin_config_partial
- py/incomplete-url-substring-sanitization: Replace 'github.com' in
substring checks with urlparse hostname comparison in store_manager.py
- py/clear-text-logging-sensitive-data: Remove football-scoreboard debug
prints and sensitive request-body prints from update endpoint
- js/bad-tag-filter: Replace script-only regex in BaseWidget.sanitizeValue
with DOM-based textContent stripping that removes all HTML
- js/incomplete-sanitization: Fix escapeAttr to properly encode &, ", ',
<, > using HTML entities instead of backslash escaping
- js/prototype-pollution-utility: Add __proto__/constructor/prototype
key guards to deepMerge function in plugins_manager.js
- app.py error handlers: Always return generic messages; remove debug-mode
branches that could expose tracebacks in production
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix three remaining CodeQL path-injection and info-exposure alerts
- plugin_loader.py: resolve plugin_dir with strict=True and validate
marker_path with relative_to() before any filesystem writes, giving
CodeQL the positive sanitization pattern it requires (py/path-injection)
- api_v3.py _safe_backup_path: replace substring negative checks with a
strict positive regex (^[a-zA-Z0-9][a-zA-Z0-9._-]{0,200}\.zip$) that
CodeQL recognises as sanitising the user-supplied filename
(py/path-injection)
- api_v3.py backup_validate: whitelist known-safe manifest fields before
returning JSON, preventing any exception strings captured inside
validate_backup() from reaching the HTTP response (py/stack-trace-exposure)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Resolve 29 open CodeQL security alerts across 5 files
py/flask-debug (#214):
- debug_web_manual.py: read debug mode from LEDMATRIX_FLASK_DEBUG env var
instead of hardcoded True
py/stack-trace-exposure (#216, #218):
- api_v3.py execute_system_action: remove subprocess stdout/stderr from
HTTP responses; log via logger instead
- api_v3.py get_git_version: validate output matches safe ref format
(^[a-zA-Z0-9._-]+$) before including in response
- api_v3.py: remove all remaining traceback.format_exc() dead variables
and print() debug calls (replaced with logger.debug/warning)
py/reflective-xss (#207, #208, #209, #210, #211, #212):
- api_v3.py: remove plugin_id from all error/success response messages
(uninstall, install, update, health, not-found responses)
- pages_v3.py load_partial: return static "Partial not found" message
instead of echoing partial_name
- pages_v3.py _load_starlark_config_partial: add app_id regex validation,
use static error messages instead of f-strings with app_id
py/path-injection (#187#206):
- pages_v3.py _load_plugin_config_partial: resolve plugins_base and
validate _plugin_dir with relative_to() before all file operations;
same for assets metadata directory
- pages_v3.py _load_starlark_config_partial: resolve starlark_base and
validate schema_file/config_file paths with relative_to()
- plugin_loader.py _find_plugin_directory: resolve plugins_dir and
validate strategy-2 candidates with relative_to()
- plugin_loader.py install_dependencies: resolve plugin_dir first, then
construct requirements_file and marker_path from resolved base
- plugin_loader.py load_module: resolve plugin_dir with strict=True and
validate entry_file with relative_to() before exec_module
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix 15 remaining CodeQL path-injection and stack-trace-exposure alerts
Switch from resolve()+relative_to() to os.path.basename() reassignment,
which CodeQL recognizes as a path sanitizer that breaks the taint chain.
Also remove exception objects from backup_manager validate_backup return
strings to eliminate the stack-trace-exposure taint source.
Fixes alerts #227, #233, #234, #235, #237, #238, #239, #240, #241,
#242, #243, #244, #245, #246, #247.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix broken logger format string and leaked exception in config save error
- pages_v3.py: plain string was used instead of %-style substitution,
so every manifest-read failure logged the literal "{plugin_id}"
- api_v3.py save_main_config: exception message was still leaking
through the error response; replace with generic message (consistent
with the rest of the CodeQL sweep in this PR)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Chuck <chuck@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@ChuckBuilds