forked from abi/screenshot-to-code
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
247 additions
and
29 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { useState } from "react"; | ||
import { Button } from "../ui/button"; | ||
import { ScreenRecorderState } from "../../types"; | ||
import { blobToBase64DataUrl } from "./utils"; | ||
import fixWebmDuration from "webm-duration-fix"; | ||
import toast from "react-hot-toast"; | ||
|
||
interface Props { | ||
screenRecorderState: ScreenRecorderState; | ||
setScreenRecorderState: (state: ScreenRecorderState) => void; | ||
generateCode: ( | ||
referenceImages: string[], | ||
inputMode: "image" | "video" | ||
) => void; | ||
} | ||
|
||
function ScreenRecorder({ | ||
screenRecorderState, | ||
setScreenRecorderState, | ||
generateCode, | ||
}: Props) { | ||
const [mediaRecorder, setMediaRecorder] = useState<MediaRecorder | null>( | ||
null | ||
); | ||
const [screenRecordingDataUrl, setScreenRecordingDataUrl] = useState< | ||
string | null | ||
>(null); | ||
|
||
const startScreenRecording = async () => { | ||
try { | ||
// Get the screen recording stream | ||
const stream = await navigator.mediaDevices.getDisplayMedia({ | ||
video: true, | ||
audio: { echoCancellation: true }, | ||
}); | ||
|
||
// TODO: Test across different browsers | ||
// Create the media recorder | ||
const options = { mimeType: "video/webm" }; | ||
const mediaRecorder = new MediaRecorder(stream, options); | ||
setMediaRecorder(mediaRecorder); | ||
|
||
const chunks: BlobPart[] = []; | ||
|
||
// Accumalate chunks as data is available | ||
mediaRecorder.ondataavailable = (e: BlobEvent) => chunks.push(e.data); | ||
|
||
// When media recorder is stopped, create a data URL | ||
mediaRecorder.onstop = async () => { | ||
// TODO: Do I need to fix duration if it's not a webm? | ||
const completeBlob = await fixWebmDuration( | ||
new Blob(chunks, { | ||
type: options.mimeType, | ||
}) | ||
); | ||
|
||
const dataUrl = await blobToBase64DataUrl(completeBlob); | ||
setScreenRecordingDataUrl(dataUrl); | ||
setScreenRecorderState(ScreenRecorderState.FINISHED); | ||
}; | ||
|
||
// Start recording | ||
mediaRecorder.start(); | ||
setScreenRecorderState(ScreenRecorderState.RECORDING); | ||
} catch (error) { | ||
toast.error("Could not start screen recording"); | ||
throw error; | ||
} | ||
}; | ||
|
||
const stopScreenRecording = () => { | ||
if (mediaRecorder) { | ||
mediaRecorder.stop(); | ||
setMediaRecorder(null); | ||
} | ||
}; | ||
|
||
const kickoffGeneration = () => { | ||
if (screenRecordingDataUrl) { | ||
generateCode([screenRecordingDataUrl], "video"); | ||
} else { | ||
toast.error("Screen recording does not exist. Please try again."); | ||
throw new Error("No screen recording data url"); | ||
} | ||
}; | ||
|
||
return ( | ||
<div className="flex items-center justify-center my-3"> | ||
{screenRecorderState === ScreenRecorderState.INITIAL && ( | ||
<Button onClick={startScreenRecording}>Record Screen</Button> | ||
)} | ||
|
||
{screenRecorderState === ScreenRecorderState.RECORDING && ( | ||
<div className="flex items-center flex-col gap-y-4"> | ||
<div className="flex items-center mr-2 text-xl gap-x-1"> | ||
<span className="block h-10 w-10 bg-red-600 rounded-full mr-1 animate-pulse"></span> | ||
<span>Recording...</span> | ||
</div> | ||
<Button onClick={stopScreenRecording}>Finish Recording</Button> | ||
</div> | ||
)} | ||
|
||
{screenRecorderState === ScreenRecorderState.FINISHED && ( | ||
<div className="flex items-center flex-col gap-y-4"> | ||
<div className="flex items-center mr-2 text-xl gap-x-1"> | ||
<span>Screen Recording Captured.</span> | ||
</div> | ||
<Button onClick={kickoffGeneration}>Generate</Button> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
} | ||
|
||
export default ScreenRecorder; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
export function downloadBlob(blob: Blob) { | ||
// Create a URL for the blob object | ||
const videoURL = URL.createObjectURL(blob); | ||
|
||
// Create a temporary anchor element and trigger the download | ||
const a = document.createElement("a"); | ||
a.href = videoURL; | ||
a.download = "recording.webm"; | ||
document.body.appendChild(a); | ||
a.click(); | ||
document.body.removeChild(a); | ||
|
||
// Clear object URL | ||
URL.revokeObjectURL(videoURL); | ||
} | ||
|
||
export function blobToBase64DataUrl(blob: Blob): Promise<string> { | ||
return new Promise((resolve, reject) => { | ||
const reader = new FileReader(); | ||
reader.onloadend = () => { | ||
if (reader.result) { | ||
resolve(reader.result as string); | ||
} else { | ||
reject(new Error("FileReader did not return a result.")); | ||
} | ||
}; | ||
reader.onerror = () => | ||
reject(new Error("FileReader encountered an error.")); | ||
reader.readAsDataURL(blob); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.