Skip to content

fix(scenes): hold host commands until the ready handshake - #40

Merged
karngyan merged 1 commit into
mainfrom
fix/scenes-post-before-host-ready
Jul 24, 2026
Merged

fix(scenes): hold host commands until the ready handshake#40
karngyan merged 1 commit into
mainfrom
fix/scenes-post-before-host-ready

Conversation

@karngyan

@karngyan karngyan commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Fixes the console error reported in karnstack/karnstack#153:

Failed to execute 'postMessage' on 'DOMWindow': The target origin provided
('http://localhost:8788') does not match the recipient window's origin
('http://localhost:8080').

Root cause

The failing direction is parent to host, not host to parent as the issue guessed (the scene host posts with parentOrigin: "*", which never mismatches).

A freshly created iframe holds an about:blank document that inherits the embedding page's origin until the real host document loads. send() targets the host origin resolved from src, so any command posted in that window is refused by the browser and surfaces as a console error.

ScenesPlayer hits this on every cross-origin embed: React runs child effects first, so Player mounts the iframe and then ScenesPlayerInner's sceneTheme effect immediately posts a kino:setTheme into a frame that is still about:blank.

Fix

Gate send() on the kino:ready handshake, the earliest point at which the host is both on its own origin and listening (before then a delivered message would be dropped anyway, since the host has not attached its listener). Nothing is lost: the pre-ready rate, volume, muted and theme all ride the kino:init reply.

The picture-in-picture mirror is about:blank on the pip window's origin under the same rule, so sendMirror() gates on the mirror's own handshake, reset when a mirror is created and when pip closes.

Tests

Two new cases pin the invariant for the master and the mirror, including that pre-ready settings still arrive via kino:init. Three existing tests posted commands without the handshake (encoding the buggy behavior) and now perform it first. Full suite: 212 passing, typecheck and lint clean.

Summary by CodeRabbit

  • Bug Fixes
    • Improved scene synchronization during iframe loading by delaying commands until the connection is ready.
    • Ensured playback settings such as volume, rate, mute state, and theme are applied reliably during initialization.
    • Improved Picture-in-Picture synchronization when starting or ending a session.
  • Tests
    • Added coverage verifying that commands are withheld before readiness and delivered correctly afterward.

A freshly created iframe holds an about:blank document that inherits the
embedding page's origin until the host document loads. Posting a command
at the host origin before then is refused by the browser ("The target
origin provided does not match the recipient window's origin") and lands
as a console error on every cross-origin embed: ScenesPlayer's mount-time
setSceneTheme fires microseconds after the Player mounts the iframe.

Gate send() on the kino:ready handshake, which is the earliest point the
host is both on its own origin and listening. Nothing is lost, since the
pre-ready rate, volume, muted and theme all ride the kino:init reply. The
picture-in-picture mirror is about:blank on the pip window's origin under
the same rule, so sendMirror() gates on the mirror's own handshake.
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
kino 6206cc1 Commit Preview URL

Branch Preview URL
Jul 24 2026, 09:53 PM

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Scenes messaging now waits for kino:ready from the host and picture-in-picture mirror before sending commands. Readiness resets across PiP lifecycle and provider destruction, with tests covering deferred initialization and post-ready command delivery.

Changes

Scenes readiness gating

Layer / File(s) Summary
Host readiness gate
src/scenes/provider.ts, src/scenes/provider.test.ts, .changeset/scenes-post-before-host-ready.md
Host commands are suppressed until kino:ready; subsequent initialization carries the current scene state, and destruction disables further host messaging.
Picture-in-picture readiness lifecycle
src/scenes/provider.ts, src/scenes/provider.test.ts
Mirror commands wait for the mirror handshake, readiness resets for new or closed PiP sessions, and tests cover deferred initialization and post-ready fan-out.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ScenesProvider
  participant HostIframe
  participant PipMirror
  HostIframe->>ScenesProvider: kino:ready
  ScenesProvider->>HostIframe: kino:init with current settings
  PipMirror->>ScenesProvider: kino:ready
  ScenesProvider->>PipMirror: kino:init with current mirror state
  ScenesProvider->>HostIframe: Send transport or scene command
  ScenesProvider->>PipMirror: Send mirrored command
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely captures the main change: scenes now wait for the ready handshake before sending host commands.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/scenes-post-before-host-ready

Comment @coderabbitai help to get the list of available commands.

@karngyan

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/scenes/provider.ts (1)

173-183: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Send kino:init before notifying subscribers.

Line 175 synchronously emits to subscribers while ready is already true. A subscriber that invokes an action can post a control command before kino:init; the later init may then override that command (for example, autoPlay). Send the init reply before patch({ duration }).

Proposed fix
 case "kino:ready":
   ready = true
-  patch({ duration: msg.duration })
   send({
     type: "kino:init",
     rate: desiredRate,
     volume: state.volume,
     muted: state.muted,
     autoPlay: opts.autoPlay ?? false,
     theme,
   })
+  patch({ duration: msg.duration })
   break
🤖 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/scenes/provider.ts` around lines 173 - 183, In the initialization flow,
move the kino:init send() call before patch({ duration }) so subscribers are
notified only after the initialization reply has been emitted. Preserve ready =
true before send(), and keep the existing init payload unchanged.
🤖 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.

Outside diff comments:
In `@src/scenes/provider.ts`:
- Around line 173-183: In the initialization flow, move the kino:init send()
call before patch({ duration }) so subscribers are notified only after the
initialization reply has been emitted. Preserve ready = true before send(), and
keep the existing init payload unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1a346907-630b-4407-b712-924d114f97e0

📥 Commits

Reviewing files that changed from the base of the PR and between 0a1ba5c and 6206cc1.

📒 Files selected for processing (3)
  • .changeset/scenes-post-before-host-ready.md
  • src/scenes/provider.test.ts
  • src/scenes/provider.ts

@karngyan
karngyan merged commit 3f61944 into main Jul 24, 2026
6 checks passed
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.

1 participant