diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..ad8dea9 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,109 @@ +# Copilot Instructions for splash_video + +## Project Overview +A Flutter package for video splash screens using `media_kit`. Provides smooth transitions from native splash to video playback with overlay support and manual controls. + +## Architecture + +### Core Components +- **`SplashVideo`** ([lib/widgets/splash_video_w.dart](../lib/widgets/splash_video_w.dart)) - Main widget orchestrating lifecycle, overlays, and navigation +- **`SplashVideoPlayer`** ([lib/widgets/splash_video_player_w.dart](../lib/widgets/splash_video_player_w.dart)) - Internal widget handling media_kit Player/VideoController +- **`SplashVideoController`** ([lib/splash_video_controller.dart](../lib/splash_video_controller.dart)) - Public API for play/pause/skip with underlying Player access +- **`VideoConfig`** ([lib/video_config.dart](../lib/video_config.dart)) - Configuration: fitMode, volume, playImmediately, useSafeArea +- **`Source`** (sealed class in [lib/video_source.dart](../lib/video_source.dart)) - Video sources: `AssetSource`, `NetworkSource`, `DeviceFileSource`, `BytesSource` + +### Key Pattern: Deferred First Frame +The package uses Flutter's `deferFirstFrame`/`allowFirstFrame` to prevent jank: +```dart +// In main() - call SplashVideo.initialize() ONLY if first screen is SplashVideo +SplashVideo.initialize(); // Defers frame + initializes MediaKit +// Later, SplashVideo.resume() is called automatically when video loads +``` + +### Widget Composition +``` +SplashVideo (lifecycle, navigation, overlays) + └── SplashVideoPlayer (media_kit integration) + └── Video widget from media_kit_video +``` + +## Development Commands + +```bash +# Run all unit tests +flutter test + +# Run specific test +flutter test test/video_source_test.dart + +# Run with coverage +flutter test --coverage + +# Integration tests (from example/) +cd example && flutter test integration_test/plugin_integration_test.dart + +# Run example app +cd example && flutter run +``` + +## Conventions + +### Source Types (Sealed Class) +Always use the appropriate `Source` subclass - the sealed class prevents external subclassing: +```dart +AssetSource('assets/videos/splash.mp4') // Bundled assets +NetworkSource('https://example.com/video.mp4') // URLs (validates format) +DeviceFileSource('/path/to/video.mp4') // Local files +BytesSource(Uint8List) // In-memory bytes +``` + +### VideoFitMode +Maps to Flutter's `BoxFit`. Default is `cover` for splash screens (fills screen, may crop). + +### Controller Lifecycle +- Controller must be disposed by the caller (not the widget) +- Access `controller.player` only after widget is built (throws `StateError` otherwise) +- For looping videos, controller is **required** + +### Error Handling +- Use `SplashVideoException` for package-specific errors +- Provide `onVideoError` callback for graceful degradation +- In debug mode, diagnostic prints prefixed with `SplashVideoPlayer:` or `SplashVideo:` + +## Testing Patterns + +Tests use Arrange-Act-Assert pattern. Widget tests should handle async initialization: +```dart +group('FeatureName', () { + test('should do something', () { + // Arrange - setup + // Act - execute + // Assert - verify with expect() + }); +}); +``` + +See [test/README.md](../test/README.md) for comprehensive testing guide. + +## File Structure +``` +lib/ +├── splash_video.dart # Library exports (barrel file) +├── splash_video_controller.dart +├── splash_video_enums.dart # VideoFitMode + BoxFit extension +├── video_config.dart +├── video_source.dart # Sealed Source class hierarchy +├── utils.dart # Typedefs, exceptions, extensions +└── widgets/ + ├── splash_video_w.dart # Main public widget + └── splash_video_player_w.dart # Internal player widget +``` + +## Platform Support +All platforms via media_kit: Android, iOS, macOS, Windows, Linux. Platform-specific libs are in pubspec.yaml dependencies. + +## Workflow Rules +- **Documentation format**: Create all documentation with lowercase filenames and `.rst` format, no emojis +- **End of task**: Always ask if tests and documentation should be updated (README.md is critical) +- **No auto-docs**: Do not create a document for every feature/change - ask if comprehensive documentation is wanted at task end +- **No silent removal**: Never remove functionality without explicit user confirmation