feat: Vimeo provider#18
Merged
Merged
Conversation
Event-driven provider over the Vimeo Player SDK — quality, captions, PiP, and rate. Mirrors youtube/ shape (async iframe SDK, destroyed flag) but syncs on SDK events instead of a ticker, renders cues in kino's own overlay (enableTextTrack showing:false + cuechange), and derives plan-gated capabilities at runtime. Supports unlisted videos via hash parsing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
10 TDD tasks from source parsing through docs/changeset. Shared FakeVimeoPlayer fixture, event-driven sync, runtime-derived capabilities, hash channel through swapSource. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…esolve The Vimeo SDK's getQualities() returns an "auto" pseudo-entry; mapping it surfaced a bogus "0p" rendition that also matched activeQualityId === "auto", double-checking the quality menu. Filter it out — kino renders its own Auto row. setRate relied on a playbackratechange echo that Vimeo does not emit for programmatic rate changes, leaving the control stuck at the old rate while the video played at the new one. Patch rate when setPlaybackRate resolves instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Provider tabs now read Mux, Vimeo, Native, YouTube. The Source picker swaps the Mux playback id and has no effect on the embed providers, so it's hidden unless the Mux tab is active. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…uring seek Seeking past the buffered region appeared stuck/laggy: the seeking event carries the target seconds (like seeked), but the handler ignored it. While Vimeo buffers the new position, timeupdate is suppressed, so currentTime — and the scrubber thumb — held at the pre-seek value. Native/mux read el.currentTime, which returns the target instantly; mirror that by patching currentTime from the seeking event. bufferstart also fired during a paused seek and flipped paused to false with no later event to correct it, stranding the wrong paused state. Guard it on !state.seeking so it only reflects an active-playback buffer stall. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each docs page gets a 'Copy as Markdown' button (top-right of the header) that copies a co-located markdown rendering of the page — handy for pasting into an LLM. Adds /llms.txt (served as text/plain at the site root, ahead of the SPA fallback) following the llms.txt standard: a summary plus curated links to the four pages, the npm entry points, GitHub, and the README. A footer link makes it discoverable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
kino | 889c5e1 | Commit Preview URL Branch Preview URL |
Jun 28 2026, 03:10 PM |
The Vimeo SDK builds its cross-origin player.vimeo.com iframe with only allow="autoplay; encrypted-media". Permissions Policy is enforced at the iframe boundary, so requestPictureInPicture() inside the frame was silently rejected (and swallowed by the action's .catch) even though the top document had PiP enabled — the button did nothing. Patch the iframe's allow attribute to include picture-in-picture the moment the SDK inserts it, via a MutationObserver on the host (set before navigation so the policy is evaluated with the grant present). The observer self-disconnects on the first iframe and is torn down on destroy(). The test fake now mirrors the SDK's real default allow list so the patch path is exercised. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Supersedes the previous allow-attribute patch, which was misguided: inspecting the live embed shows the Vimeo SDK already grants the iframe allow="…; picture-in-picture; …", so Permissions Policy was never the blocker. The real cause is cross-frame user-gesture loss. The SDK's requestPictureInPicture() is a plain postMessage to the cross-origin player.vimeo.com iframe, and postMessage does not transfer transient user activation. The iframe's video.requestPictureInPicture() therefore runs without a gesture and the browser rejects it (NotAllowedError), which the action's .catch swallowed — so the button silently did nothing. Vimeo's SDK exposes no capability-delegation path (vimeo/player.js#696, #734, both closed not-planned). Same limitation as YouTube: set canPiP: false so the dead button is hidden, and stub enterPiP/exitPiP. Removes the now-pointless MutationObserver allow-patch, its tests, and the fake's iframe injection. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PiP is disabled for Vimeo (can't be driven from the parent frame), so remove it from the providers card, the page markdown, and the changeset. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CI's format:check (prettier --check) flagged the Vimeo source and the docs/superpowers markdown — eslint passed locally but prettier is a separate gate. Run prettier --write. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a fourth kino provider — Vimeo — under the same glass chrome as Mux/native/YouTube, plus two demo-site improvements for LLM consumption.
Vimeo provider (
@karnstack/kino/vimeo)Built task-by-task (TDD, per-task subagent review) from the design spec + plan on this branch:
createVimeoProviderover the Vimeo Player SDK (player.js) — module-singleton loader, host<div>,destroyed-flag teardown, event-driven state sync (no polling ticker).vimeo.com/ID,/ID/HASH,player.vimeo.com?h=), unlisted-hash embed URLs.loadedhandler: duration, qualities (height parsed from the rendition id, not the label), synthesized text-track ids, capability flips derived at runtime.enableTextTrack(…, showing=false));swapSourcewith the hash channel.<VimeoPlayer>React wrapper (reactivevideoId/hash/metadata.videoTitlethroughswapSource);./vimeobuild entry + export map; README/demo/changeset (minor).@types/*— hand-rolled structural types.Bug fixes (found in manual testing)
0prow + double checkmark — Vimeo'sgetQualities()returns anautopseudo-entry; it's now filtered out (kino renders its own Auto row).playbackratechangefor programmatic rate changes, so the control stuck at the old rate; now patches onsetPlaybackRateresolve.seekingevent carries the targetseconds(likeseeked) but was ignored, andtimeupdateis silent while buffering; now advancescurrentTimefromseeking(matching native/mux) and guardsbufferstartduring a paused seek.Demo site
/llms.txtindex (served astext/plainat the site root, ahead of the SPA fallback) per the llms.txt standard, with a footer link.Test plan
pnpm test— 122 passing (19 files), incl. the Vimeo provider suite and regression tests for all three bug fixes.pnpm typecheck,pnpm lint,pnpm build— all clean;./vimeoentry emitsdist/vimeo.{js,d.ts}; demo build emitsdemo/dist/llms.txt./llms.txtreturns200 text/plain.🤖 Generated with Claude Code