Files
2026-05-27 23:29:53 +01:00

4.1 KiB

Tasks: enterprise-react-media

Change ID: enterprise-react-media Package: @suaveplan/react-media Target path: react/media/react-media Prerequisite packages on Verdaccio: @suaveplan/error, @suaveplan/types, @suaveplan/react-translate, @suaveplan/timing


0. Pre-flight (non-negotiable)

  • 0.1 Confirm clean working tree (git status reports nothing) and no in-flight worktrees with uncommitted work (git worktree list then git status in each)
  • 0.2 If any worktrees exist under .claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
  • 0.3 Confirm the package set in MANIFEST.md matches find packages -maxdepth 3 -name package.json count. Drift here means a prior change archived without regenerating the manifest.

1. Scaffold

  • Create directory tree per proposal layout.
  • Write package.json at 0.1.0 with "sideEffects": false, explicit exports map.
  • Dependencies: @suaveplan/error workspace:^, @suaveplan/types workspace:^, @suaveplan/react-translate workspace:^, @suaveplan/timing workspace:^, @react-three/drei catalog:3d, @react-three/fiber catalog:3d, zod catalog:forms, @opentelemetry/api catalog:observability, tslib catalog:build.
  • Write tsconfig.json, vitest.config.ts, scripts.

2. Error codes and types

  • Define MediaError extending SuaveplanError; codes: MEDIA_LOAD_FAILED, MEDIA_FORMAT_UNSUPPORTED, MEDIA_SOURCE_NOT_FOUND.
  • Define MediaConfig, MediaVariant, MediaSource, AdaptiveQualityOptions, ServiceWorkerConfig.
  • Write Zod MediaConfigSchema.

3. MediaProvider + context

  • MediaProvider — accepts config: MediaConfig, fetchFn?: typeof fetch (default globalThis.fetch). Provides all use* hooks.
  • useMediaConfig, useMediaError, useMediaPath, useMediaSource, useMediaVariant — consume context.
  • Write tests (no vi.stubGlobal — inject mock fetchFn) + md.

4. Hooks

  • useLazyLoad(ref) — IntersectionObserver-based; returns isVisible.
  • useAdaptiveQuality(src) — selects quality tier based on connection type from connection-quality util.
  • useProgressiveImage(src, placeholder) — loads full image after placeholder; returns currentSrc, isLoading.
  • useServiceWorker(options) — registers/unregisters service worker for media caching.
  • Write tests + md for each. All use real DI, no mocks. { timeout: 10000 }.

5. Components

For each (Media, ImageRenderer, VideoRenderer, AudioRenderer, Model3DRenderer, DocumentRenderer, PictureRenderer, ProgressiveImage, MediaErrorBoundary, PerformanceDashboard, PerformanceBadge):

  • Implement with OTel media.load span on successful load.
  • MediaErrorBoundary — extends @suaveplan/react-error-boundary with media-specific fallback.
  • Write test + stories (default, loading, error, playground) + md.

6. Utils

  • connection-quality/ — getConnectionQuality(): 'low' | 'medium' | 'high' using navigator.connection. Write test + md.
  • image-optimization/ — generateSrcSet(url, widths[]), getOptimalFormat(accept). Write test + md.
  • media-cache/ — LRU cache for media blobs. Write test + md.
  • responsive-images/ — buildPictureSourceSet(image). Write test + md.
  • style-helpers/ — aspect-ratio, object-fit helpers. Write test + md.

7. E2E

  • e2e/media.spec.ts — Playwright:
    • ImageRenderer with lazy-load: scroll into view → image loads.
    • VideoRenderer: play button → video plays.
    • ProgressiveImage: placeholder shown immediately, full image loads after.

8. Verify and publish

  • bunx turbo lint typecheck test --filter=@suaveplan/react-media — green.
  • bunx turbo test:coverage --filter=@suaveplan/react-media — 100%.
  • bun scripts/publish.ts --package @suaveplan/react-media.
  • Archive change folder.