fix(vegas): stop capping plugin width by default

Vegas plugins read as "cut off early" or "starting in the middle". That
was the per-plugin width budget, not the scroll engine:
overflow_mode=rotate is designed to resume mid-content on each
appearance, so the symptom was the feature working as specified.

Measured over a 17-plugin fleet on a 512px panel, the cap was a bad
trade. Only four plugins were ever wide enough to hit the 3.0 default --
leaderboard 11,518px, news 10,021px, odds-ticker 4,643px, hockey
1,508px. Weather is 650px and flights 512px; the cap never touched them
or the other eleven. So it bought nothing on thirteen plugins while
costing two visible faults on four: content entering mid-item (a news
ticker started at column 6027 of its own strip), and a final rotation
window of whatever happened to be left -- 348px of an 1,840px stocks
ticker, seven seconds of panel time.

Default max_plugin_width_ratio to 0 (uncapped), so every plugin
contributes all of its content and is always entered at its beginning.
The cap remains available, and vegas_max_width_screens still caps an
individual plugin -- which is where the knob belongs, since a genuinely
long ticker is a property of that plugin rather than of the fleet.

Verified on a live 512px device: 327 budget crops in the preceding six
hours, none after.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Udr6MfaFLUPhX5Fgo67Jf5
This commit is contained in:
ChuckBuilds
2026-08-07 16:05:28 -04:00
co-authored by Claude Opus 5
parent fc25a70d75
commit e0d62a9e54
5 changed files with 27 additions and 9 deletions
+1 -1
View File
@@ -149,7 +149,7 @@
"min_plugin_width": 8, "min_plugin_width": 8,
"lead_in_width": 0, "lead_in_width": 0,
"plugins_per_cycle": 6, "plugins_per_cycle": 6,
"max_plugin_width_ratio": 3.0, "max_plugin_width_ratio": 0.0,
"overflow_mode": "rotate", "overflow_mode": "rotate",
"dynamic_duration_enabled": true, "dynamic_duration_enabled": true,
"min_cycle_duration": 60, "min_cycle_duration": 60,
+1 -1
View File
@@ -127,7 +127,7 @@ Read by `src/vegas_mode/config.py` (`VegasScrollConfig.from_config`). See
| `min_plugin_width` | int, `8` | | `min_plugin_width` | int, `8` |
| `lead_in_width` | int, `0` | | `lead_in_width` | int, `0` |
| `plugins_per_cycle` | int, `6` | | `plugins_per_cycle` | int, `6` |
| `max_plugin_width_ratio` | float, `3.0` | | `max_plugin_width_ratio` | float, `0.0` |
| `overflow_mode` | string, `"rotate"` | | `overflow_mode` | string, `"rotate"` |
| `dynamic_duration_enabled` | bool, `true` | | `dynamic_duration_enabled` | bool, `true` |
| `min_cycle_duration` | int, `60` | | `min_cycle_duration` | int, `60` |
+17 -5
View File
@@ -104,10 +104,22 @@ class VegasModeConfig:
overflow_mode: str = "rotate" overflow_mode: str = "rotate"
# Cap on one plugin's share of a cycle, as a multiple of display width. # Cap on one plugin's share of a cycle, as a multiple of display width.
# A single ticker returning 7,000px would otherwise hold the panel for over # 0 (the default) disables the cap, so every plugin contributes all of its
# two minutes. Overflow is deferred to later cycles rather than discarded. # content and is always entered at its beginning.
# 0 disables the cap. #
max_plugin_width_ratio: float = 3.0 # Capping was the default until it proved to cost more than it bought.
# Measured over a 17-plugin fleet on a 512px panel, only four plugins were
# ever wide enough to hit a 3.0 cap; for those four it produced two visible
# faults. Content resumed mid-item on each appearance (a news ticker entered
# at column 6027 of its own strip), and the final window of a rotation was
# whatever happened to be left — 348px of a 1840px stocks ticker, seven
# seconds of panel time. Both read as the display being broken rather than
# as deferral working.
#
# A wide plugin does hold the panel for a long time uncapped: set the cap
# per plugin with vegas_max_width_screens where that matters, rather than
# globally where it mostly hurts plugins that were never the problem.
max_plugin_width_ratio: float = 0.0
# Plugin management # Plugin management
plugin_order: List[str] = field(default_factory=list) plugin_order: List[str] = field(default_factory=list)
@@ -159,7 +171,7 @@ class VegasModeConfig:
lead_in_width=int(vegas_config.get('lead_in_width', 0)), lead_in_width=int(vegas_config.get('lead_in_width', 0)),
plugins_per_cycle=int(vegas_config.get('plugins_per_cycle', 6)), plugins_per_cycle=int(vegas_config.get('plugins_per_cycle', 6)),
max_plugin_width_ratio=float( max_plugin_width_ratio=float(
vegas_config.get('max_plugin_width_ratio', 3.0)), vegas_config.get('max_plugin_width_ratio', 0.0)),
overflow_mode=str(vegas_config.get('overflow_mode', 'rotate')), overflow_mode=str(vegas_config.get('overflow_mode', 'rotate')),
plugin_order=list(vegas_config.get('plugin_order', [])), plugin_order=list(vegas_config.get('plugin_order', [])),
excluded_plugins=set(vegas_config.get('excluded_plugins', [])), excluded_plugins=set(vegas_config.get('excluded_plugins', [])),
+6
View File
@@ -781,6 +781,12 @@ class TestNewConfigKeys:
assert cfg.render_width_pct == 100 assert cfg.render_width_pct == 100
assert cfg.min_content_separation == 24 assert cfg.min_content_separation == 24
def test_width_cap_is_off_by_default(self):
# Capping made wide plugins resume mid-content on every appearance and
# emit runt final windows; it is now opt-in per plugin instead.
assert VegasModeConfig().max_plugin_width_ratio == 0.0
assert VegasModeConfig.from_config({}).max_plugin_width_ratio == 0.0
@pytest.mark.parametrize('overrides,bad_key', [ @pytest.mark.parametrize('overrides,bad_key', [
({'render_width_pct': 5}, 'render_width_pct'), ({'render_width_pct': 5}, 'render_width_pct'),
({'render_width_pct': 101}, 'render_width_pct'), ({'render_width_pct': 101}, 'render_width_pct'),
@@ -556,11 +556,11 @@
</div> </div>
<div class="form-group" id="setting-display-vegas_max_plugin_width_ratio" data-setting-key="display.vegas_scroll.max_plugin_width_ratio"> <div class="form-group" id="setting-display-vegas_max_plugin_width_ratio" data-setting-key="display.vegas_scroll.max_plugin_width_ratio">
<label for="vegas_max_plugin_width_ratio" class="block text-sm font-medium text-gray-700">Max Plugin Width (screens){{ ui.help_tip('Caps how much of one cycle a single plugin may occupy, measured in screen widths (020).\nDefault: 3. A long ticker such as a news feed or leaderboard is trimmed to this and the remainder shown on later cycles, so one plugin cannot hold the display for minutes. Set 0 for no limit.', 'Max Plugin Width') }}</label> <label for="vegas_max_plugin_width_ratio" class="block text-sm font-medium text-gray-700">Max Plugin Width (screens){{ ui.help_tip('Caps how much of one cycle a single plugin may occupy, measured in screen widths (020).\nDefault: 0 (no limit) — every plugin shows all of its content and always starts at the beginning.\nSet a limit to stop one long ticker holding the display for minutes: it is cut to this width and the remainder shown on later cycles. The trade-off is that such a plugin then resumes mid-content on each appearance instead of starting fresh.', 'Max Plugin Width') }}</label>
<input type="number" <input type="number"
id="vegas_max_plugin_width_ratio" id="vegas_max_plugin_width_ratio"
name="vegas_max_plugin_width_ratio" name="vegas_max_plugin_width_ratio"
value="{{ main_config.display.get('vegas_scroll', {}).get('max_plugin_width_ratio', 3.0) }}" value="{{ main_config.display.get('vegas_scroll', {}).get('max_plugin_width_ratio', 0.0) }}"
min="0" min="0"
max="20" max="20"
step="0.5" step="0.5"