Two outstanding CodeRabbit outside-diff findings on this PR:
- The divider branch applied its 0.5px centering offset after scaling
(`ay * s + 0.5`) instead of before it (`(ay + 0.5) * s`), so at SCALE>1
the stroke bled into the preceding LED row/column instead of straddling
its own.
- A small imported gauge with a wide lineWidth (e.g. width=1, height=1,
lineWidth=3) produced a negative radius, which ctx.ellipse() throws
IndexSizeError on, aborting render() for every element still to be
drawn. Radii are now clamped to zero.
Verified both against current code (neither was fixed by later commits
on this branch) and mutation-checked: reverting either fix fails the new
test.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
composer_bp defined the whole Plugin Composer feature -- the /composer/
page and all its API routes -- but web_interface/app.py never imported or
registered it, so every composer URL 404'd in the running app (every
composer test builds its own minimal Flask app and registers the
blueprint directly, which is why this went unnoticed). Wire it up the
same way pages_v3/api_v3 are: import, set config_manager/plugin_manager/
plugins_dir/project_root, register_blueprint(url_prefix='/composer') --
matching the prefix composer.html and composer-app.js already hardcode.
Also closes the other still-open half of a CodeRabbit finding: manager.py.j2
already guards element types the template has no branch for, but a
dynamic_text element with binding.source other than 'config' hit the same
empty-if-block bug one level deeper (its own inner if produced nothing).
Added the same pass fallback.
Verified both against current code before fixing; the other 8 findings
from that review were already fixed in earlier commits on this branch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>