feat(vegas): live elements update in place while they scroll

One background worker (src/vegas_mode/live_worker.py) redraws a plugin's
live elements when its data epoch moves on (update listener) or on their
refresh_hz, nearest the screen first, and hands changed pixels lock-free to
the render thread, which copies them into the strip between frames
(RenderPipeline.apply_live_patches, ScrollHelper.patch_columns): at most
four patches or two screens of bytes a frame, no drawing or locks there.
The worker takes over group prefetch once a live element is placed, runs
inside the render gate, and is supervised. Update tick 1s while live
elements exist. Web UI switch for live_refresh. OFFSCREEN_RENDERING.md
describes what was built and why SegmentStrip was not needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-09-30 18:36:39 -04:00
co-authored by Claude Opus 5.5
parent 701c220ad0
commit 876130e93f
23 changed files with 2540 additions and 240 deletions
+8 -7
View File
@@ -67,7 +67,7 @@ def _as_vegas_canvas(plugin: Any, display_manager: Any, width: int) -> Iterator[
def render_vegas_elements(plugin: Any, display_manager: Any,
width: Optional[int] = None) -> Optional[list]:
width: Optional[int] = None) -> Any:
"""Call ``plugin.get_vegas_elements()`` as the Vegas ticker does."""
render_width = int(width or display_manager.width)
with _as_vegas_canvas(plugin, display_manager, render_width):
@@ -151,8 +151,9 @@ def check_vegas_elements(plugin: Any, display_manager: Any) -> VegasElementRepor
report.errors.append(f"{where} appears twice; keys must be unique")
continue
seen.add(key)
if not isinstance(element.image, Image.Image):
report.errors.append(f"{where} image is a {type(element.image).__name__}")
image: Any = element.image # typed Image, but a plugin may pass anything
if not isinstance(image, Image.Image):
report.errors.append(f"{where} image is a {type(image).__name__}")
continue
if element.image.height != height:
report.errors.append(
@@ -296,12 +297,12 @@ def check_plugin_vegas_elements(plugin_id: str, plugin_dir: Any, config: dict,
if run_update:
try:
plugin.update()
except _TOLERATED_UPDATE_ERRORS as exc:
except Exception as exc: # noqa: BLE001 - a plugin's update can raise anything
if not isinstance(exc, _TOLERATED_UPDATE_ERRORS):
report.errors.append(f"update() raised {exc!r}")
return report
report.warnings.append(f"update() had no network ({exc!r}); checked "
"with whatever data the plugin starts with")
except Exception as exc: # noqa: BLE001 - a plugin's update can raise anything
report.errors.append(f"update() raised {exc!r}")
return report
checked = check_vegas_elements(plugin, display_manager)
checked.warnings[:0] = report.warnings
return checked