feat(display): double-sided mode — mirror one screen across the panel chain#375
Conversation
…panel chain
Renders a plugin once at a logical (per-screen) size, then tiles the
rendered frame across the full physical chain so two (or more) panels show
identical content. A 128x32 chain configured with 2 copies drives two 64x32
screens; vertical axis splits parallel outputs instead of the chain.
Plugins size themselves from matrix.width/height, so a thin _LogicalMatrix
proxy reports the logical size while delegating every real operation
(CreateFrameCanvas, SwapOnVSync, brightness, Clear) to the physical matrix —
no plugin changes required. Duplication is a single PIL paste per copy in
update_display(), so render cost is unchanged.
Config: display.double_sided { enabled, copies, axis }. Invalid config
(non-divisible dimension, bad axis/copies) logs a warning and falls back to
single-screen rather than failing to light up.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Warning Review limit reached
More reviews will be available in 42 minutes and 45 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughAdds a ChangesDouble-sided display mode
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 21 |
| Duplication | 0 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/display_manager.py (2)
58-58: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueOptional: sort
__slots__to clear the Ruff RUF023 warning.Static analysis flags the tuple ordering; reorder to silence it and keep lint clean.
♻️ Proposed change
- __slots__ = ("_matrix", "_logical_width", "_logical_height") + __slots__ = ("_logical_height", "_logical_width", "_matrix")🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/display_manager.py` at line 58, The __slots__ tuple is not sorted in alphabetical order, which triggers the Ruff RUF023 warning. Reorder the items in the __slots__ tuple that contains "_matrix", "_logical_width", and "_logical_height" to be in alphabetical order to resolve the linting warning.Source: Linters/SAST tools
60-71: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd return type hints to the new public-ish helpers.
The coding guidelines require type hints for parameters and return values.
_resolve_double_sidedreturnsOptional[Dict[str, Any]], and the_LogicalMatrixconstructor/properties are unannotated. Annotating clarifies the logical/physical contract for future maintainers.As per coding guidelines: "Use type hints for function parameters and return values".♻️ Suggested annotations
- def __init__(self, matrix, logical_width, logical_height): + def __init__(self, matrix, logical_width: int, logical_height: int) -> None: object.__setattr__(self, "_matrix", matrix) object.__setattr__(self, "_logical_width", logical_width) object.__setattr__(self, "_logical_height", logical_height) `@property` - def width(self): + def width(self) -> int: return self._logical_width `@property` - def height(self): + def height(self) -> int: return self._logical_heightdef _resolve_double_sided(physical_width: int, physical_height: int, - ds_config: Dict[str, Any]): + ds_config: Dict[str, Any]) -> Optional[Dict[str, Any]]:Also applies to: 83-84
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/display_manager.py` around lines 60 - 71, The `__init__` method of the _LogicalMatrix class is missing type hints for its parameters (matrix, logical_width, logical_height) and return type annotation, and the `width` and `height` properties are missing return type hints. Add appropriate type annotations to the `__init__` method parameters based on their expected types, add a return type annotation (None) to the `__init__` method, and add return type hints to the `width` and `height` properties to clarify the data types they return, following the project's coding guidelines that require type hints for function parameters and return values.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/display_manager.py`:
- Line 58: The __slots__ tuple is not sorted in alphabetical order, which
triggers the Ruff RUF023 warning. Reorder the items in the __slots__ tuple that
contains "_matrix", "_logical_width", and "_logical_height" to be in
alphabetical order to resolve the linting warning.
- Around line 60-71: The `__init__` method of the _LogicalMatrix class is
missing type hints for its parameters (matrix, logical_width, logical_height)
and return type annotation, and the `width` and `height` properties are missing
return type hints. Add appropriate type annotations to the `__init__` method
parameters based on their expected types, add a return type annotation (None) to
the `__init__` method, and add return type hints to the `width` and `height`
properties to clarify the data types they return, following the project's coding
guidelines that require type hints for function parameters and return values.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: acec5af0-b227-4d82-8f7f-df6f9541a46a
📒 Files selected for processing (3)
config/config.template.jsonsrc/display_manager.pytest/test_display_manager.py
Adds a Double-Sided Display section to the Display settings page (enabled checkbox, copies, horizontal/vertical axis) and wires the save handler to persist it under display.double_sided. Validates copies (2-8) and axis, returning 400 on bad input; an omitted checkbox is saved as disabled. Like the other hardware fields, changes take effect after a display restart. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses CodeRabbit nits: sort _LogicalMatrix.__slots__ (Ruff RUF023), annotate the proxy's __init__/properties/dunders and _resolve_double_sided's return type, and add docstrings to the property/dunder methods. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
What
Adds an optional double-sided mode: render a plugin once at a logical (per-screen) size, then duplicate the rendered frame across the full physical panel chain so multiple panels show identical content.
horizontal) or parallel outputs (vertical)Why it's cheap
Plugins render once at the logical size — no extra render CPU. The only per-frame cost is a single PIL
pasteper copy inupdate_display()(an ~8 KB memcpy for 64×32), so it's effectively free.How it works
Plugins size themselves from
matrix.width/matrix.height(the documented convention), so a thin_LogicalMatrixproxy reports the logical size while delegating every real operation (CreateFrameCanvas,SwapOnVSync,brightness,Clear, …) to the underlying physical matrix. No plugin changes required. The full-chain buffer is reused each frame.The web preview snapshot reads the logical image, so the dashboard shows one clean screen rather than the duplicated output.
Config
axis: "horizontal"splits the chain (panels side by side);"vertical"splitsparallel(stacked).copies, badaxis/copies) logs a warning and falls back to single-screen rather than failing to light up.Web UI
The Settings → Display page gets a Double-Sided Display section (enabled checkbox, copies, axis select). The save handler validates and persists it under
display.double_sided. Like the other hardware settings on that page, it takes effect after a display restart.Tests
TestDisplayManagerDoubleSided(6 cases): logical-dimension reporting, pixel-level horizontal and vertical tiling through the realupdate_displaypath, divisibility fallback, disabled pass-through, and brightness write forwarding through the proxy.TestConfigAPIdouble-sided cases (4): form persistence, unchecked-disables, and 400s for invalid copies/axis.test_display_manager.py+test_web_api.pypass (48/48).🤖 Generated with Claude Code