Skip to content

perf: cut redundant DOM work in the section-map poller and difficulty updates - #9

Merged
carochacs merged 2 commits into
mainfrom
claude/performance-improvements-kfow1s
Aug 17, 2026
Merged

perf: cut redundant DOM work in the section-map poller and difficulty updates#9
carochacs merged 2 commits into
mainfrom
claude/performance-improvements-kfow1s

Conversation

@carochacs

Copy link
Copy Markdown

Summary

Two verified performance fixes, no behavior change:

  1. _smUpdate() did redundant DOM work every 200ms regardless of change — it called document.getElementById('sm-marker') and _smBar.querySelectorAll('.sm-block') fresh on every 5Hz poller tick, and force-wrote style.opacity on every block on every tick even when the active section hadn't changed since the last tick. _smRender() now caches the marker element and block array once, right after building the bar (invalidated on _smRemove()); _smUpdate() reads from the cache and only flips opacity on the previous/new active block when the active index actually changes.
  2. difficulty:sections-updated handler always called a full _smRender() — every difficulty-data push from dynamic-difficulty tore down and rebuilt every section block's entire DOM purely to redraw a handful of small "glass fill" percentage bars. Added _smUpdateDifficultyFills(), which updates each glass element's fill height/title in place, falling back to a full _smRender() only if the cached blocks don't match the current section count.

Both changes preserve every plugin-spec rule from CLAUDE.md: idempotent guard untouched, screen:changed mount/unmount untouched, seeking still routed through _smSeek, section/difficulty data still read only via window.highway/the event bus, no MutationObserver/DOM polling introduced.

Bumped plugin.json version 1.2.31.2.4 (patch) per the CLAUDE.md versioning rule.

Testing

  • node -c screen.js — clean
  • tests/screen.test.js — all 16 existing tests pass unchanged
  • Manual DOM-simulation scripts (not committed) confirmed querySelectorAll/getElementById now run once per render rather than once per tick, and difficulty updates no longer trigger full innerHTML rebuilds

Generated by Claude Code

_smUpdate() ran at 5Hz and, on every single tick, did a fresh
querySelectorAll('.sm-block') + document.getElementById('sm-marker') and
then force-wrote style.opacity on every block regardless of whether the
active section had actually changed. Cache the marker/block elements in
_smRender() (rebuilt only when the section list changes, same as before)
and track the last-active index so a tick only touches the (at most two)
blocks whose highlight state actually flipped.

Separately, the 'difficulty:sections-updated' handler called a full
_smRender() on every event, tearing down and rebuilding every section
block's DOM (colors/labels/positions included) just to redraw a few
percentage "glass fill" indicators that were the only thing that changed.
Added _smUpdateDifficultyFills() to update each glass's fill height/title
in place, falling back to a full render only when the cached blocks don't
match the current sections.

Bumped plugin.json to 1.2.4 (patch) per the versioning rule in CLAUDE.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d65b9b07-138c-40b9-acb8-88d34e62cda3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Solid perf work — one gap on test coverage.

Reviewed changes

Two targeted performance optimizations to screen.js, no behavior change:

  • Poller DOM ref caching: _smRender() caches the marker element and block array once after building the bar; _smUpdate() reads from the cache and only touches the two blocks whose opacity actually changed, instead of querySelectorAll + forEach on every 200ms tick.
  • Incremental difficulty fills: _smUpdateDifficultyFills() updates glass fill height/title in place on difficulty:sections-updated events, avoiding a full innerHTML rebuild of every section block. Falls back to _smRender() when cached blocks are stale.

Both changes preserve the idempotent guard, screen:changed mount/unmount, _smSeek seeking path, and window.highway data reads. Version bump 1.2.31.2.4 per spec.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using Big Pickle (free) | 𝕏

Comment thread screen.js
…path

Pullfrog's review on #9 flagged that neither new perf-optimization code
path had test coverage: the in-place glass-fill update (vs. rebuild on
size change vs. removal), and _smUpdate's assumption that _smRender()
bakes opacity:0.5 into every block so only the flipped block needs a
touch. Add targeted tests for both, injecting fake DOM state through the
existing _setState/_getState test hooks.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

Test coverage commit (7bedc3c) directly addressing the prior review's flagged gap:

  • Opacity bake contract_smRender test asserts exactly 3 opacity:0.5 matches in the rendered HTML (one per section), catching any removal of the baked-in default that the cached highlight depends on.
  • Cached highlight path — Injected mock blockEls with specific values; _smUpdate only touches the two blocks whose active state flipped ('1' / '0.5'), untouched blocks stay undefined.
  • _smUpdateDifficultyFills branches — Three tests cover the in-place update (glass identity preserved, fill height/title updated, no insertAdjacentHTML), size-change rebuild (old glass removed, new one inserted), and stale removal (glass removed when difficulty data is absent).

Assertions are specific (identity checks, exact values, counts) rather than truthy, so each test would fail if the optimization it targets was broken.

Pullfrog  | View workflow run | Using Big Pickle (free) | 𝕏

@carochacs
carochacs merged commit 3c26694 into main Aug 17, 2026
9 checks passed
@carochacs
carochacs deleted the claude/performance-improvements-kfow1s branch August 17, 2026 21:55
Sign up for free to 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.

2 participants