npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mostajs/media

v1.0.6

Published

Image and video capture, editing, and upload components for React/Next.js

Readme

@mostajs/media

Image and video capture, editing, screen recording, and media gallery components for React/Next.js. Includes webcam capture, screen + webcam overlay recording, image editing (crop, rotate, brightness, contrast), and a floating debug screen recorder widget.

Installation

npm install @mostajs/media lucide-react

Components

<ImageCapture />

Capture images from webcam, file upload, or screen capture.

import { ImageCapture } from '@mostajs/media'

<ImageCapture
  photo={photo}
  onCapture={(dataUrl) => setPhoto(dataUrl)}
  onClear={() => setPhoto('')}
  allowUpload
  maxWidth={800}
  maxHeight={800}
  quality={0.85}
  captureLabel="Take Photo"
  uploadLabel="Upload"
  onError={(msg) => toast.error(msg)}
/>

Features:

  • Webcam capture with front/back camera switch
  • File upload (accepts image/*)
  • Screen capture (single frame)
  • Auto-resize to maxWidth/maxHeight
  • Mirror for front camera
  • Inline preview with clear button

<VideoCapture />

Record video from webcam or screen with webcam overlay.

import { VideoCapture } from '@mostajs/media'

<VideoCapture
  onCapture={(blob, url) => {
    setVideoUrl(url)
    uploadVideo(blob)
  }}
  maxDuration={120}
  startLabel="Record"
  stopLabel="Stop"
  onError={(msg) => toast.error(msg)}
/>

Features:

  • Webcam recording: Record from webcam with audio
  • Screen + Webcam: Record screen with webcam overlay in corner (picture-in-picture)
  • Auto-stop at maxDuration
  • In-recording screenshot capture
  • Duration timer with recording indicator
  • Video playback after recording

<ScreenRecorder />

Floating debug/feedback widget. Users can trigger screen capture at any time to report bugs or explain behavior.

import { ScreenRecorder } from '@mostajs/media/components/ScreenRecorder'

// Place at app root
<ScreenRecorder
  onScreenshot={(img) => submitFeedback({ screenshot: img })}
  onRecording={(blob) => uploadRecording(blob)}
  uploadEndpoint="/api/feedback/media"
  webcamOverlay
  position="bottom-right"
  maxDuration={300}
/>

Features:

  • Floating button (FAB) that expands into a control panel
  • Screenshot capture (screen + webcam overlay composited)
  • Screen recording with webcam overlay
  • Duration timer and recording indicator
  • Auto-upload to configured endpoint
  • Download button for local save
  • Positions: bottom-right, bottom-left, top-right, top-left

<ImageEditor />

Edit captured images with rotate, flip, brightness, and contrast controls.

import { ImageEditor } from '@mostajs/media'

<ImageEditor
  src={photo}
  onSave={(edited) => setPhoto(edited)}
  onCancel={() => setEditing(false)}
  tools={['crop', 'rotate', 'brightness', 'contrast', 'flip']}
  format="image/jpeg"
  quality={0.85}
/>

Tools: rotate (90° increments), flip (horizontal/vertical), brightness (-100 to 100), contrast (-100 to 100), crop

<MediaGallery />

Grid gallery with lightbox for images and videos.

import { MediaGallery } from '@mostajs/media'

<MediaGallery
  items={[
    { id: '1', url: '/photos/1.jpg', type: 'image', name: 'Photo 1' },
    { id: '2', url: '/videos/demo.webm', type: 'video', thumbnail: '/thumbs/demo.jpg' },
  ]}
  columns={3}
  deletable
  onSelect={(item) => console.log('Selected:', item)}
  onDelete={(item) => deleteMedia(item.id)}
/>

Integration dans le projet hote

Ce module expose des composants React et des contributions menu mais ne cree pas de pages Next.js. Le projet hote doit creer les pages correspondant aux routes declarees dans le menu (mediaMenuContribution).

1. Pages a creer

Creez les fichiers suivants dans votre projet Next.js :

src/app/dashboard/media/capture/page.tsx

'use client'
import { ImageCapture } from '@mostajs/media'

export default function MediaCapturePage() {
  return (
    <div className="space-y-6">
      <h1 className="text-2xl font-bold">Capture Image</h1>
      <ImageCapture
        onCapture={(dataUrl) => console.log('Captured:', dataUrl)}
        onClear={() => {}}
        allowUpload
      />
    </div>
  )
}

src/app/dashboard/media/video/page.tsx

'use client'
import { VideoCapture } from '@mostajs/media'

export default function MediaVideoPage() {
  return (
    <div className="space-y-6">
      <h1 className="text-2xl font-bold">Capture Video</h1>
      <VideoCapture onCapture={(blob, url) => console.log('Recorded:', url)} />
    </div>
  )
}

src/app/dashboard/media/gallery/page.tsx

'use client'
import { MediaGallery } from '@mostajs/media'

export default function MediaGalleryPage() {
  return (
    <div className="space-y-6">
      <h1 className="text-2xl font-bold">Galerie Media</h1>
      <MediaGallery items={[]} columns={3} />
    </div>
  )
}

src/app/dashboard/media/screen/page.tsx

'use client'
import { ScreenRecorder } from '@mostajs/media'

export default function MediaScreenPage() {
  return (
    <div className="space-y-6">
      <h1 className="text-2xl font-bold">Enregistrement Ecran</h1>
      <ScreenRecorder />
    </div>
  )
}

2. Menu dynamique

Le module exporte mediaMenuContribution qui declare automatiquement les 4 routes ci-dessus. Importez-le dans votre sidebar via le deep import :

import { mediaMenuContribution } from '@mostajs/media/lib/menu'

Passez-le a buildMenuConfig() de @mostajs/menu avec les autres contributions de modules.

3. Pourquoi le projet hote doit creer les pages ?

Les modules @mostajs/* sont des bibliotheques npm (composants, hooks, utilitaires), pas des applications. Next.js App Router exige que les fichiers page.tsx soient dans le dossier src/app/ du projet. Un package npm ne peut pas injecter de pages dans le routeur — c'est donc au projet hote de creer ces fichiers wrapper, meme s'ils ne font qu'importer et afficher un composant du module.

Hooks

useCamera()

Low-level camera access hook.

import { useCamera } from '@mostajs/media/hooks/useCamera'

const { videoRef, active, start, stop, capture, switchCamera, facingMode, error } = useCamera()

<video ref={videoRef} autoPlay playsInline muted />
<button onClick={() => start({ facingMode: 'user' })}>Start</button>
<button onClick={() => { const img = capture(); if (img) setPhoto(img) }}>Snap</button>
<button onClick={switchCamera}>Switch Camera</button>

useVideoRecorder()

Webcam video recording hook.

import { useVideoRecorder } from '@mostajs/media/hooks/useVideoRecorder'

const { videoRef, cameraActive, recording, duration, startCamera, startRecording, stopRecording, error } = useVideoRecorder()

<video ref={videoRef} autoPlay playsInline muted />
<button onClick={() => startCamera()}>Camera</button>
<button onClick={startRecording}>Record</button>
<button onClick={async () => {
  const result = await stopRecording()
  if (result) saveVideo(result.blob)
}}>Stop</button>

useScreenCapture()

Screen sharing + recording with optional webcam overlay.

import { useScreenCapture } from '@mostajs/media/hooks/useScreenCapture'

const {
  videoRef, webcamRef, active, webcamActive, recording, duration,
  startScreenShare, stopScreenShare, captureFrame, startRecording, stopRecording, error,
} = useScreenCapture()

<div style={{ position: 'relative' }}>
  <video ref={videoRef} autoPlay playsInline />
  <video ref={webcamRef} autoPlay playsInline muted
    style={{ position: 'absolute', bottom: 16, right: 16, width: 200, borderRadius: 8 }} />
</div>
<button onClick={() => startScreenShare({ audio: true, webcamOverlay: true })}>Share</button>
<button onClick={startRecording}>Record</button>
<button onClick={() => { const img = captureFrame(); if (img) download(img) }}>Screenshot</button>

Image Utilities

import {
  resizeImage, rotateImage, flipImage, cropImage,
  adjustBrightness, adjustContrast,
  dataUrlToBlob, fileToDataUrl,
} from '@mostajs/media/lib/image-utils'

// Resize
const resized = await resizeImage(dataUrl, 800, 800, 0.85, 'image/jpeg')

// Rotate 90 degrees
const rotated = await rotateImage(dataUrl, 90)

// Flip horizontally
const flipped = await flipImage(dataUrl, 'horizontal')

// Crop
const cropped = await cropImage(dataUrl, 100, 100, 400, 300)

// Adjust brightness/contrast (-100 to 100)
const brighter = await adjustBrightness(dataUrl, 20)
const sharper = await adjustContrast(dataUrl, 30)

// Convert formats
const blob = dataUrlToBlob(dataUrl)
const dataUrl2 = await fileToDataUrl(file)

License

MIT