Commit Graph

881 Commits

Author SHA1 Message Date
Timo 096775d39f fix(extension): make anime-style nested players selectable again
Reproduced against the live yummyanime.tv layout, which is:

  top (no video)
  ├── visible same-origin wrapper 830x498 -> cross-origin player
  ├── hidden same-origin wrapper    0x0   -> cross-origin mirror
  └── hidden cross-origin trailer   0x0

Three things kept that page from ever settling on a target.

Equally-ranked players were a hard failure. Several mirrors or dubs loaded at
once is an ordinary layout for these sites, and refusing to activate made them
unusable. The resolver now holds the top frame and waits for one of them to
start playing, which is the signal that breaks the tie.

Inconclusive probes moved the target. A page whose players are still loading
resolves differently from one call to the next, and every difference triggered
a full teardown and reinjection, so activation never finished — the popup sat
on "activating" with nothing in the log. A probe that finds no video now leaves
the target where it is.

The visibility handshake expired mid-probe. Its listener lived 1000ms while the
probe sequence is six separate executeScript round trips; on a heavy page it
was gone before the answer arrived, leaving every frame's visibility unknown —
the exact state that makes two players look equal. It now outlives the sequence.

A settled failure also no longer reports itself as "activating".

Covered by two fixtures built from the real page: one where the player exists
up front, and one where the host only creates it on play, asserting the target
is promoted into the deep cross-origin frame without touching the popup again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 00:47:29 +02:00
Timo aa5173e0d4 fix(extension): report why a target failed instead of a generic comm error
The dev panel asked the content script for video state even when activation
never completed, so every failure surfaced as "communication with the tab
video failed" — the one message that says nothing about the cause. It now
shows the activation state and its actual error, and content injection logs
the frame it was aimed at.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 00:36:23 +02:00
Timo fd42de365a fix(extension): keep the chosen tab selected when activation fails
Selecting a tab and reopening the popup showed no target again. The selection
only ever existed as currentTabId, which means "the tab we successfully
injected into". Any activation failure — a player frame needing host access, a
page still loading, a document that navigated mid-injection — cleared it, so
the user's choice disappeared along with the failure.

The choice is now stored in its own right, persisted before activation starts
and kept across a failed one. GET_STATUS reports it as targetTabId with a
terminal state next to it: ready, activating, access_required or error, plus
the underlying message. The popup already highlights targetTabId, so the tab
stays visibly selected and can explain itself instead of silently vanishing.

Nothing retries on its own. Reactivation happens only when the user selects
again or grants the missing host access, which is what turned the previous
attempt at this into an endless reinjection loop on every popup open.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 00:28:50 +02:00
Timo 75a9ba5d3d fix(extension): control nested players without permission prompts or churn
Google Drive and YummyAnime host their player in a cross-origin iframe. The
3.1.2 targeting work reached those frames but misdiagnosed and destabilized
them in four separate ways. No manifest permission is added or restored;
webNavigation stays removed.

Access diagnosis was inferred, not measured. Every frame probe error was
swallowed, and any origin that failed to answer was reported as missing host
access. A slow or still-loading player frame therefore produced
"Host access required for youtube.googleapis.com" for an origin the extension
already held. The resolver now asks permissions.contains() before raising an
access error, and treats a granted-but-unresponsive origin as a retry, not a
user decision.

Probes were unbounded. Every executeScript in the resolver now runs under a
timeout, so one unreachable frame can no longer stall an activation, and the
retry budget drops from eight passes to three.

The chat overlay followed the player into its frame, which rendered it on top
of the video and scoped closing and minimizing to that frame. It is now always
installed in the tab's top document, with all chat traffic routed to frame 0,
while only the playback controller goes into the selected media frame.

Nested targets reactivated continuously. Every heartbeat and content event
revalidated the target with a full teardown and reinjection, and the media
monitor treated ordinary play, pause and buffering as frame layout changes.
Both paths now reactivate only when the selected frame or document actually
moves.

Also restores the audio-route retention that keeps a deselected tab audible:
createMediaElementSource() can only be called once per element, so a
reinjected content script must adopt the existing route rather than rebuild it.

Verified with 90 unit tests, 40 browser E2E tests including two new
Drive-shaped fixtures that assert the controller lands in the player frame
while the chat stays in the top document, and npm run verify.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 00:15:10 +02:00
Timo 04694d4439 revert(extension): drop post-3.1.2 frame-targeting band-aids
Restores extension/ to the state directly after webNavigation was removed
(4d78970). The six follow-up commits layered heuristics on an unverified
premise (frame-ID sweeps, multi-phase probes, retry loops) without fixing
the underlying resolver. They are removed so the real fix can be built on
a known state.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 23:53:10 +02:00
Timo 804c36c694 fix(extension): bound frame injection and retry failures 2026-08-17 23:37:09 +02:00
Timo e1daeecab1 fix(extension): harden target frame recovery and switching 2026-08-17 23:06:46 +02:00
Timo 3c99efe4e4 fix(extension): persist dynamic target selection 2026-08-17 22:10:11 +02:00
Timo 8eaf026487 fix(extension): harden target and chat recovery 2026-08-17 21:44:41 +02:00
Timo bce582b568 fix(extension): preserve target and chat visibility state 2026-08-17 21:33:19 +02:00
Timo 1e6778ba93 fix(extension): recover stale media frame targets 2026-08-17 21:14:56 +02:00
Timo d8978577ad fix: update nanoid to 3.3.18 2026-08-17 18:41:58 +02:00
Timo 4d7897028b fix(extension): remove webNavigation permission 2026-08-17 18:41:40 +02:00
KoalaDev 76adafd1e4 docs: record Drive and YummyAnime support 2026-08-17 17:18:32 +02:00
GitHub Action b06d6773af chore(release): update versions to v3.1.2 [skip ci] 2026-08-17 15:14:41 +00:00
KoalaDev a8fd79618f fix(extension): cancel superseded frame monitors v3.1.2 2026-08-17 17:11:47 +02:00
KoalaDev 0701d347c7 fix(extension): preserve media detection across scrolling 2026-08-17 17:04:05 +02:00
KoalaDev 082b69f509 fix(extension): support cross-origin media frames 2026-08-17 16:49:53 +02:00
GitHub Action 77bdf21405 chore(release): update versions to v3.1.1 [skip ci] 2026-08-15 15:22:46 +00:00
Timo 9cf0a6ebaa docs: changelog for v3.1.1
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
v3.1.1
2026-08-15 17:21:06 +02:00
Timo 5711daefd4 docs: record the open episode-transition stray-pause investigation
A peer can be left paused at the end of an episode while the other moves on,
and nothing recovers it. The episode guard, the title on the wire and the
comparison all exist already, so the interesting part is why the guard lets
that one pause through — and that cannot be settled without logs from both
sides while it happens.

Records what was verified from the code, the three candidate causes that are
still hypotheses, the exact log lines that separate them, the planned fix
order, and the approaches already ruled out (forcing the comparison would
break every platform that exposes no media title).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 17:20:45 +02:00
Timo 381c6a93a2 fix(popup): keep the room tab from stretching the popup
Chrome sizes the popup window to whatever the content claims it needs and
never shrinks that window back for the rest of the session. The clipping
rule that keeps content inside the fixed 360px body used a child
combinator for the room tab (#section-active > .info-card), which misses
#peerList -- it sits in a wrapper div. The sync tab's identical peer list
was covered by #tab-sync .info-card, so the room tab held the only
unclipped card in the popup: one overflowing peer row there left the
popup stuck at up to double width, including after switching back to
Sync.

Generalize the rule to .info-card so every card clips (cards that scroll
keep their own overflow-y), let the peer row's badge group wrap instead
of overflowing now that it would otherwise be cut off, wrap long room ids
and connection status text, and let the label/button rows in Sync and
Status wrap -- those were the next candidates with longer translations.

Verified against the built extension: no element in any tab can grow the
popup any more, in all 15 shipped locales.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 11:55:09 +02:00
KoalaDev 2fe785af6e test: avoid URL substring assertions 2026-08-15 07:57:02 +02:00
GitHub Action 2ea981b68f chore(release): update versions to v3.1.0 [skip ci] 2026-08-14 06:48:31 +00:00
KoalaDev 46569aefd3 docs: changelog for v3.1.0
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
v3.1.0
2026-08-14 08:48:08 +02:00
KoalaDev 1b6f7dd9c6 docs: record the gaps v3.1.0 knowingly ships with
Sticky player selection, Firefox E2E coverage and a two-peer relay E2E are
all deliberate omissions rather than oversights. Writing them down keeps the
current suite from reading as broader coverage than it is.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 08:45:59 +02:00
KoalaDev ef1fdc89c3 test: prove the frame lifecycle fixes against the real extension
The evidence for the frame-observation fixes was a reimplementation of the
logic measured in a browser console, not the shipped code. Two extension
specs now cover the observable behaviour end to end: a player frame that
swaps its document, and the same one level deeper.

The nested case fails against the pre-fix content.js and passes now. The
top-level case already passed before the fix, so that fix removed dead
observer registrations without changing what a user could see; recorded
here so the distinction is not lost.

Also adds bench-finder.mjs, which measures the extracted shipped finder
instead of a transcription of it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 07:53:45 +02:00
KoalaDev 66971150c7 test: cover the popup blacklist UI, tighten audit findings
The delta model was only covered at module level; the popup wiring around
it was not exercised at all. Seven specs now drive the real settings UI in
the packed extension, including the migration path: a pre-v3.1.0 snapshot
is converted on open, the legacy key is removed, and a default missing
from that snapshot is delivered again.

Also from the audit pass:
- await the blacklist read in init instead of firing a floating promise
- unify the debug report on the finder's own candidate list, which the
  separate traversal missed shadow-DOM videos from
- assert that a single candidate is always returned regardless of its
  ranking signals, so no scoring signal can regress a single-player site

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 07:39:41 +02:00
KoalaDev 36e1291d2c feat: rank player candidates by ordered signals, add browser E2E suite
The weighted score summed incomparable units, so size could outvote traits
that disqualify an element outright. Measured on a real page: a display:none
preload reports its full 1080p intrinsic size and scored 2073600, beating a
visible unmuted player at 509920.

Selection now compares an ordered list of signals, highest priority first:
has a source, is rendered, is not a silent background loop, rendered size
bucket, is playing, has controls, duration. Rendered size replaces intrinsic
resolution, and mute state is gone from the ranking entirely: it is a viewer
preference, not evidence about which element is the player.

It stays a ranking rather than a filter, so a page of only bad candidates
still yields one and findVideo never returns null where a video exists.

The new tests/e2e suite runs the shipped finder against real fixture pages
and drives the packed extension for injection, reinjection and remote
play/pause/seek into a first-party frame. All five scoring scenarios fail
against the previous implementation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 07:23:12 +02:00
KoalaDev 79a2204a97 feat: store the hide-clutter list as a delta, not a snapshot
Saving the editor used to persist the full effective list, which froze the
shipped defaults at that moment: every default added in a later version
never reached anyone who had touched the list once.

Storage now keeps only { removedDefaults, addedDomains }, and the effective
list is resolved against whatever ships in the running version. Entries are
tagged default or user, the editor groups them under comment headers, and
'#' lines are ignored on save. Lists saved by earlier versions are migrated
to the delta form on first read and the legacy key is removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 06:57:41 +02:00
KoalaDev b5619ac93e docs: record JkAnime compatibility and frame support
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 06:49:29 +02:00
KoalaDev 477dad03a3 fix: harden frame observation and unblock supported Drive tabs
Audit follow-ups on the same-origin frame walk:
- prune detached frames from the load-hook set so ad/SPA frame churn
  no longer grows it for the page's lifetime
- hook load on nested frames, not just top-level ones
- re-observe from scratch after a frame reload instead of leaving the
  replaced document's tree registered
- reset the frame registry when the heartbeat error path disconnects

Also: a broad parent domain in the blacklist no longer hides a host with
its own supported player path (drive.google.com behind google.com), while
an exact user entry for that host still filters it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 06:45:33 +02:00
KoalaDev 809301b641 fix: detect videos inside same-origin player iframes
Sites like jkanime.net render the real <video> inside a first-party
iframe, so the top document had zero video elements and the content
script reported "NO VIDEO ELEMENT".

findVideo() now descends into reachable frame documents, the
MutationObserver registers those documents too (frame mutations never
bubble to the parent), and frame load events re-trigger the scan so a
late-loading player is still picked up. Debug reports count videos
across frames and expose an "In Iframe" flag.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 06:36:07 +02:00
Timo 48d6c1dc0b feat:local-blacklist-and-audio-boost 2026-08-12 02:07:34 +02:00
Timo 3d78af1e95 fix(security): fix Dependabot dependency alerts 2026-08-07 17:32:13 +02:00
KoalaDev d110957de0 Update roadmap priorities 2026-08-02 06:00:41 +02:00
KoalaDev 3062e6c93c test: cover HTML parser regressions 2026-08-01 10:15:56 +02:00
KoalaDev ec6865a0e7 test: parse help HTML with cheerio 2026-08-01 10:04:29 +02:00
Timo 816cc38ea4 fix: align help accordions consistently 2026-07-31 10:55:06 +02:00
Timo 9ade6b67ea docs: compact help topics with accordions 2026-07-31 10:46:19 +02:00
Timo 5b75bde9bb docs: expand product help guide 2026-07-31 10:10:08 +02:00
Timo 88e777eb44 docs: move v3.0.2 chat summary to changelog 2026-07-31 09:59:41 +02:00
Timo c04c501e6d docs: record v3.0.2 chat improvements 2026-07-31 09:58:23 +02:00
GitHub Action da93c09a93 chore(release): update versions to v3.0.2 [skip ci] 2026-07-31 07:48:55 +00:00
Timo 8050748e61 fix(extension): harden chat and target tab lifecycle v3.0.2 2026-07-31 09:48:22 +02:00
Timo 0b6ae803c8 feat: complete docked chat interactions 2026-07-29 22:31:40 +02:00
KoalaDev 172073896f fix: prevent language selector text clipping 2026-07-28 07:46:07 +02:00
KoalaDev ffd629a522 fix: remove unsupported legacy redirects 2026-07-28 07:35:46 +02:00
KoalaDev 2c5e2a7cc3 feat: expand help center and harden website SEO 2026-07-28 07:29:41 +02:00
KoalaDev fc005c0c88 docs: add Help Center and Edge store badge 2026-07-28 04:04:33 +02:00