feat(monorepo): migrate prototype code to organized package structure

Migrate all prototype code from vjs10-prototype repository into the
monorepo structure following established dependency hierarchy and
package organization patterns.

Core packages migrated:
- @vjs-10/media-store: Complete media store with nanostores integration
- @vjs-10/playback-engine: HLS.js playback engine implementation
- @vjs-10/media: Core media state owner and interfaces

HTML packages migrated:
- @vjs-10/html-icons: Web component icons with proper base classes
- @vjs-10/html-media-elements: Context provider integration
- @vjs-10/html: Main HTML UI components, skins, and utilities

React packages migrated:
- @vjs-10/react-icons: React icon components
- @vjs-10/react-media-elements: Video component with state integration
- @vjs-10/react-media-store: MediaProvider and React hooks
- @vjs-10/react: Main React UI components and skins

Changes made:
- Updated all imports to use monorepo package names (@vjs-10/*)
- Distributed dependencies to specific packages that use them
- Preserved all prototype functionality and component structure
- Created proper index files for all packages
- Fixed workspace protocol usage for npm compatibility

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Christian Pillsbury
2025-07-29 13:17:46 -07:00
co-authored by Claude
parent fc5a0e46fd
commit 4b472ec49c
61 changed files with 1967 additions and 995 deletions
+1 -155
View File
@@ -1,155 +1 @@
import { MediaElementLike, createMediaElementAdapter } from '@vjs-10/media';
import { PlaybackEngine, NativePlaybackEngine } from '@vjs-10/playback-engine';
export interface MediaElementOptions {
playbackEngine?: PlaybackEngine;
controls?: boolean;
autoplay?: boolean;
preload?: 'none' | 'metadata' | 'auto';
}
export class VjsMediaElement extends HTMLElement {
private mediaElement: HTMLVideoElement | HTMLAudioElement;
private playbackEngine: PlaybackEngine;
private adapter: MediaElementLike;
constructor() {
super();
this.attachShadow({ mode: 'open' });
const mediaType = this.getAttribute('media-type') || 'video';
this.mediaElement = mediaType === 'audio'
? document.createElement('audio')
: document.createElement('video');
this.playbackEngine = new NativePlaybackEngine();
this.adapter = createMediaElementAdapter(this.mediaElement);
}
static get observedAttributes() {
return ['src', 'controls', 'autoplay', 'preload', 'media-type'];
}
connectedCallback() {
this.render();
this.setupEventListeners();
this.playbackEngine.attach(this.mediaElement);
}
disconnectedCallback() {
this.playbackEngine.detach();
}
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
if (oldValue === newValue) return;
switch (name) {
case 'src':
if (newValue) {
this.playbackEngine.load({ src: newValue, type: 'video/mp4' });
}
break;
case 'controls':
this.mediaElement.controls = newValue !== null;
break;
case 'autoplay':
this.mediaElement.autoplay = newValue !== null;
break;
case 'preload':
this.mediaElement.preload = newValue as any;
break;
}
}
private render() {
if (!this.shadowRoot) return;
const styles = `
<style>
:host {
display: block;
width: 100%;
}
video, audio {
width: 100%;
height: 100%;
}
</style>
`;
this.shadowRoot.innerHTML = styles;
this.shadowRoot.appendChild(this.mediaElement);
}
private setupEventListeners() {
this.mediaElement.addEventListener('play', () => {
this.dispatchEvent(new CustomEvent('vjs-play'));
});
this.mediaElement.addEventListener('pause', () => {
this.dispatchEvent(new CustomEvent('vjs-pause'));
});
this.mediaElement.addEventListener('timeupdate', () => {
this.dispatchEvent(new CustomEvent('vjs-timeupdate', {
detail: { currentTime: this.mediaElement.currentTime }
}));
});
this.mediaElement.addEventListener('loadedmetadata', () => {
this.dispatchEvent(new CustomEvent('vjs-loadedmetadata', {
detail: { duration: this.mediaElement.duration }
}));
});
}
async play() {
return this.playbackEngine.play();
}
pause() {
this.playbackEngine.pause();
}
seekTo(time: number) {
this.playbackEngine.seekTo(time);
}
get currentTime() {
return this.adapter.currentTime;
}
set currentTime(value: number) {
this.adapter.currentTime = value;
}
get duration() {
return this.adapter.duration;
}
get paused() {
return this.adapter.paused;
}
get volume() {
return this.adapter.volume;
}
set volume(value: number) {
this.adapter.volume = value;
}
get muted() {
return this.adapter.muted;
}
set muted(value: boolean) {
this.adapter.muted = value;
}
}
if (!customElements.get('vjs-media')) {
customElements.define('vjs-media', VjsMediaElement);
}
export { MediaElementLike, createMediaElementAdapter } from '@vjs-10/media';
export * from './media-provider';
@@ -0,0 +1,12 @@
import { ProviderMixin } from '@open-wc/context-protocol';
import { createMediaStore } from '@vjs-10/media-store';
export class MediaProvider extends ProviderMixin(HTMLElement) {
contexts = {
mediaStore: () => {
return createMediaStore();
},
};
}
customElements.define('media-provider', MediaProvider);