Skip to content

WEBDEV-8974: Migrate the radio player search handler into elements - #85

Open
jbuckner wants to merge 2 commits into
WEBDEV-8968-migrate-transcript-viewfrom
WEBDEV-8974-migrate-search-handler
Open

WEBDEV-8974: Migrate the radio player search handler into elements#85
jbuckner wants to merge 2 commits into
WEBDEV-8968-migrate-transcript-viewfrom
WEBDEV-8974-migrate-search-handler

Conversation

@jbuckner

@jbuckner jbuckner commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

WEBDEV-8974. Stacked on #84, part of WEBDEV-8962.

Split out of the player's ticket because it's pure logic with no DOM in it, and putting the two together made a ~3,000 line PR. src/elements/ia-radio-player/ has no element in it yet — that's the next and last PR, which lands beside this.

Seven bugs came over with it, all fixed and pinned by tests that fail without the fix:

  • An empty query hung the browser. The scan loop is while ((i = haystack.indexOf(query, i + 1)) !== -1), and indexOf with an empty needle clamps to the string length and never returns -1, so the loop can't end. Confirmed in node — the index sticks at 32 for a 32-character haystack.
  • The transcript index mismeasured music entries. Spans came from displayText.length while rawText was what went into the merged string. For a music entry those differ ([Transcript unavailable] against a usually-empty rawText), so it claimed a 24-character span overlapping the entries after it.
  • The merged string was trimmed after spans were measured against the untrimmed version. A transcript opening on music starts with an empty entry, so the trim slid every span one character left and search returned nothing for the first entry.
  • A match ending on an entry's last character got the wrong end time. The span end is exclusive, so the lookup landed on the joining space, found nothing, and fell back to the entry the match started in.
  • Overlapping matches built backwards spans, and substring reverses those silently, so text came back duplicated. Only the first of an overlapping set is taken now.
  • Entries with no text vanished during a search, taking their time codes with them, so the transcript lost its music rows.
  • The full-text backend built its tags into a RegExp unescaped. The default {{{/}}} survive only because JS treats an invalid quantifier as a literal; a tag containing a bracket would throw.

Range is TextRange here — it's re-exported from the package root and shadowing the DOM's Range is a trap. TranscriptEntryRange and SearchResult are interfaces; TextRange stays a class for its length getter. The response payload types are declared rather than any.

CI shows only deploy-preview here because the PR isn't based on main (WEBDEV-8972). Verified locally: build, full suite 380 passing, eslint and prettier clean, 95% coverage on this subsystem.

Move the search subsystem out of @internetarchive/radio-player ahead of the
player itself. Pure logic, no DOM, so it reviews on its own rather than
riding along in the player's PR.

Brings over SearchHandler, TranscriptIndex, the search helper and models,
and both backends.

Bugs fixed on the way through:

- An empty query hung the browser. The scan loop is
  `while ((i = haystack.indexOf(query, i + 1)) !== -1)`, and indexOf with an
  empty needle clamps to the string length and never returns -1, so the loop
  has no way to end. Confirmed in node: the index sticks at 32 for a 32
  character haystack.
- The transcript index measured each entry's span with displayText but put
  rawText into the merged string. Those differ for a music entry, whose
  displayText is a placeholder standing in for usually-empty rawText, so it
  claimed a span two dozen characters long that overlapped the entries after
  it. Measured with rawText now.
- The merged string was trimmed after the spans had already been measured
  against the untrimmed version. A transcript opening on music starts with
  an empty entry, so the trim slid every span one character left and search
  quietly returned nothing for the first entry. Nothing is adjusted after
  measuring now.
- A match ending on an entry's last character got the wrong end time. The
  span's end is exclusive, so looking it up landed on the space between
  entries, found nothing, and fell back to the entry the match started in.
- Overlapping matches from a backend built backwards spans, and substring
  reverses those silently, so text came back duplicated. Only the first of
  an overlapping set is taken.
- Entries with no text of their own vanished from the transcript while a
  search was running, taking their time codes with them, so the view lost
  its music rows.
- The full text backend built its tags into a regular expression without
  escaping. The default {{{ and }}} survive only because an invalid
  quantifier is treated literally, and a tag holding a bracket would throw.

Range is TextRange here, since Range is a DOM global and this is re-exported
from the package root. TranscriptEntryRange and SearchResult are interfaces;
TextRange stays a class for its length getter. The response payload types
are declared rather than any.

Claude-Session: https://claude.ai/code/session_01U6iNyGicKJywdrWJMTyFin

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1

🚀 View preview at
https://internetarchive.github.io/elements/pr/pr-85/

Built to branch ghpages at 2026-08-27 20:29 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

…igrate-search-handler

* WEBDEV-8968-migrate-transcript-view:
  WEBDEV-8972: Run CI on every PR, not just ones based on main (#90)
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.45455% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.76%. Comparing base (db2a347) to head (111fbd4).

Files with missing lines Patch % Lines
...layer/search/backends/full-text-search-response.ts 87.50% 2 Missing and 1 partial ⚠️
.../elements/ia-radio-player/search/search-handler.ts 94.11% 0 Missing and 3 partials ⚠️
Additional details and impacted files
@@                           Coverage Diff                           @@
##           WEBDEV-8968-migrate-transcript-view      #85      +/-   ##
=======================================================================
+ Coverage                                81.58%   82.76%   +1.17%     
=======================================================================
  Files                                       46       53       +7     
  Lines                                     1423     1555     +132     
  Branches                                   319      336      +17     
=======================================================================
+ Hits                                      1161     1287     +126     
- Misses                                     171      173       +2     
- Partials                                    91       95       +4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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