diff --git a/examples/react/package.json b/examples/react/package.json
index 886c5d905..469fdca07 100644
--- a/examples/react/package.json
+++ b/examples/react/package.json
@@ -11,6 +11,7 @@
"@tailwindcss/vite": "^4.0.9",
"@uppy/core": "workspace:*",
"@uppy/react": "workspace:*",
+ "@uppy/screen-capture": "workspace:*",
"@uppy/tus": "workspace:*",
"@uppy/webcam": "workspace:*",
"react": "^19.0.0",
diff --git a/examples/react/src/App.tsx b/examples/react/src/App.tsx
index a476ee2a8..46da25fd5 100644
--- a/examples/react/src/App.tsx
+++ b/examples/react/src/App.tsx
@@ -2,156 +2,54 @@
/* eslint-disable react/button-has-type */
/* eslint-disable react/jsx-props-no-spreading */
/* eslint-disable react/react-in-jsx-scope */
-import { useEffect, useState, useRef } from 'react'
+import React, { useState, useRef } from 'react'
import {
Dropzone,
FilesGrid,
FilesList,
- ProviderIcon,
UploadButton,
UppyContextProvider,
- useWebcam,
- useDropzone,
- useFileInput,
} from '@uppy/react'
import Uppy from '@uppy/core'
import Tus from '@uppy/tus'
import UppyWebcam from '@uppy/webcam'
+import UppyScreenCapture from '@uppy/screen-capture'
+import Webcam from './Webcam.tsx'
+import ScreenCapture from './ScreenCapture.tsx'
+import CustomDropzone from './CustomDropzone.tsx'
import './app.css'
import '@uppy/react/dist/styles.css'
-interface WebcamProps {
- isOpen: boolean
- close: () => void
-}
-
-function Webcam({ isOpen, close }: WebcamProps) {
- const {
- start,
- stop,
- getVideoProps,
- getSnapshotButtonProps,
- getRecordButtonProps,
- getStopRecordingButtonProps,
- getSubmitButtonProps,
- getDiscardButtonProps,
- } = useWebcam({ onSubmit: close })
-
- useEffect(() => {
- if (isOpen) {
- start()
- }
-
- return () => {
- stop()
- }
- }, [start, stop, isOpen])
-
- return (
-
-
-
Camera
-
-
-
-
-
-
-
-
-
-
-
- )
-}
-
-interface CustomDropzoneProps {
- openWebcamModal: () => void
-}
-
-function CustomDropzone({ openWebcamModal }: CustomDropzoneProps) {
- const { getRootProps, getInputProps } = useDropzone({
- noClick: true,
- })
- const { getButtonProps, getInputProps: getFileInputProps } = useFileInput()
-
- return (
-
-
-
-
-
-
-
-
-
-
-
- )
-}
-
function App() {
const [uppy] = useState(() =>
new Uppy()
.use(Tus, {
endpoint: 'https://tusd.tusdemo.net/files/',
})
+ .use(UppyScreenCapture, {
+ preferredVideoMimeType: 'video/webm',
+ displayMediaConstraints: {
+ video: {
+ width: 1280,
+ height: 720,
+ frameRate: {
+ ideal: 3,
+ max: 5,
+ },
+ },
+ audio: false,
+ },
+ enableScreenshots: true,
+ preferredImageMimeType: 'image/webp',
+ })
.use(UppyWebcam),
)
const webcamDialogRef = useRef(null)
+ const screenCaptureDialogRef = useRef(null)
const [isWebcamOpen, setIsWebcamOpen] = useState(false)
+ const [isScreenCaptureOpen, setIsScreenCaptureOpen] = useState(false)
function openWebcamModal() {
setIsWebcamOpen(true)
@@ -163,6 +61,16 @@ function App() {
webcamDialogRef.current?.close()
}
+ function openScreenCaptureModal() {
+ setIsScreenCaptureOpen(true)
+ screenCaptureDialogRef.current?.showModal()
+ }
+
+ function closeScreenCaptureModal() {
+ setIsScreenCaptureOpen(false)
+ screenCaptureDialogRef.current?.close()
+ }
+
return (
@@ -177,6 +85,16 @@ function App() {
closeWebcamModal()} />
+
+
With list
@@ -191,7 +109,11 @@ function App() {
With custom dropzone
- openWebcamModal()} />
+ openWebcamModal()}
+ openScreenCaptureModal={() => openScreenCaptureModal()}
+ />
+
diff --git a/examples/react/src/CustomDropzone.tsx b/examples/react/src/CustomDropzone.tsx
new file mode 100644
index 000000000..26777e0ab
--- /dev/null
+++ b/examples/react/src/CustomDropzone.tsx
@@ -0,0 +1,63 @@
+/* eslint-disable react/jsx-props-no-spreading */
+/* eslint-disable react/react-in-jsx-scope */
+/* eslint-disable react/button-has-type */
+import { useDropzone, useFileInput, ProviderIcon } from '@uppy/react'
+
+export interface CustomDropzoneProps {
+ openWebcamModal: () => void
+ openScreenCaptureModal: () => void
+}
+
+export function CustomDropzone({
+ openWebcamModal,
+ openScreenCaptureModal,
+}: CustomDropzoneProps) {
+ const { getRootProps, getInputProps } = useDropzone({ noClick: true })
+ const { getButtonProps, getInputProps: getFileInputProps } = useFileInput()
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default CustomDropzone
diff --git a/examples/react/src/MediaCapture.tsx b/examples/react/src/MediaCapture.tsx
new file mode 100644
index 000000000..58c5f08ce
--- /dev/null
+++ b/examples/react/src/MediaCapture.tsx
@@ -0,0 +1,88 @@
+/* eslint-disable react/jsx-props-no-spreading, react/button-has-type */
+/* eslint-disable react/react-in-jsx-scope */
+import React from 'react'
+
+type ButtonProps = {
+ type: 'button'
+ onClick: () => void
+ disabled: boolean
+}
+
+export interface MediaCaptureProps {
+ title: string
+ close: () => void
+ getVideoProps: () => Record
+ getPrimaryActionButtonProps: () => ButtonProps
+ primaryActionButtonLabel: string
+ getRecordButtonProps: () => ButtonProps
+ getStopRecordingButtonProps: () => ButtonProps
+ getSubmitButtonProps: () => ButtonProps
+ getDiscardButtonProps: () => ButtonProps
+}
+
+export function MediaCapture({
+ title,
+ close,
+ getVideoProps,
+ getPrimaryActionButtonProps,
+ primaryActionButtonLabel,
+ getRecordButtonProps,
+ getStopRecordingButtonProps,
+ getSubmitButtonProps,
+ getDiscardButtonProps,
+}: MediaCaptureProps) {
+ return (
+
+
+
{title}
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default MediaCapture
diff --git a/examples/react/src/ScreenCapture.tsx b/examples/react/src/ScreenCapture.tsx
new file mode 100644
index 000000000..64639a99b
--- /dev/null
+++ b/examples/react/src/ScreenCapture.tsx
@@ -0,0 +1,47 @@
+/* eslint-disable react/react-in-jsx-scope */
+import React, { useEffect } from 'react'
+import { useScreenCapture } from '@uppy/react'
+import MediaCapture from './MediaCapture.tsx'
+
+export interface ScreenCaptureProps {
+ isOpen: boolean
+ close: () => void
+}
+
+export function ScreenCapture({ isOpen, close }: ScreenCaptureProps) {
+ const {
+ start,
+ stop,
+ getVideoProps,
+ getScreenshotButtonProps,
+ getRecordButtonProps,
+ getStopRecordingButtonProps,
+ getSubmitButtonProps,
+ getDiscardButtonProps,
+ } = useScreenCapture({ onSubmit: close })
+
+ useEffect(() => {
+ if (isOpen) {
+ start()
+ }
+ return () => {
+ stop()
+ }
+ }, [start, stop, isOpen])
+
+ return (
+
+ )
+}
+
+export default ScreenCapture
diff --git a/examples/react/src/Webcam.tsx b/examples/react/src/Webcam.tsx
new file mode 100644
index 000000000..c884a7249
--- /dev/null
+++ b/examples/react/src/Webcam.tsx
@@ -0,0 +1,48 @@
+/* eslint-disable react/jsx-props-no-spreading */
+/* eslint-disable react/react-in-jsx-scope */
+import React, { useEffect } from 'react'
+import { useWebcam } from '@uppy/react'
+import MediaCapture from './MediaCapture.tsx'
+
+export interface WebcamProps {
+ isOpen: boolean
+ close: () => void
+}
+
+export function Webcam({ isOpen, close }: WebcamProps) {
+ const {
+ start,
+ stop,
+ getVideoProps,
+ getSnapshotButtonProps,
+ getRecordButtonProps,
+ getStopRecordingButtonProps,
+ getSubmitButtonProps,
+ getDiscardButtonProps,
+ } = useWebcam({ onSubmit: close })
+
+ useEffect(() => {
+ if (isOpen) {
+ start()
+ }
+ return () => {
+ stop()
+ }
+ }, [start, stop, isOpen])
+
+ return (
+
+ )
+}
+
+export default Webcam
diff --git a/examples/react/tsconfig.json b/examples/react/tsconfig.json
index 4df86fb6a..49d7c965f 100644
--- a/examples/react/tsconfig.json
+++ b/examples/react/tsconfig.json
@@ -9,6 +9,7 @@
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
+ "allowImportingTsExtensions": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
diff --git a/examples/sveltekit/package.json b/examples/sveltekit/package.json
index 9fa0fb63d..126584ee4 100644
--- a/examples/sveltekit/package.json
+++ b/examples/sveltekit/package.json
@@ -16,6 +16,7 @@
"@uppy/dashboard": "workspace:*",
"@uppy/drag-drop": "workspace:*",
"@uppy/progress-bar": "workspace:*",
+ "@uppy/screen-capture": "workspace:*",
"@uppy/svelte": "workspace:*",
"@uppy/tus": "workspace:*",
"@uppy/webcam": "workspace:*"
diff --git a/examples/sveltekit/src/components/CustomDropzone.svelte b/examples/sveltekit/src/components/CustomDropzone.svelte
index 89270b959..2b095e0aa 100644
--- a/examples/sveltekit/src/components/CustomDropzone.svelte
+++ b/examples/sveltekit/src/components/CustomDropzone.svelte
@@ -3,9 +3,10 @@
interface Props {
openWebcamModal: () => void
+ openScreenCaptureModal: () => void
}
- const { openWebcamModal }: Props = $props()
+ const { openWebcamModal, openScreenCaptureModal }: Props = $props()
const { getRootProps, getInputProps } = useDropzone({ noClick: true })
const { getButtonProps, getInputProps: getFileInputProps } = useFileInput()
@@ -39,6 +40,16 @@
Webcam
+
+
diff --git a/examples/sveltekit/src/components/MediaCapture.svelte b/examples/sveltekit/src/components/MediaCapture.svelte
new file mode 100644
index 000000000..ee78dc462
--- /dev/null
+++ b/examples/sveltekit/src/components/MediaCapture.svelte
@@ -0,0 +1,73 @@
+
+
+
+
+
{title}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/sveltekit/src/components/ScreenCapture.svelte b/examples/sveltekit/src/components/ScreenCapture.svelte
new file mode 100644
index 000000000..200b12af3
--- /dev/null
+++ b/examples/sveltekit/src/components/ScreenCapture.svelte
@@ -0,0 +1,38 @@
+
+
+
diff --git a/examples/sveltekit/src/components/Webcam.svelte b/examples/sveltekit/src/components/Webcam.svelte
index 646724252..67df212c1 100644
--- a/examples/sveltekit/src/components/Webcam.svelte
+++ b/examples/sveltekit/src/components/Webcam.svelte
@@ -1,4 +1,5 @@
-
-
-
Camera
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/examples/sveltekit/src/routes/+page.svelte b/examples/sveltekit/src/routes/+page.svelte
index 73f9c7b31..b8744c1aa 100644
--- a/examples/sveltekit/src/routes/+page.svelte
+++ b/examples/sveltekit/src/routes/+page.svelte
@@ -2,6 +2,7 @@
import Uppy from '@uppy/core'
import Tus from '@uppy/tus'
import UppyWebcam from '@uppy/webcam'
+ import UppyScreenCapture from '@uppy/screen-capture'
import {
UppyContextProvider,
Dropzone,
@@ -13,16 +14,21 @@
import CustomDropzone from '../components/CustomDropzone.svelte'
import Webcam from '../components/Webcam.svelte'
+ import ScreenCapture from '../components/ScreenCapture.svelte'
const uppy = new Uppy()
.use(Tus, {
endpoint: 'https://tusd.tusdemo.net/files/',
})
.use(UppyWebcam)
+ .use(UppyScreenCapture)
let webcamDialogRef: HTMLDialogElement
let isWebcamOpen = $state(false)
+ let screenCaptureDialogRef: HTMLDialogElement
+ let isScreenCaptureOpen = $state(false)
+
function openWebcamModal() {
isWebcamOpen = true
webcamDialogRef?.showModal()
@@ -32,6 +38,16 @@
isWebcamOpen = false
webcamDialogRef?.close()
}
+
+ function openScreenCaptureModal() {
+ isScreenCaptureOpen = true
+ screenCaptureDialogRef?.showModal()
+ }
+
+ function closeScreenCaptureModal() {
+ isScreenCaptureOpen = false
+ screenCaptureDialogRef?.close()
+ }
@@ -47,6 +63,13 @@
+
+
With list
@@ -61,7 +84,7 @@
With custom dropzone
-
+
diff --git a/examples/vue3/package.json b/examples/vue3/package.json
index 8d4f15043..f48c5ae16 100644
--- a/examples/vue3/package.json
+++ b/examples/vue3/package.json
@@ -13,6 +13,7 @@
"@uppy/dashboard": "workspace:*",
"@uppy/drag-drop": "workspace:*",
"@uppy/progress-bar": "workspace:*",
+ "@uppy/screen-capture": "workspace:*",
"@uppy/tus": "workspace:*",
"@uppy/vue": "workspace:*",
"@uppy/webcam": "workspace:*",
diff --git a/examples/vue3/src/App.vue b/examples/vue3/src/App.vue
index 0f7f907c9..1f53eedd4 100644
--- a/examples/vue3/src/App.vue
+++ b/examples/vue3/src/App.vue
@@ -12,21 +12,40 @@
+
+
With list
-
+
With grid
-
+
With custom dropzone
-
+
@@ -45,10 +64,14 @@ import {
} from '@uppy/vue'
import CustomDropzone from './Dropzone.vue'
import Webcam from './Webcam.vue'
+import ScreenCapture from './ScreenCapture.vue'
import UppyWebcam from '@uppy/webcam'
+import UppyScreenCapture from '@uppy/screen-capture'
const webcamDialogRef = ref(null)
const isWebcamOpen = ref(false)
+const screenCaptureDialogRef = ref(null)
+const isScreenCaptureOpen = ref(false)
function openWebcamModal() {
isWebcamOpen.value = true
@@ -60,12 +83,23 @@ function closeWebcamModal() {
webcamDialogRef.value?.close()
}
+function openScreenCaptureModal() {
+ isScreenCaptureOpen.value = true
+ screenCaptureDialogRef.value?.showModal()
+}
+
+function closeScreenCaptureModal() {
+ isScreenCaptureOpen.value = false
+ screenCaptureDialogRef.value?.close()
+}
+
const uppy = computed(() =>
new Uppy()
.use(Tus, {
endpoint: 'https://tusd.tusdemo.net/files/',
})
- .use(UppyWebcam),
+ .use(UppyWebcam)
+ .use(UppyScreenCapture),
)
diff --git a/examples/vue3/src/Dropzone.vue b/examples/vue3/src/Dropzone.vue
index bc9ab7e0a..f2c460178 100644
--- a/examples/vue3/src/Dropzone.vue
+++ b/examples/vue3/src/Dropzone.vue
@@ -26,6 +26,16 @@
Webcam
+
+
@@ -35,6 +45,7 @@ import { useDropzone, useFileInput, ProviderIcon } from '@uppy/vue'
const props = defineProps<{
openWebcamModal: () => void
+ openScreenCaptureModal: () => void
}>()
const { getRootProps, getInputProps } = useDropzone({
diff --git a/examples/vue3/src/MediaCapture.vue b/examples/vue3/src/MediaCapture.vue
new file mode 100644
index 000000000..9de828494
--- /dev/null
+++ b/examples/vue3/src/MediaCapture.vue
@@ -0,0 +1,66 @@
+
+
+
+
{{ title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/vue3/src/ScreenCapture.vue b/examples/vue3/src/ScreenCapture.vue
new file mode 100644
index 000000000..1fe1ab114
--- /dev/null
+++ b/examples/vue3/src/ScreenCapture.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
diff --git a/examples/vue3/src/Webcam.vue b/examples/vue3/src/Webcam.vue
index dec3c4e74..dc8db0dfb 100644
--- a/examples/vue3/src/Webcam.vue
+++ b/examples/vue3/src/Webcam.vue
@@ -1,53 +1,21 @@
-
-
-
Camera
-
-
-
-
-
-
-
-
-
-
-
+