From 98a57ab0ec40888048f02a055e5ce02ed8771f9e Mon Sep 17 00:00:00 2001 From: David Di Biase <1168397+davedbase@users.noreply.github.com> Date: Sun, 24 May 2026 11:42:35 -0400 Subject: [PATCH 1/9] Initial commit --- .changeset/video-initial.md | 19 ++ packages/video/LICENSE | 21 ++ packages/video/README.md | 178 ++++++++++++++ packages/video/dev/index.tsx | 80 +++++++ packages/video/package.json | 68 ++++++ packages/video/src/index.ts | 363 +++++++++++++++++++++++++++++ packages/video/test/index.test.ts | 347 +++++++++++++++++++++++++++ packages/video/test/server.test.ts | 54 +++++ packages/video/test/setup.ts | 176 ++++++++++++++ packages/video/tsconfig.json | 19 ++ pnpm-lock.yaml | 16 ++ 11 files changed, 1341 insertions(+) create mode 100644 .changeset/video-initial.md create mode 100644 packages/video/LICENSE create mode 100644 packages/video/README.md create mode 100644 packages/video/dev/index.tsx create mode 100644 packages/video/package.json create mode 100644 packages/video/src/index.ts create mode 100644 packages/video/test/index.test.ts create mode 100644 packages/video/test/server.test.ts create mode 100644 packages/video/test/setup.ts create mode 100644 packages/video/tsconfig.json diff --git a/.changeset/video-initial.md b/.changeset/video-initial.md new file mode 100644 index 000000000..51754b91b --- /dev/null +++ b/.changeset/video-initial.md @@ -0,0 +1,19 @@ +--- +"@solid-primitives/video": minor +--- + +New package: `@solid-primitives/video` + +Layered primitives for managing HTML video playback. + +### `makeVideo` + +Non-reactive base primitive. Creates an `HTMLVideoElement` with optional event handlers and returns a `[player, cleanup]` tuple. No Solid owner required. + +### `makeVideoPlayer` + +Wraps `makeVideo` with playback and fullscreen controls: `play`, `pause`, `seek`, `setVolume`, `setMuted`, `setPlaybackRate`, `requestFullscreen`, `exitFullscreen`, `toggleFullscreen`. + +### `createVideo` + +Reactive primitive that tracks all media state as signals: `playing`, `currentTime`, `volume`, `muted`, `playbackRate`, `ended`, `buffered`, `readyState`, `videoWidth`, `videoHeight`, `fullscreen`, and `duration`. The `duration` accessor throws `NotReadyError` until metadata loads, integrating naturally with ``. Accepts a static or reactive `VideoSource`. diff --git a/packages/video/LICENSE b/packages/video/LICENSE new file mode 100644 index 000000000..38b41d975 --- /dev/null +++ b/packages/video/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Solid Primitives Working Group + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/packages/video/README.md b/packages/video/README.md new file mode 100644 index 000000000..5b6a170cb --- /dev/null +++ b/packages/video/README.md @@ -0,0 +1,178 @@ +

+ Solid Primitives Video +

+ +# @solid-primitives/video + +[![size](https://img.shields.io/bundlephobia/minzip/@solid-primitives/video?style=for-the-badge&label=size)](https://bundlephobia.com/package/@solid-primitives/video) +[![version](https://img.shields.io/npm/v/@solid-primitives/video?style=for-the-badge)](https://www.npmjs.com/package/@solid-primitives/video) +[![stage](https://img.shields.io/endpoint?style=for-the-badge&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-0.json)](https://github.com/solidjs-community/solid-primitives#contribution-process) + +Primitives to manage HTML video playback in the browser. The primitives are layered: `make*` variants are non-reactive base primitives that require no Solid owner, while `createVideo` integrates with Solid's reactive system. + +Within an SSR context these primitives perform noops and never interrupt the process. + +## Installation + +```bash +npm install @solid-primitives/video +# or +yarn add @solid-primitives/video +# or +pnpm add @solid-primitives/video +``` + +## How to use it + +### makeVideo + +A foundational non-reactive primitive that creates a raw `HTMLVideoElement` with optional event handlers. No Solid owner required. + +```ts +const [player, cleanup] = makeVideo("clip.mp4"); +// later: +cleanup(); +``` + +#### Definition + +```ts +function makeVideo( + src: VideoSource | HTMLVideoElement, + handlers?: VideoEventHandlers, +): [player: HTMLVideoElement, cleanup: VoidFunction]; +``` + +### makeVideoPlayer + +Wraps `makeVideo` with playback and fullscreen controls. No Solid owner required. + +```ts +const [{ play, pause, seek, setVolume, setMuted, setPlaybackRate, player }, cleanup] = + makeVideoPlayer("clip.mp4"); + +await play(); +seek(30); +setPlaybackRate(1.5); +await requestFullscreen(); +cleanup(); +``` + +#### Definition + +```ts +function makeVideoPlayer( + src: VideoSource | HTMLVideoElement, + handlers?: VideoEventHandlers, +): [controls: VideoControls, cleanup: VoidFunction]; +``` + +`VideoControls`: + +```ts +type VideoControls = { + play: () => Promise; + pause: VoidFunction; + seek: (time: number) => void; + setVolume: (volume: number) => void; + setMuted: (muted: boolean) => void; + setPlaybackRate: (rate: number) => void; + requestFullscreen: () => Promise; + exitFullscreen: () => Promise; + toggleFullscreen: () => Promise; + player: HTMLVideoElement; +}; +``` + +The `seek` function uses `fastSeek` on [supporting browsers](https://caniuse.com/?search=fastseek). + +### createVideo + +A reactive video primitive. Returns a flat object with writable signal accessors for `playing`, `volume`, `muted`, and `playbackRate`; reactive signals for `currentTime`, `ended`, `buffered`, `readyState`, `videoWidth`, `videoHeight`, and `fullscreen`; and an async `duration` that suspends until metadata is loaded — integrating with ``. + +```ts +const video = createVideo("clip.mp4"); +// or with a reactive source: +const video = createVideo(() => selectedUrl()); + +video.playing() // boolean +video.setPlaying(true) // plays +video.volume() // 0–1 +video.setVolume(0.5) +video.muted() // boolean +video.setMuted(true) +video.playbackRate() // number +video.setPlaybackRate(1.5) +video.currentTime() // seconds +video.seek(30) +video.ended() // boolean +video.readyState() // 0–4 +video.videoWidth() // intrinsic pixel width +video.videoHeight() // intrinsic pixel height +video.fullscreen() // boolean +video.requestFullscreen() +video.exitFullscreen() +video.toggleFullscreen() +``` + +Attach the `player` to a `