mirror of
https://github.com/documenso/documenso.git
synced 2025-11-10 04:22:32 +10:00
Compare commits
1 Commits
v2.0.5
...
fun/sign-w
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f4f8491fb |
@ -33,7 +33,7 @@ const config = {
|
||||
},
|
||||
swcPlugins: [['@lingui/swc-plugin', {}]],
|
||||
},
|
||||
reactStrictMode: true,
|
||||
reactStrictMode: false,
|
||||
transpilePackages: [
|
||||
'@documenso/assets',
|
||||
'@documenso/ee',
|
||||
|
||||
@ -25,9 +25,13 @@
|
||||
"@hookform/resolvers": "^3.1.0",
|
||||
"@lingui/macro": "^4.11.3",
|
||||
"@lingui/react": "^4.11.3",
|
||||
"@mediapipe/face_mesh": "^0.4.1633559619",
|
||||
"@simplewebauthn/browser": "^9.0.1",
|
||||
"@simplewebauthn/server": "^9.0.3",
|
||||
"@tanstack/react-query": "^4.29.5",
|
||||
"@tensorflow-models/face-landmarks-detection": "^1.0.6",
|
||||
"@tensorflow/tfjs": "^4.22.0",
|
||||
"@tensorflow/tfjs-backend-webgl": "^4.22.0",
|
||||
"cookie-es": "^1.0.0",
|
||||
"formidable": "^2.1.1",
|
||||
"framer-motion": "^10.12.8",
|
||||
@ -52,6 +56,7 @@
|
||||
"react-hotkeys-hook": "^4.4.1",
|
||||
"react-icons": "^4.11.0",
|
||||
"react-rnd": "^10.4.1",
|
||||
"react-webcam": "^7.2.0",
|
||||
"recharts": "^2.7.2",
|
||||
"remeda": "^2.12.1",
|
||||
"sharp": "0.32.6",
|
||||
|
||||
@ -25,6 +25,7 @@ import { SignaturePad } from '@documenso/ui/primitives/signature-pad';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
import { SigningDisclosure } from '~/components/general/signing-disclosure';
|
||||
import { NoseCanvasDrawer } from '~/components/nose-canvas-drawer';
|
||||
|
||||
import { useRequiredDocumentAuthContext } from './document-auth-provider';
|
||||
import { useRequiredSigningContext } from './provider';
|
||||
@ -70,6 +71,8 @@ export const SignatureField = ({
|
||||
|
||||
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
||||
|
||||
const [isDrawing, setIsDrawing] = useState(false);
|
||||
|
||||
const [showSignatureModal, setShowSignatureModal] = useState(false);
|
||||
const [localSignature, setLocalSignature] = useState<string | null>(null);
|
||||
|
||||
@ -225,12 +228,16 @@ export const SignatureField = ({
|
||||
<Trans>Signature</Trans>
|
||||
</Label>
|
||||
|
||||
<SignaturePad
|
||||
id="signature"
|
||||
className="border-border mt-2 h-44 w-full rounded-md border"
|
||||
onChange={(value) => setLocalSignature(value)}
|
||||
allowTypedSignature={typedSignatureEnabled}
|
||||
/>
|
||||
<div className="mt-4">
|
||||
<NoseCanvasDrawer
|
||||
className="h-[320px]"
|
||||
onStart={() => setIsDrawing(true)}
|
||||
onStop={() => setIsDrawing(false)}
|
||||
onCapture={(dataUrl) => {
|
||||
setLocalSignature(dataUrl);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SigningDisclosure />
|
||||
@ -250,7 +257,7 @@ export const SignatureField = ({
|
||||
<Button
|
||||
type="button"
|
||||
className="flex-1"
|
||||
disabled={!localSignature}
|
||||
disabled={!localSignature || isDrawing}
|
||||
onClick={() => onDialogSignClick()}
|
||||
>
|
||||
<Trans>Sign</Trans>
|
||||
|
||||
10
apps/web/src/app/demo/nose-drawer/layout.tsx
Normal file
10
apps/web/src/app/demo/nose-drawer/layout.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Nose Drawing Demo',
|
||||
description: 'Draw with your nose using face detection technology',
|
||||
};
|
||||
|
||||
export default function NoseDrawerLayout({ children }: { children: React.ReactNode }) {
|
||||
return children;
|
||||
}
|
||||
60
apps/web/src/app/demo/nose-drawer/page.tsx
Normal file
60
apps/web/src/app/demo/nose-drawer/page.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
import { NoseCanvasDrawer } from '~/components/nose-canvas-drawer';
|
||||
|
||||
export default function NoseDrawerDemo() {
|
||||
const [capturedImage, setCapturedImage] = useState<string | null>(null);
|
||||
|
||||
const handleCapture = (dataUrl: string) => {
|
||||
setCapturedImage(dataUrl);
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="container mx-auto p-4">
|
||||
<div className="mx-auto max-w-4xl">
|
||||
<h1 className="mb-6 text-3xl font-bold">Nose Drawing Demo</h1>
|
||||
|
||||
<div className="space-y-8">
|
||||
{/* Instructions */}
|
||||
<div className="bg-muted rounded-lg p-4">
|
||||
<h2 className="mb-2 font-semibold">How to use:</h2>
|
||||
<ol className="list-inside list-decimal space-y-2">
|
||||
<li>Click "Play" to start your camera</li>
|
||||
<li>Move your nose to draw on the canvas</li>
|
||||
<li>Click "Export as PNG" to save your drawing</li>
|
||||
<li>Use "Clear" to start over</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
{/* Canvas drawer */}
|
||||
<div className="bg-background rounded-lg border p-4">
|
||||
<NoseCanvasDrawer onCapture={handleCapture} />
|
||||
</div>
|
||||
|
||||
{/* Preview captured image */}
|
||||
{capturedImage && (
|
||||
<div className="rounded-lg border p-4">
|
||||
<h2 className="mb-4 font-semibold">Captured Drawing</h2>
|
||||
<img
|
||||
src={capturedImage}
|
||||
alt="Captured nose drawing"
|
||||
className="max-w-full rounded-lg"
|
||||
/>
|
||||
<div className="mt-4">
|
||||
<a
|
||||
href={capturedImage}
|
||||
download="nose-drawing.png"
|
||||
className="text-primary hover:underline"
|
||||
>
|
||||
Download Image
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
267
apps/web/src/components/nose-canvas-drawer.tsx
Normal file
267
apps/web/src/components/nose-canvas-drawer.tsx
Normal file
@ -0,0 +1,267 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import * as faceLandmarksDetection from '@tensorflow-models/face-landmarks-detection';
|
||||
import * as tf from '@tensorflow/tfjs';
|
||||
import '@tensorflow/tfjs-backend-webgl';
|
||||
import { Play, Square, X } from 'lucide-react';
|
||||
import type { StrokeOptions } from 'perfect-freehand';
|
||||
import { getStroke } from 'perfect-freehand';
|
||||
import Webcam from 'react-webcam';
|
||||
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import { getSvgPathFromStroke } from '@documenso/ui/primitives/signature-pad/helper';
|
||||
|
||||
export type NoseCanvasDrawerProps = {
|
||||
className?: string;
|
||||
onStart?: () => void;
|
||||
onStop?: () => void;
|
||||
onCapture?: (dataUrl: string) => void;
|
||||
};
|
||||
|
||||
export const NoseCanvasDrawer = ({
|
||||
className,
|
||||
onStart,
|
||||
onStop,
|
||||
onCapture,
|
||||
}: NoseCanvasDrawerProps) => {
|
||||
const $el = useRef<HTMLDivElement>(null);
|
||||
|
||||
const $webcam = useRef<Webcam>(null);
|
||||
const $canvas = useRef<HTMLCanvasElement>(null);
|
||||
|
||||
const $detector = useRef<faceLandmarksDetection.FaceLandmarksDetector | null>(null);
|
||||
const $animationFrameId = useRef<number | null>(null);
|
||||
|
||||
const $previousNosePosition = useRef<{ x: number; y: number } | null>(null);
|
||||
const $lines = useRef<{ x: number; y: number }[]>([]);
|
||||
|
||||
const $scaleFactor = useRef(1);
|
||||
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const onTogglePlayingClick = () => {
|
||||
setIsPlaying((playing) => {
|
||||
if (playing && $animationFrameId.current) {
|
||||
cancelAnimationFrame($animationFrameId.current);
|
||||
|
||||
if ($canvas.current) {
|
||||
const ctx = $canvas.current.getContext('2d');
|
||||
|
||||
if (ctx) {
|
||||
ctx.save();
|
||||
|
||||
onCapture?.($canvas.current.toDataURL('image/png'));
|
||||
}
|
||||
|
||||
$lines.current = [];
|
||||
}
|
||||
}
|
||||
|
||||
return !playing;
|
||||
});
|
||||
};
|
||||
|
||||
const onClearClick = () => {
|
||||
if (isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($canvas.current) {
|
||||
const ctx = $canvas.current.getContext('2d');
|
||||
|
||||
if (ctx) {
|
||||
ctx.clearRect(0, 0, $canvas.current.width, $canvas.current.height);
|
||||
ctx.save();
|
||||
|
||||
onCapture?.($canvas.current.toDataURL('image/png'));
|
||||
}
|
||||
}
|
||||
|
||||
$lines.current = [];
|
||||
};
|
||||
|
||||
const loadModel = async () => {
|
||||
await tf.ready();
|
||||
|
||||
return await faceLandmarksDetection.createDetector(
|
||||
faceLandmarksDetection.SupportedModels.MediaPipeFaceMesh,
|
||||
{
|
||||
runtime: 'mediapipe',
|
||||
solutionPath: 'https://cdn.jsdelivr.net/npm/@mediapipe/face_mesh',
|
||||
refineLandmarks: true,
|
||||
maxFaces: 1,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const detectAndDraw = async () => {
|
||||
if (!$detector.current || !$canvas.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const canvas = $canvas.current;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
const video = $webcam.current?.video;
|
||||
|
||||
if (!video) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('about to predict');
|
||||
|
||||
const predictions = await $detector.current.estimateFaces(video, {
|
||||
flipHorizontal: true,
|
||||
staticImageMode: false,
|
||||
});
|
||||
|
||||
console.log({ predictions });
|
||||
|
||||
if (predictions.length > 0) {
|
||||
const keypoints = predictions[0].keypoints;
|
||||
const nose = keypoints[1]; // Nose tip keypoint
|
||||
|
||||
const currentPosition = {
|
||||
x: nose.x * $scaleFactor.current,
|
||||
y: nose.y * $scaleFactor.current,
|
||||
};
|
||||
|
||||
if ($previousNosePosition.current) {
|
||||
$lines.current.push(currentPosition);
|
||||
|
||||
ctx.restore();
|
||||
|
||||
ctx.imageSmoothingEnabled = true;
|
||||
ctx.imageSmoothingQuality = 'high';
|
||||
ctx.fillStyle = 'red';
|
||||
|
||||
const strokeOptions: StrokeOptions = {
|
||||
size: 5,
|
||||
thinning: 0.25,
|
||||
streamline: 0.5,
|
||||
smoothing: 0.5,
|
||||
end: {
|
||||
taper: 5,
|
||||
},
|
||||
};
|
||||
|
||||
const pathData = new Path2D(getSvgPathFromStroke(getStroke($lines.current, strokeOptions)));
|
||||
|
||||
ctx.fill(pathData);
|
||||
|
||||
ctx.save();
|
||||
}
|
||||
|
||||
$previousNosePosition.current = currentPosition;
|
||||
} else {
|
||||
$previousNosePosition.current = null;
|
||||
}
|
||||
|
||||
$animationFrameId.current = requestAnimationFrame(() => void detectAndDraw());
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoading(true);
|
||||
|
||||
void loadModel().then((model) => {
|
||||
$detector.current = model;
|
||||
setIsLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) {
|
||||
void detectAndDraw();
|
||||
|
||||
onStart?.();
|
||||
} else {
|
||||
onStop?.();
|
||||
}
|
||||
}, [isPlaying]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!$webcam.current?.video) {
|
||||
return;
|
||||
}
|
||||
|
||||
const observer = new ResizeObserver((_entries) => {
|
||||
if ($webcam.current?.video) {
|
||||
const videoWidth = $webcam.current.video.videoWidth;
|
||||
const videoHeight = $webcam.current.video.videoHeight;
|
||||
|
||||
const { width, height } = $webcam.current.video.getBoundingClientRect();
|
||||
|
||||
$scaleFactor.current = Math.min(width / videoWidth, height / videoHeight);
|
||||
|
||||
setIsPlaying(false);
|
||||
|
||||
if ($animationFrameId.current) {
|
||||
cancelAnimationFrame($animationFrameId.current);
|
||||
}
|
||||
|
||||
onClearClick();
|
||||
|
||||
if ($canvas.current) {
|
||||
console.log('resizing canvas');
|
||||
$canvas.current.width = width;
|
||||
$canvas.current.height = height;
|
||||
|
||||
const ctx = $canvas.current.getContext('2d');
|
||||
|
||||
if (ctx) {
|
||||
ctx.moveTo(0, 0);
|
||||
|
||||
ctx.save();
|
||||
ctx.scale(-1, 1);
|
||||
ctx.drawImage($webcam.current.video, 0, 0, width, height);
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe($webcam.current.video);
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={$el} className={cn('relative inline-block aspect-[4/3] h-full', className)}>
|
||||
<Webcam ref={$webcam} videoConstraints={{ facingMode: 'user' }} className="scale-x-[-1]" />
|
||||
|
||||
<canvas ref={$canvas} className="absolute inset-0 z-10" />
|
||||
|
||||
<div className="absolute bottom-2 right-2 z-20 flex items-center gap-x-2">
|
||||
<Button
|
||||
disabled={isLoading}
|
||||
onClick={onTogglePlayingClick}
|
||||
className="text-primary-foreground/80 h-8 w-8 rounded-full p-0"
|
||||
>
|
||||
{isPlaying ? <Square className="h-4 w-4" /> : <Play className="-mr-0.5 h-4 w-4" />}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
disabled={isLoading || isPlaying}
|
||||
onClick={onClearClick}
|
||||
className="text-primary-foreground/80 h-8 w-8 rounded-full p-0"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
3
apps/web/src/styles/globals.css
Normal file
3
apps/web/src/styles/globals.css
Normal file
@ -0,0 +1,3 @@
|
||||
.mirror {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
393
package-lock.json
generated
393
package-lock.json
generated
@ -454,9 +454,13 @@
|
||||
"@hookform/resolvers": "^3.1.0",
|
||||
"@lingui/macro": "^4.11.3",
|
||||
"@lingui/react": "^4.11.3",
|
||||
"@mediapipe/face_mesh": "^0.4.1633559619",
|
||||
"@simplewebauthn/browser": "^9.0.1",
|
||||
"@simplewebauthn/server": "^9.0.3",
|
||||
"@tanstack/react-query": "^4.29.5",
|
||||
"@tensorflow-models/face-landmarks-detection": "^1.0.6",
|
||||
"@tensorflow/tfjs": "^4.22.0",
|
||||
"@tensorflow/tfjs-backend-webgl": "^4.22.0",
|
||||
"cookie-es": "^1.0.0",
|
||||
"formidable": "^2.1.1",
|
||||
"framer-motion": "^10.12.8",
|
||||
@ -481,6 +485,7 @@
|
||||
"react-hotkeys-hook": "^4.4.1",
|
||||
"react-icons": "^4.11.0",
|
||||
"react-rnd": "^10.4.1",
|
||||
"react-webcam": "^7.2.0",
|
||||
"recharts": "^2.7.2",
|
||||
"remeda": "^2.12.1",
|
||||
"sharp": "0.32.6",
|
||||
@ -4894,6 +4899,19 @@
|
||||
"react": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@mediapipe/face_detection": {
|
||||
"version": "0.4.1646425229",
|
||||
"resolved": "https://registry.npmjs.org/@mediapipe/face_detection/-/face_detection-0.4.1646425229.tgz",
|
||||
"integrity": "sha512-aeCN+fRAojv9ch3NXorP6r5tcGVLR3/gC1HmtqB0WEZBRXrdP6/3W/sGR0dHr1iT6ueiK95G9PVjbzFosf/hrg==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@mediapipe/face_mesh": {
|
||||
"version": "0.4.1633559619",
|
||||
"resolved": "https://registry.npmjs.org/@mediapipe/face_mesh/-/face_mesh-0.4.1633559619.tgz",
|
||||
"integrity": "sha512-Vc8cdjxS5+O2gnjWH9KncYpUCVXT0h714KlWAsyqJvJbIgUJBqpppbIx8yWcAzBDxm/5cYSuBI5p5ySIPxzcEg==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@messageformat/parser": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@messageformat/parser/-/parser-5.1.0.tgz",
|
||||
@ -10158,6 +10176,328 @@
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow-models/face-detection": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tensorflow-models/face-detection/-/face-detection-1.0.3.tgz",
|
||||
"integrity": "sha512-4Ld/vFF8MrdFdrMWhlLKZD4hMW0PNY9OkYeqoCPNZ+LwFyenxAqVaNaWrR8JKp37vw9Nuzp4ILbkal5zPUnA0g==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"rimraf": "^3.0.2",
|
||||
"tslib": "2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@mediapipe/face_detection": "~0.4.0",
|
||||
"@tensorflow/tfjs-backend-webgl": "^4.21.0",
|
||||
"@tensorflow/tfjs-converter": "^4.21.0",
|
||||
"@tensorflow/tfjs-core": "^4.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow-models/face-detection/node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow-models/face-detection/node_modules/tslib": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@tensorflow-models/face-landmarks-detection": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@tensorflow-models/face-landmarks-detection/-/face-landmarks-detection-1.0.6.tgz",
|
||||
"integrity": "sha512-CwcKcTwk/7PZ5f+9POi6dJV1osa6FvpxPduW9zw/6q0AmMhbdexTZ17qzG9SXdPONuakV1fPaiZNkXXXUDajdw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"rimraf": "^3.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@mediapipe/face_mesh": "~0.4.0",
|
||||
"@tensorflow-models/face-detection": "^1.0.3",
|
||||
"@tensorflow/tfjs-backend-webgl": "^4.13.0",
|
||||
"@tensorflow/tfjs-converter": "^4.13.0",
|
||||
"@tensorflow/tfjs-core": "^4.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow-models/face-landmarks-detection/node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs": {
|
||||
"version": "4.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-4.22.0.tgz",
|
||||
"integrity": "sha512-0TrIrXs6/b7FLhLVNmfh8Sah6JgjBPH4mZ8JGb7NU6WW+cx00qK5BcAZxw7NCzxj6N8MRAIfHq+oNbPUNG5VAg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tensorflow/tfjs-backend-cpu": "4.22.0",
|
||||
"@tensorflow/tfjs-backend-webgl": "4.22.0",
|
||||
"@tensorflow/tfjs-converter": "4.22.0",
|
||||
"@tensorflow/tfjs-core": "4.22.0",
|
||||
"@tensorflow/tfjs-data": "4.22.0",
|
||||
"@tensorflow/tfjs-layers": "4.22.0",
|
||||
"argparse": "^1.0.10",
|
||||
"chalk": "^4.1.0",
|
||||
"core-js": "3.29.1",
|
||||
"regenerator-runtime": "^0.13.5",
|
||||
"yargs": "^16.0.3"
|
||||
},
|
||||
"bin": {
|
||||
"tfjs-custom-module": "dist/tools/custom_module/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs-backend-cpu": {
|
||||
"version": "4.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-cpu/-/tfjs-backend-cpu-4.22.0.tgz",
|
||||
"integrity": "sha512-1u0FmuLGuRAi8D2c3cocHTASGXOmHc/4OvoVDENJayjYkS119fcTcQf4iHrtLthWyDIPy3JiPhRrZQC9EwnhLw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/seedrandom": "^2.4.28",
|
||||
"seedrandom": "^3.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"yarn": ">= 1.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tensorflow/tfjs-core": "4.22.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs-backend-webgl": {
|
||||
"version": "4.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-webgl/-/tfjs-backend-webgl-4.22.0.tgz",
|
||||
"integrity": "sha512-H535XtZWnWgNwSzv538czjVlbJebDl5QTMOth4RXr2p/kJ1qSIXE0vZvEtO+5EC9b00SvhplECny2yDewQb/Yg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tensorflow/tfjs-backend-cpu": "4.22.0",
|
||||
"@types/offscreencanvas": "~2019.3.0",
|
||||
"@types/seedrandom": "^2.4.28",
|
||||
"seedrandom": "^3.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"yarn": ">= 1.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tensorflow/tfjs-core": "4.22.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs-converter": {
|
||||
"version": "4.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-4.22.0.tgz",
|
||||
"integrity": "sha512-PT43MGlnzIo+YfbsjM79Lxk9lOq6uUwZuCc8rrp0hfpLjF6Jv8jS84u2jFb+WpUeuF4K33ZDNx8CjiYrGQ2trQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"@tensorflow/tfjs-core": "4.22.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs-core": {
|
||||
"version": "4.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-4.22.0.tgz",
|
||||
"integrity": "sha512-LEkOyzbknKFoWUwfkr59vSB68DMJ4cjwwHgicXN0DUi3a0Vh1Er3JQqCI1Hl86GGZQvY8ezVrtDIvqR1ZFW55A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/long": "^4.0.1",
|
||||
"@types/offscreencanvas": "~2019.7.0",
|
||||
"@types/seedrandom": "^2.4.28",
|
||||
"@webgpu/types": "0.1.38",
|
||||
"long": "4.0.0",
|
||||
"node-fetch": "~2.6.1",
|
||||
"seedrandom": "^3.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"yarn": ">= 1.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs-core/node_modules/@types/offscreencanvas": {
|
||||
"version": "2019.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz",
|
||||
"integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs-core/node_modules/long": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs-core/node_modules/node-fetch": {
|
||||
"version": "2.6.13",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz",
|
||||
"integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs-data": {
|
||||
"version": "4.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-data/-/tfjs-data-4.22.0.tgz",
|
||||
"integrity": "sha512-dYmF3LihQIGvtgJrt382hSRH4S0QuAp2w1hXJI2+kOaEqo5HnUPG0k5KA6va+S1yUhx7UBToUKCBHeLHFQRV4w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/node-fetch": "^2.1.2",
|
||||
"node-fetch": "~2.6.1",
|
||||
"string_decoder": "^1.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tensorflow/tfjs-core": "4.22.0",
|
||||
"seedrandom": "^3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs-data/node_modules/node-fetch": {
|
||||
"version": "2.6.13",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz",
|
||||
"integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs-layers": {
|
||||
"version": "4.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-4.22.0.tgz",
|
||||
"integrity": "sha512-lybPj4ZNj9iIAPUj7a8ZW1hg8KQGfqWLlCZDi9eM/oNKCCAgchiyzx8OrYoWmRrB+AM6VNEeIT+2gZKg5ReihA==",
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"peerDependencies": {
|
||||
"@tensorflow/tfjs-core": "4.22.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs/node_modules/argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs/node_modules/cliui": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs/node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs/node_modules/regenerator-runtime": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs/node_modules/wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@tensorflow/tfjs/node_modules/yargs": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cliui": "^7.0.2",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.0",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^20.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@theguild/remark-mermaid": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@theguild/remark-mermaid/-/remark-mermaid-0.0.5.tgz",
|
||||
@ -11184,6 +11524,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/long": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
|
||||
"integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/luxon": {
|
||||
"version": "3.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.5.tgz",
|
||||
@ -11228,7 +11574,6 @@
|
||||
"version": "2.6.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz",
|
||||
"integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"form-data": "^4.0.0"
|
||||
@ -11248,6 +11593,12 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
|
||||
"integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="
|
||||
},
|
||||
"node_modules/@types/offscreencanvas": {
|
||||
"version": "2019.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz",
|
||||
"integrity": "sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/papaparse": {
|
||||
"version": "5.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.14.tgz",
|
||||
@ -11392,6 +11743,12 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
|
||||
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A=="
|
||||
},
|
||||
"node_modules/@types/seedrandom": {
|
||||
"version": "2.4.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.34.tgz",
|
||||
"integrity": "sha512-ytDiArvrn/3Xk6/vtylys5tlY6eo7Ane0hvcx++TKo6RxQXuVfW0AF/oeWqAj9dN29SyhtawuXstgmPlwNcv/A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz",
|
||||
@ -11803,6 +12160,12 @@
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@webgpu/types": {
|
||||
"version": "0.1.38",
|
||||
"resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.38.tgz",
|
||||
"integrity": "sha512-7LrhVKz2PRh+DD7+S+PVaFd5HxaWQvoMqBbsV9fNJO1pjUs1P8bM2vQVNfk+3URTqbuTI7gkXi0rfsN0IadoBA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@xtuc/ieee754": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||
@ -14587,6 +14950,17 @@
|
||||
"toggle-selection": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "3.29.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.1.tgz",
|
||||
"integrity": "sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/core-js-pure": {
|
||||
"version": "3.35.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.35.0.tgz",
|
||||
@ -28797,6 +29171,16 @@
|
||||
"react-dom": ">=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-webcam": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-webcam/-/react-webcam-7.2.0.tgz",
|
||||
"integrity": "sha512-xkrzYPqa1ag2DP+2Q/kLKBmCIfEx49bVdgCCCcZf88oF+0NPEbkwYk3/s/C7Zy0mhM8k+hpdNkBLzxg8H0aWcg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.2.0",
|
||||
"react-dom": ">=16.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
@ -30651,6 +31035,12 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/seedrandom": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz",
|
||||
"integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/selderee": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz",
|
||||
@ -35369,7 +35759,6 @@
|
||||
"version": "20.2.9",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
|
||||
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user