From ebacae72dc3a1e53f52e19fbce8eed910f61bfef Mon Sep 17 00:00:00 2001 From: proCreator_01 <82655626+AaryakCreator@users.noreply.github.com> Date: Sun, 22 Mar 2026 17:40:03 +0530 Subject: [PATCH 01/14] Update runanywhere.ts --- src/runanywhere.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/runanywhere.ts b/src/runanywhere.ts index 55ecee5b..8755a47c 100644 --- a/src/runanywhere.ts +++ b/src/runanywhere.ts @@ -61,6 +61,16 @@ const MODELS: CompactModelDef[] = [ modality: ModelCategory.Multimodal, memoryRequirement: 500_000_000, }, + // VLM - Meta Pose2D + { + id: 'meta-pose2d', + name: 'Meta Pose2D', + repo: 'facebook/sapiens-pose-0.3b-GGUF', + files: ['sapiens_0.3b_goliath_best_goliath_AP_573.pth'], + framework: LLMFramework.LlamaCpp, + modality: ModelCategory.Multimodal, + memoryRequirement: 500_000_000, + }, // STT (sherpa-onnx archive) { id: 'sherpa-onnx-whisper-tiny.en', From f662071d97a8e09adc9d2e136a42673d5a0cc9b2 Mon Sep 17 00:00:00 2001 From: Himanshu Ranjan Date: Sun, 22 Mar 2026 18:01:37 +0530 Subject: [PATCH 02/14] atlast_working --- index.html | 2 +- package-lock.json | 7 + package.json | 1 + src/App.tsx | 15 +- src/components/FitnessTab.tsx | 545 ++++++++++++++++++++++++++++++++++ src/fitness/poseEngine.ts | 358 ++++++++++++++++++++++ src/styles/index.css | 490 ++++++++++++++++++++++++++++++ 7 files changed, 1412 insertions(+), 6 deletions(-) create mode 100644 src/components/FitnessTab.tsx create mode 100644 src/fitness/poseEngine.ts diff --git a/index.html b/index.html index b5bfe7c6..22c83192 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ - RunAnywhere AI Starter + Kine-Sight AI Fitness Trainer diff --git a/package-lock.json b/package-lock.json index 8a57a88a..c12f5066 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "runanywhere-web-starter", "version": "0.1.0", "dependencies": { + "@mediapipe/tasks-vision": "^0.10.33", "@runanywhere/web": "0.1.0-beta.10", "@runanywhere/web-llamacpp": "0.1.0-beta.10", "@runanywhere/web-onnx": "0.1.0-beta.10", @@ -796,6 +797,12 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mediapipe/tasks-vision": { + "version": "0.10.33", + "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.33.tgz", + "integrity": "sha512-Hh41r6ezzz6aeT1e3aJHjLaeVKtz7yhHLjVMQpoxg2fEgA8HRByS2KNMU28ahtDjJRJNCyDHWb2tJWVAyshyMw==", + "license": "Apache-2.0" + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.27", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", diff --git a/package.json b/package.json index abb9209a..f3537a45 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "preview": "vite preview" }, "dependencies": { + "@mediapipe/tasks-vision": "^0.10.33", "@runanywhere/web": "0.1.0-beta.10", "@runanywhere/web-llamacpp": "0.1.0-beta.10", "@runanywhere/web-onnx": "0.1.0-beta.10", diff --git a/src/App.tsx b/src/App.tsx index 01de1ab6..5d67113d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,16 +1,17 @@ import { useState, useEffect } from 'react'; import { initSDK, getAccelerationMode } from './runanywhere'; +import { FitnessTab } from './components/FitnessTab'; import { ChatTab } from './components/ChatTab'; import { VisionTab } from './components/VisionTab'; import { VoiceTab } from './components/VoiceTab'; import { ToolsTab } from './components/ToolsTab'; -type Tab = 'chat' | 'vision' | 'voice' | 'tools'; +type Tab = 'fitness' | 'chat' | 'vision' | 'voice' | 'tools'; export function App() { const [sdkReady, setSdkReady] = useState(false); const [sdkError, setSdkError] = useState(null); - const [activeTab, setActiveTab] = useState('chat'); + const [activeTab, setActiveTab] = useState('fitness'); useEffect(() => { initSDK() @@ -31,8 +32,8 @@ export function App() { return (
-

Loading RunAnywhere SDK...

-

Initializing on-device AI engine

+

Loading Kine-Sight AI...

+

Initializing on-device AI fitness engine

); } @@ -42,11 +43,14 @@ export function App() { return (
-

RunAnywhere AI

+

Kine-Sight

{accel && {accel === 'webgpu' ? 'WebGPU' : 'CPU'}}
+ {activeTab === 'fitness' && } {activeTab === 'chat' && } {activeTab === 'vision' && } {activeTab === 'voice' && } diff --git a/src/components/FitnessTab.tsx b/src/components/FitnessTab.tsx new file mode 100644 index 00000000..0af5a8d6 --- /dev/null +++ b/src/components/FitnessTab.tsx @@ -0,0 +1,545 @@ +import { useState, useRef, useEffect, useCallback } from 'react'; +import { + EXERCISES, + getPoseLandmarker, + drawSkeleton, + drawAngleBadge, + LM, +} from '../fitness/poseEngine'; +import type { ExerciseDef, PoseAnalysis, FormQuality } from '../fitness/poseEngine'; + +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- +const CANVAS_FPS = 30; + +// --------------------------------------------------------------------------- +// Types +// --------------------------------------------------------------------------- +type RepPhase = 'idle' | 'up' | 'down'; + +interface SessionStats { + correctReps: number; + incorrectReps: number; + startTime: number; + formHistory: FormQuality[]; +} + +// --------------------------------------------------------------------------- +// Component +// --------------------------------------------------------------------------- +export function FitnessTab() { + // Exercise selection + const [selectedExercise, setSelectedExercise] = useState(null); + + // Camera + const [cameraActive, setCameraActive] = useState(false); + const [error, setError] = useState(null); + + // Pose engine + const [poseReady, setPoseReady] = useState(false); + const [poseLoading, setPoseLoading] = useState(false); + + // Session + const [isWorkout, setIsWorkout] = useState(false); + const [phase, setPhase] = useState('idle'); + const [formQuality, setFormQuality] = useState('unknown'); + const [feedback, setFeedback] = useState(''); + const [currentAngle, setCurrentAngle] = useState(0); + const [stats, setStats] = useState({ + correctReps: 0, + incorrectReps: 0, + startTime: 0, + formHistory: [], + }); + const [elapsed, setElapsed] = useState(0); + + // Refs + const videoRef = useRef(null); + const canvasRef = useRef(null); + const streamRef = useRef(null); + const rafRef = useRef(0); + const timerRef = useRef | null>(null); + const phaseRef = useRef('idle'); + const formDuringRepRef = useRef(true); + const isWorkoutRef = useRef(false); + const statsRef = useRef({ correctReps: 0, incorrectReps: 0, startTime: 0, formHistory: [] }); + const lastTimeRef = useRef(0); + const exerciseRef = useRef(null); + + // Keep refs in sync + phaseRef.current = phase; + isWorkoutRef.current = isWorkout; + exerciseRef.current = selectedExercise; + + // ------------------------------------------------------------------ + // Init MediaPipe Pose Landmarker + // ------------------------------------------------------------------ + const initPose = useCallback(async () => { + if (poseReady || poseLoading) return; + setPoseLoading(true); + setError(null); + try { + await getPoseLandmarker(); + setPoseReady(true); + } catch (err) { + setError(`Failed to load pose model: ${err instanceof Error ? err.message : String(err)}`); + } finally { + setPoseLoading(false); + } + }, [poseReady, poseLoading]); + + // ------------------------------------------------------------------ + // Camera โ€” native getUserMedia + // ------------------------------------------------------------------ + const startCamera = useCallback(async () => { + setError(null); + try { + const stream = await navigator.mediaDevices.getUserMedia({ + video: { width: { ideal: 640 }, height: { ideal: 480 } }, + audio: false, + }); + streamRef.current = stream; + + const video = videoRef.current; + if (video) { + video.srcObject = stream; + await video.play(); + } + setCameraActive(true); + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + if (msg.includes('NotAllowed') || msg.includes('Permission')) { + setError('Camera permission denied. Please allow camera access.'); + } else if (msg.includes('NotFound') || msg.includes('DevicesNotFound')) { + setError('No camera found on this device.'); + } else { + setError(`Camera error: ${msg}`); + } + } + }, []); + + const stopCamera = useCallback(() => { + const stream = streamRef.current; + if (stream) { + stream.getTracks().forEach(t => t.stop()); + streamRef.current = null; + } + const video = videoRef.current; + if (video) { + video.srcObject = null; + } + setCameraActive(false); + }, []); + + // Cleanup on unmount + useEffect(() => { + return () => { + if (rafRef.current) cancelAnimationFrame(rafRef.current); + if (timerRef.current) clearInterval(timerRef.current); + const stream = streamRef.current; + if (stream) stream.getTracks().forEach(t => t.stop()); + }; + }, []); + + // ------------------------------------------------------------------ + // Pose detection loop (requestAnimationFrame) + // ------------------------------------------------------------------ + const startDetectionLoop = useCallback(() => { + const video = videoRef.current; + const canvas = canvasRef.current; + if (!video || !canvas) return; + + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + const detect = async () => { + if (!isWorkoutRef.current) return; + + const now = performance.now(); + // Throttle to ~CANVAS_FPS + if (now - lastTimeRef.current < 1000 / CANVAS_FPS) { + rafRef.current = requestAnimationFrame(detect); + return; + } + lastTimeRef.current = now; + + const landmarker = await getPoseLandmarker(); + const exercise = exerciseRef.current; + + if (!exercise || video.readyState < 2) { + rafRef.current = requestAnimationFrame(detect); + return; + } + + // Resize canvas to match video + canvas.width = video.videoWidth; + canvas.height = video.videoHeight; + + // Run pose detection + const results = landmarker.detectForVideo(video, now); + + // Clear and draw video frame (mirrored) + ctx.save(); + ctx.translate(canvas.width, 0); + ctx.scale(-1, 1); + ctx.drawImage(video, 0, 0, canvas.width, canvas.height); + ctx.restore(); + + if (results.landmarks && results.landmarks.length > 0) { + const landmarks = results.landmarks[0]; + + // Mirror landmarks for display + const mirroredLandmarks = landmarks.map(lm => ({ + ...lm, + x: 1 - lm.x, + })); + + // Analyze exercise with ORIGINAL landmarks (not mirrored) + const analysis: PoseAnalysis = exercise.analyze(landmarks); + + // Draw skeleton with mirrored landmarks + const formColor = analysis.form === 'good' ? '#22C55E' + : analysis.form === 'bad' ? '#EF4444' : '#F59E0B'; + drawSkeleton(ctx, mirroredLandmarks, canvas.width, canvas.height, formColor); + + // Draw angle badge on the relevant joint (mirrored) + const angleLandmarkIndex = getAngleLandmarkIndex(exercise.id); + if (angleLandmarkIndex >= 0) { + drawAngleBadge(ctx, analysis.angle, mirroredLandmarks[angleLandmarkIndex], canvas.width, canvas.height); + } + + // Update UI state + setCurrentAngle(analysis.angle); + setFormQuality(analysis.form); + setFeedback(analysis.feedback); + + if (analysis.form === 'bad') { + formDuringRepRef.current = false; + } + + // Rep counting state machine + if (exercise.id !== 'plank') { + const prevPhase = phaseRef.current; + + if (analysis.position === 'down' && (prevPhase === 'up' || prevPhase === 'idle')) { + setPhase('down'); + phaseRef.current = 'down'; + } else if (analysis.position === 'up') { + if (prevPhase === 'down') { + // Rep completed! (down โ†’ up) + setPhase('up'); + phaseRef.current = 'up'; + + const isCorrect = formDuringRepRef.current; + const newStats: SessionStats = { + ...statsRef.current, + correctReps: statsRef.current.correctReps + (isCorrect ? 1 : 0), + incorrectReps: statsRef.current.incorrectReps + (isCorrect ? 0 : 1), + formHistory: [...statsRef.current.formHistory, isCorrect ? 'good' : 'bad'], + }; + statsRef.current = newStats; + setStats(newStats); + + const repNum = newStats.correctReps + newStats.incorrectReps; + setFeedback(isCorrect + ? `๐ŸŽ‰ Rep ${repNum} โ€” Perfect form!` + : `โš ๏ธ Rep ${repNum} โ€” Work on form!`); + + formDuringRepRef.current = true; + } else if (prevPhase === 'idle') { + setPhase('up'); + phaseRef.current = 'up'; + setFeedback('๐ŸŽฏ Ready! Start your reps!'); + } + } + } + } else { + // No pose detected โ€” just draw the video + // (already drawn above) + setFeedback('๐Ÿ” Step into frame so I can see you!'); + } + + rafRef.current = requestAnimationFrame(detect); + }; + + rafRef.current = requestAnimationFrame(detect); + }, []); + + // ------------------------------------------------------------------ + // Get the landmark index where the angle badge should be drawn + // ------------------------------------------------------------------ + function getAngleLandmarkIndex(exerciseId: string): number { + switch (exerciseId) { + case 'squats': return LM.LEFT_KNEE; + case 'bicep-curls': return LM.LEFT_ELBOW; + case 'pushups': return LM.LEFT_ELBOW; + case 'lunges': return LM.LEFT_KNEE; + case 'shoulder-press': return LM.LEFT_ELBOW; + case 'plank': return LM.LEFT_HIP; + default: return -1; + } + } + + // ------------------------------------------------------------------ + // Workout session management + // ------------------------------------------------------------------ + const startWorkout = useCallback(async (exercise: ExerciseDef) => { + setSelectedExercise(exercise); + exerciseRef.current = exercise; + + // Init pose engine if needed + if (!poseReady) { + setPoseLoading(true); + try { + await getPoseLandmarker(); + setPoseReady(true); + } catch (err) { + setError(`Failed to load pose model: ${err instanceof Error ? err.message : String(err)}`); + setPoseLoading(false); + return; + } + setPoseLoading(false); + } + + // Start camera if needed + if (!streamRef.current) { + await startCamera(); + } + + setIsWorkout(true); + isWorkoutRef.current = true; + setPhase('idle'); + phaseRef.current = 'idle'; + setFormQuality('unknown'); + setFeedback('๐ŸŽฏ Get into position! Pose detection starting...'); + formDuringRepRef.current = true; + const freshStats: SessionStats = { + correctReps: 0, + incorrectReps: 0, + startTime: Date.now(), + formHistory: [], + }; + setStats(freshStats); + statsRef.current = freshStats; + setElapsed(0); + + // Start detection loop + startDetectionLoop(); + + // Start elapsed timer + timerRef.current = setInterval(() => { + setElapsed(prev => prev + 1); + }, 1000); + }, [poseReady, startCamera, startDetectionLoop]); + + const stopWorkout = useCallback(() => { + setIsWorkout(false); + isWorkoutRef.current = false; + setPhase('idle'); + phaseRef.current = 'idle'; + + if (rafRef.current) { + cancelAnimationFrame(rafRef.current); + rafRef.current = 0; + } + if (timerRef.current) { + clearInterval(timerRef.current); + timerRef.current = null; + } + }, []); + + const resetSession = useCallback(() => { + stopWorkout(); + stopCamera(); + setSelectedExercise(null); + exerciseRef.current = null; + setFormQuality('unknown'); + setFeedback(''); + setError(null); + setCurrentAngle(0); + const freshStats: SessionStats = { correctReps: 0, incorrectReps: 0, startTime: 0, formHistory: [] }; + setStats(freshStats); + statsRef.current = freshStats; + setElapsed(0); + }, [stopWorkout, stopCamera]); + + // ------------------------------------------------------------------ + // Helpers + // ------------------------------------------------------------------ + const totalReps = stats.correctReps + stats.incorrectReps; + const accuracy = totalReps > 0 ? Math.round((stats.correctReps / totalReps) * 100) : 0; + const formatTime = (s: number) => `${Math.floor(s / 60).toString().padStart(2, '0')}:${(s % 60).toString().padStart(2, '0')}`; + + // ------------------------------------------------------------------ + // Render โ€” Exercise Selection + // ------------------------------------------------------------------ + if (!selectedExercise) { + return ( +
+ {/* Pose model loading banner */} + {poseLoading && ( +
+ Loading MediaPipe Pose model... +
+ )} + +
+
๐Ÿ‹๏ธโ€โ™‚๏ธ
+

AI Fitness Trainer

+

Select an exercise to start your workout

+

+ Powered by MediaPipe Pose โ€ข Real-time skeleton tracking +

+
+ +
+ {EXERCISES.map(ex => ( + + ))} +
+
+ ); + } + + // ------------------------------------------------------------------ + // Render โ€” Active Workout + // ------------------------------------------------------------------ + return ( +
+ {/* Pose model loading banner */} + {poseLoading && ( +
+ Loading MediaPipe Pose model... +
+ )} + + {/* Header */} +
+
+ {selectedExercise.icon} + {selectedExercise.name} +
+
+
{currentAngle}ยฐ
+
{formatTime(elapsed)}
+
+
+ + {/* Camera Feed + Skeleton Overlay */} +
+
+ {/* Hidden video element โ€” MediaPipe reads from this */} +
+
+ + {/* Rep Counter */} +
+
+
+ {totalReps} + REPS +
+
+
+
+ โœ… + {stats.correctReps} + Correct +
+
+ โŒ + {stats.incorrectReps} + Incorrect +
+
+ ๐ŸŽฏ + {accuracy}% + Accuracy +
+
+
+ + {/* Feedback */} + {feedback && ( +
+

{feedback}

+
+ )} + + {/* Tips */} +
+

๐Ÿ’ก Tips for {selectedExercise.name}

+
    + {selectedExercise.tips.map((tip, i) => ( +
  • {tip}
  • + ))} +
+
+ + {/* Error */} + {error && ( +
+ Error: {error} +
+ )} + + {/* Controls */} +
+ {isWorkout ? ( + + ) : ( + + )} + +
+
+ ); +} diff --git a/src/fitness/poseEngine.ts b/src/fitness/poseEngine.ts new file mode 100644 index 00000000..0a555cbd --- /dev/null +++ b/src/fitness/poseEngine.ts @@ -0,0 +1,358 @@ +/** + * Pose Engine โ€” MediaPipe Pose Landmarker wrapper + * + * Provides: + * - PoseLandmarker initialisation and singleton access + * - Joint angle calculation from 3 keypoints + * - Exercise-specific position detection via angle thresholds + * - Skeleton drawing helpers + */ + +import { + PoseLandmarker, + FilesetResolver, + DrawingUtils, +} from '@mediapipe/tasks-vision'; +import type { NormalizedLandmark } from '@mediapipe/tasks-vision'; + +// --------------------------------------------------------------------------- +// Landmark indices (MediaPipe Pose 33 keypoints) +// --------------------------------------------------------------------------- +export const LM = { + NOSE: 0, + LEFT_SHOULDER: 11, + RIGHT_SHOULDER: 12, + LEFT_ELBOW: 13, + RIGHT_ELBOW: 14, + LEFT_WRIST: 15, + RIGHT_WRIST: 16, + LEFT_HIP: 23, + RIGHT_HIP: 24, + LEFT_KNEE: 25, + RIGHT_KNEE: 26, + LEFT_ANKLE: 27, + RIGHT_ANKLE: 28, +} as const; + +// --------------------------------------------------------------------------- +// Types +// --------------------------------------------------------------------------- +export type ExercisePosition = 'up' | 'down' | 'middle'; +export type FormQuality = 'good' | 'bad' | 'unknown'; + +export interface PoseAnalysis { + position: ExercisePosition; + form: FormQuality; + angle: number; // primary angle used for detection + landmarks: NormalizedLandmark[]; + feedback: string; +} + +export interface ExerciseDef { + id: string; + name: string; + icon: string; + tips: string[]; + /** Returns analysis for the given landmarks */ + analyze: (lm: NormalizedLandmark[]) => PoseAnalysis; +} + +// --------------------------------------------------------------------------- +// Math helpers +// --------------------------------------------------------------------------- +/** Calculate angle (in degrees) at point B given 3 points A-B-C */ +export function calcAngle( + a: NormalizedLandmark, + b: NormalizedLandmark, + c: NormalizedLandmark, +): number { + const radians = Math.atan2(c.y - b.y, c.x - b.x) - Math.atan2(a.y - b.y, a.x - b.x); + let angle = Math.abs(radians * (180 / Math.PI)); + if (angle > 180) angle = 360 - angle; + return angle; +} + +/** Average of left and right side angles */ +function avgSideAngle( + lm: NormalizedLandmark[], + leftA: number, leftB: number, leftC: number, + rightA: number, rightB: number, rightC: number, +): number { + const leftAngle = calcAngle(lm[leftA], lm[leftB], lm[leftC]); + const rightAngle = calcAngle(lm[rightA], lm[rightB], lm[rightC]); + return (leftAngle + rightAngle) / 2; +} + +// --------------------------------------------------------------------------- +// Exercise definitions with angle-based analysis +// --------------------------------------------------------------------------- +export const EXERCISES: ExerciseDef[] = [ + { + id: 'squats', + name: 'Squats', + icon: '๐Ÿ‹๏ธ', + tips: ['Keep back straight', 'Knees behind toes', 'Thighs parallel to ground'], + analyze(lm) { + // Primary: knee angle (hip โ†’ knee โ†’ ankle) + const kneeAngle = avgSideAngle( + lm, + LM.LEFT_HIP, LM.LEFT_KNEE, LM.LEFT_ANKLE, + LM.RIGHT_HIP, LM.RIGHT_KNEE, LM.RIGHT_ANKLE, + ); + + // Form: hip angle (shoulder โ†’ hip โ†’ knee) โ€” check for leaning too far forward + const hipAngle = avgSideAngle( + lm, + LM.LEFT_SHOULDER, LM.LEFT_HIP, LM.LEFT_KNEE, + LM.RIGHT_SHOULDER, LM.RIGHT_HIP, LM.RIGHT_KNEE, + ); + + let position: ExercisePosition = 'middle'; + if (kneeAngle < 100) position = 'down'; + else if (kneeAngle > 155) position = 'up'; + + const form: FormQuality = hipAngle > 60 ? 'good' : 'bad'; + const feedback = position === 'down' + ? (form === 'good' ? 'โฌ‡๏ธ Great depth!' : 'โš ๏ธ Keep your back straight!') + : position === 'up' + ? 'โฌ†๏ธ Stand tall!' + : '๐Ÿ”„ Keep going...'; + + return { position, form, angle: Math.round(kneeAngle), landmarks: lm, feedback }; + }, + }, + { + id: 'bicep-curls', + name: 'Bicep Curls', + icon: '๐Ÿ’ช', + tips: ['Keep elbows close to body', 'Full range of motion', 'Control the movement'], + analyze(lm) { + // Primary: elbow angle (shoulder โ†’ elbow โ†’ wrist) + const elbowAngle = avgSideAngle( + lm, + LM.LEFT_SHOULDER, LM.LEFT_ELBOW, LM.LEFT_WRIST, + LM.RIGHT_SHOULDER, LM.RIGHT_ELBOW, LM.RIGHT_WRIST, + ); + + let position: ExercisePosition = 'middle'; + if (elbowAngle < 60) position = 'up'; // curled + else if (elbowAngle > 140) position = 'down'; // extended + + // Form: check if elbows stay near body (elbow x close to hip x) + const elbowDrift = Math.abs(lm[LM.LEFT_ELBOW].x - lm[LM.LEFT_HIP].x); + const form: FormQuality = elbowDrift < 0.15 ? 'good' : 'bad'; + const feedback = position === 'up' + ? (form === 'good' ? '๐Ÿ’ช Great curl!' : 'โš ๏ธ Keep elbows tucked in!') + : position === 'down' + ? 'โฌ‡๏ธ Fully extend arms' + : '๐Ÿ”„ Control the movement...'; + + return { position, form, angle: Math.round(elbowAngle), landmarks: lm, feedback }; + }, + }, + { + id: 'pushups', + name: 'Push-ups', + icon: '๐Ÿซธ', + tips: ['Keep body straight', 'Chest near ground', 'Full arm extension'], + analyze(lm) { + // Primary: elbow angle + const elbowAngle = avgSideAngle( + lm, + LM.LEFT_SHOULDER, LM.LEFT_ELBOW, LM.LEFT_WRIST, + LM.RIGHT_SHOULDER, LM.RIGHT_ELBOW, LM.RIGHT_WRIST, + ); + + let position: ExercisePosition = 'middle'; + if (elbowAngle < 90) position = 'down'; + else if (elbowAngle > 150) position = 'up'; + + // Form: body alignment (shoulder โ†’ hip โ†’ ankle angle should be ~180) + const bodyAngle = avgSideAngle( + lm, + LM.LEFT_SHOULDER, LM.LEFT_HIP, LM.LEFT_ANKLE, + LM.RIGHT_SHOULDER, LM.RIGHT_HIP, LM.RIGHT_ANKLE, + ); + const form: FormQuality = bodyAngle > 150 ? 'good' : 'bad'; + const feedback = position === 'down' + ? (form === 'good' ? 'โฌ‡๏ธ Great push-up!' : 'โš ๏ธ Keep body straight!') + : position === 'up' + ? 'โฌ†๏ธ Arms extended!' + : '๐Ÿ”„ Push through...'; + + return { position, form, angle: Math.round(elbowAngle), landmarks: lm, feedback }; + }, + }, + { + id: 'lunges', + name: 'Lunges', + icon: '๐Ÿฆต', + tips: ['Front knee at 90ยฐ', 'Back knee near floor', 'Keep torso upright'], + analyze(lm) { + // Use the minimum knee angle (the front leg will have the smaller angle) + const leftKnee = calcAngle(lm[LM.LEFT_HIP], lm[LM.LEFT_KNEE], lm[LM.LEFT_ANKLE]); + const rightKnee = calcAngle(lm[LM.RIGHT_HIP], lm[LM.RIGHT_KNEE], lm[LM.RIGHT_ANKLE]); + const minKnee = Math.min(leftKnee, rightKnee); + + let position: ExercisePosition = 'middle'; + if (minKnee < 100) position = 'down'; + else if (minKnee > 155) position = 'up'; + + // Form: torso should be upright (shoulder-hip vertical alignment) + const torsoLean = Math.abs(lm[LM.LEFT_SHOULDER].x - lm[LM.LEFT_HIP].x); + const form: FormQuality = torsoLean < 0.1 ? 'good' : 'bad'; + const feedback = position === 'down' + ? (form === 'good' ? 'โฌ‡๏ธ Deep lunge!' : 'โš ๏ธ Keep torso upright!') + : position === 'up' + ? 'โฌ†๏ธ Stand tall!' + : '๐Ÿ”„ Lunge deeper...'; + + return { position, form, angle: Math.round(minKnee), landmarks: lm, feedback }; + }, + }, + { + id: 'shoulder-press', + name: 'Shoulder Press', + icon: '๐Ÿ™†', + tips: ['Press directly overhead', 'Don\'t arch back', 'Full extension at top'], + analyze(lm) { + // Primary: elbow angle + const elbowAngle = avgSideAngle( + lm, + LM.LEFT_SHOULDER, LM.LEFT_ELBOW, LM.LEFT_WRIST, + LM.RIGHT_SHOULDER, LM.RIGHT_ELBOW, LM.RIGHT_WRIST, + ); + + let position: ExercisePosition = 'middle'; + if (elbowAngle > 155) position = 'up'; // overhead + else if (elbowAngle < 100) position = 'down'; // at shoulders + + // Form: wrists should be roughly above shoulders (not too far out) + const wristOffset = Math.abs( + (lm[LM.LEFT_WRIST].x + lm[LM.RIGHT_WRIST].x) / 2 - + (lm[LM.LEFT_SHOULDER].x + lm[LM.RIGHT_SHOULDER].x) / 2 + ); + const form: FormQuality = wristOffset < 0.12 ? 'good' : 'bad'; + const feedback = position === 'up' + ? (form === 'good' ? 'โฌ†๏ธ Full extension!' : 'โš ๏ธ Press straight overhead!') + : position === 'down' + ? 'โฌ‡๏ธ Ready position' + : '๐Ÿ”„ Press up...'; + + return { position, form, angle: Math.round(elbowAngle), landmarks: lm, feedback }; + }, + }, + { + id: 'plank', + name: 'Plank', + icon: '๐Ÿง˜', + tips: ['Keep body in straight line', 'Engage core', 'Don\'t drop hips'], + analyze(lm) { + // Body alignment: shoulder โ†’ hip โ†’ ankle angle + const bodyAngle = avgSideAngle( + lm, + LM.LEFT_SHOULDER, LM.LEFT_HIP, LM.LEFT_ANKLE, + LM.RIGHT_SHOULDER, LM.RIGHT_HIP, LM.RIGHT_ANKLE, + ); + + const position: ExercisePosition = 'down'; // plank is always "hold" + const form: FormQuality = bodyAngle > 155 ? 'good' : 'bad'; + const feedback = form === 'good' + ? '๐Ÿง˜ Great plank! Body is straight!' + : 'โš ๏ธ Keep hips up โ€” straighten your body!'; + + return { position, form, angle: Math.round(bodyAngle), landmarks: lm, feedback }; + }, + }, +]; + +// --------------------------------------------------------------------------- +// Singleton PoseLandmarker +// --------------------------------------------------------------------------- +let landmarkerInstance: PoseLandmarker | null = null; +let initPromise: Promise | null = null; + +export async function getPoseLandmarker(): Promise { + if (landmarkerInstance) return landmarkerInstance; + if (initPromise) return initPromise; + + initPromise = (async () => { + const vision = await FilesetResolver.forVisionTasks( + 'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm', + ); + + const landmarker = await PoseLandmarker.createFromOptions(vision, { + baseOptions: { + modelAssetPath: 'https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task', + delegate: 'GPU', + }, + runningMode: 'VIDEO', + numPoses: 1, + }); + + landmarkerInstance = landmarker; + return landmarker; + })(); + + return initPromise; +} + +// --------------------------------------------------------------------------- +// Drawing helpers +// --------------------------------------------------------------------------- +export function drawSkeleton( + ctx: CanvasRenderingContext2D, + landmarks: NormalizedLandmark[], + width: number, + height: number, + formColor: string = '#22C55E', +) { + const drawUtils = new DrawingUtils(ctx); + + // Draw connections (bones) + drawUtils.drawConnectors(landmarks, PoseLandmarker.POSE_CONNECTIONS, { + color: formColor, + lineWidth: 3, + }); + + // Draw landmarks (joints) + drawUtils.drawLandmarks(landmarks, { + color: '#FFFFFF', + fillColor: formColor, + lineWidth: 1, + radius: 4, + }); +} + +/** Draw the primary angle value near the relevant joint */ +export function drawAngleBadge( + ctx: CanvasRenderingContext2D, + angle: number, + landmark: NormalizedLandmark, + width: number, + height: number, +) { + const x = landmark.x * width; + const y = landmark.y * height; + + ctx.save(); + ctx.font = 'bold 14px "SF Mono", monospace'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + + // Background pill + const text = `${angle}ยฐ`; + const metrics = ctx.measureText(text); + const pad = 6; + const w = metrics.width + pad * 2; + const h = 20; + + ctx.fillStyle = 'rgba(0,0,0,0.7)'; + ctx.beginPath(); + ctx.roundRect(x - w / 2, y - h / 2 - 20, w, h, 8); + ctx.fill(); + + ctx.fillStyle = '#FF8A50'; + ctx.fillText(text, x, y - 20); + ctx.restore(); +} diff --git a/src/styles/index.css b/src/styles/index.css index f5849982..d03b4c1c 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -700,3 +700,493 @@ body { } .text-muted { color: var(--text-muted); font-size: 13px; } + +/* --------------------------------------------------------------------------- + * Fitness tab + * --------------------------------------------------------------------------- */ + +.fitness-panel { + padding: 0; + gap: 0; + overflow-y: auto; +} + +/* Hero / exercise selection screen */ +.fitness-hero { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + padding: 28px 16px 12px; +} + +.fitness-hero-icon { + font-size: 48px; + animation: float 3s ease-in-out infinite; +} + +@keyframes float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-8px); } +} + +.fitness-hero h2 { + font-size: 22px; + font-weight: 700; + background: linear-gradient(135deg, var(--primary), #FF8A50); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* Exercise grid */ +.exercise-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 10px; + padding: 12px 16px 24px; +} + +.exercise-card { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + padding: 20px 12px; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: var(--radius); + cursor: pointer; + transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); +} + +.exercise-card:hover { + background: var(--bg-input); + border-color: var(--primary); + transform: translateY(-3px); + box-shadow: 0 8px 25px rgba(255, 85, 0, 0.15); +} + +.exercise-card:active { + transform: translateY(0); +} + +.exercise-card:disabled { + opacity: 0.4; + cursor: not-allowed; + transform: none; +} + +.exercise-icon { + font-size: 32px; +} + +.exercise-name { + font-size: 12px; + font-weight: 600; + color: var(--text); + text-align: center; +} + +/* Workout header */ +.fitness-workout-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 16px; + background: var(--bg-card); + border-bottom: 1px solid var(--border); +} + +.fitness-exercise-label { + display: flex; + align-items: center; + gap: 8px; + font-weight: 600; + font-size: 15px; +} + +.exercise-icon-sm { + font-size: 20px; +} + +.fitness-timer { + font-size: 18px; + font-weight: 700; + font-family: 'SF Mono', 'Fira Code', monospace; + color: var(--primary); + letter-spacing: 1px; +} + +/* Camera wrapper */ +.fitness-camera-wrapper { + padding: 12px 16px; +} + +.fitness-camera { + position: relative; + border-radius: var(--radius); + background: var(--bg-card); + min-height: 220px; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + border: 3px solid transparent; + transition: border-color 0.4s ease; +} + +.fitness-camera.form-good { + border-color: var(--green); + box-shadow: 0 0 20px rgba(34, 197, 94, 0.2); +} + +.fitness-camera.form-bad { + border-color: var(--red); + box-shadow: 0 0 20px rgba(239, 68, 68, 0.2); + animation: shake 0.3s ease; +} + +@keyframes shake { + 0%, 100% { transform: translateX(0); } + 25% { transform: translateX(-3px); } + 75% { transform: translateX(3px); } +} + +/* Form quality indicator */ +.form-indicator { + position: absolute; + top: 12px; + right: 12px; + width: 40px; + height: 40px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + font-weight: 700; + color: white; + background: var(--bg-input); + backdrop-filter: blur(8px); + z-index: 10; + transition: all 0.3s ease; +} + +.form-indicator.good { + background: var(--green); + box-shadow: 0 0 15px rgba(34, 197, 94, 0.5); +} + +.form-indicator.bad { + background: var(--red); + box-shadow: 0 0 15px rgba(239, 68, 68, 0.5); + animation: pulse-indicator 1s ease-in-out infinite; +} + +@keyframes pulse-indicator { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.15); } +} + +/* Analyzing overlay */ +.fitness-analyzing { + position: absolute; + bottom: 12px; + left: 12px; + display: flex; + align-items: center; + gap: 6px; + padding: 4px 10px; + border-radius: 20px; + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(8px); + color: white; + font-size: 11px; + font-weight: 500; + z-index: 10; +} + +.fitness-analyzing-dot { + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--primary); + animation: blink 1s ease-in-out infinite; +} + +@keyframes blink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.3; } +} + +/* Rep counter */ +.rep-counter-section { + padding: 16px; + display: flex; + flex-direction: column; + gap: 12px; +} + +.rep-counter-main { + display: flex; + align-items: center; + justify-content: center; +} + +.rep-count-display { + display: flex; + flex-direction: column; + align-items: center; + padding: 12px 32px; + background: linear-gradient(135deg, var(--bg-card), var(--bg-input)); + border-radius: 20px; + border: 1px solid var(--border); +} + +.rep-count-number { + font-size: 56px; + font-weight: 800; + line-height: 1; + background: linear-gradient(135deg, var(--primary), #FF8A50); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.rep-count-label { + font-size: 11px; + font-weight: 700; + color: var(--text-muted); + letter-spacing: 3px; + text-transform: uppercase; +} + +.rep-counter-details { + display: flex; + justify-content: center; + gap: 16px; +} + +.rep-detail { + display: flex; + flex-direction: column; + align-items: center; + gap: 2px; + padding: 10px 16px; + background: var(--bg-card); + border-radius: var(--radius-sm); + border: 1px solid var(--border); + min-width: 80px; +} + +.rep-detail-icon { + font-size: 16px; +} + +.rep-detail-value { + font-size: 20px; + font-weight: 700; + color: var(--text); +} + +.rep-detail-label { + font-size: 10px; + font-weight: 600; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.rep-detail.correct .rep-detail-value { color: var(--green); } +.rep-detail.incorrect .rep-detail-value { color: var(--red); } +.rep-detail.accuracy .rep-detail-value { color: var(--primary); } + +/* Feedback */ +.fitness-feedback { + margin: 0 16px; + padding: 12px 16px; + border-radius: var(--radius); + font-size: 14px; + font-weight: 500; + line-height: 1.4; + background: var(--bg-card); + border: 1px solid var(--border); + transition: all 0.3s ease; + animation: fadeIn 0.3s ease; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(4px); } + to { opacity: 1; transform: translateY(0); } +} + +.fitness-feedback.good { + background: rgba(34, 197, 94, 0.1); + border-color: rgba(34, 197, 94, 0.3); + color: #86efac; +} + +.fitness-feedback.bad { + background: rgba(239, 68, 68, 0.1); + border-color: rgba(239, 68, 68, 0.3); + color: #fca5a5; +} + +/* Tips */ +.fitness-tips { + margin: 12px 16px; + padding: 12px 16px; + background: var(--bg-card); + border-radius: var(--radius); + border: 1px solid var(--border); +} + +.fitness-tips h4 { + font-size: 12px; + font-weight: 600; + color: var(--text-muted); + margin-bottom: 8px; +} + +.fitness-tips ul { + list-style: none; + display: flex; + flex-direction: column; + gap: 4px; +} + +.fitness-tips li { + font-size: 12px; + color: var(--text-muted); + padding-left: 16px; + position: relative; +} + +.fitness-tips li::before { + content: 'โ€ข'; + position: absolute; + left: 0; + color: var(--primary); + font-weight: 700; +} + +/* Error */ +.fitness-error { + margin: 0 16px; + padding: 12px 16px; + background: rgba(239, 68, 68, 0.1); + border: 1px solid rgba(239, 68, 68, 0.3); + border-radius: var(--radius); +} + +/* Controls */ +.fitness-controls { + display: flex; + justify-content: center; + gap: 10px; + padding: 16px; + margin-top: auto; + border-top: 1px solid var(--border); + background: var(--bg); +} + +.btn-stop { + background: var(--red); + border-color: var(--red); + color: white; + padding: 10px 24px; + font-size: 15px; + font-weight: 600; +} + +.btn-stop:hover { + background: #DC2626; +} + +/* VLM debug panel */ +.fitness-vlm-debug { + margin: 8px 16px; + padding: 8px 12px; + background: rgba(255, 255, 255, 0.03); + border: 1px solid rgba(255, 255, 255, 0.06); + border-radius: var(--radius-sm); + display: flex; + flex-wrap: wrap; + gap: 6px; + align-items: center; + font-size: 11px; +} + +.vlm-debug-label { + font-weight: 600; + color: var(--text-muted); +} + +.vlm-debug-text { + color: var(--text-muted); + font-style: italic; + flex: 1; + min-width: 100px; +} + +.vlm-debug-phase { + font-size: 10px; + font-weight: 700; + padding: 2px 8px; + background: var(--bg-card); + border-radius: 10px; + color: var(--primary); + letter-spacing: 0.5px; +} + +/* Skeleton canvas overlay */ +.skeleton-canvas { + width: 100%; + border-radius: 12px; + display: block; +} + +/* Angle badge in workout header */ +.fitness-header-right { + display: flex; + align-items: center; + gap: 12px; +} + +.fitness-angle-badge { + font-size: 16px; + font-weight: 800; + font-family: 'SF Mono', 'Fira Code', monospace; + color: #FF8A50; + background: rgba(255, 138, 80, 0.12); + padding: 2px 10px; + border-radius: 10px; + letter-spacing: 0.5px; +} + +/* Phase indicator overlay */ +.phase-indicator { + position: absolute; + bottom: 12px; + right: 12px; + padding: 4px 12px; + border-radius: 20px; + font-size: 12px; + font-weight: 700; + z-index: 10; + backdrop-filter: blur(8px); +} + +.phase-indicator.up { + background: rgba(34, 197, 94, 0.7); + color: white; +} + +.phase-indicator.down { + background: rgba(59, 130, 246, 0.7); + color: white; +} + + + From 421c3ee8a43adaff8aa3004319ae65bfcea7802f Mon Sep 17 00:00:00 2001 From: proCreator_01 <82655626+AaryakCreator@users.noreply.github.com> Date: Sun, 22 Mar 2026 22:58:28 +0530 Subject: [PATCH 03/14] Refined it Rename project to "kine-sight" and streamline the app to focus on the fitness experience. App.tsx was simplified to remove multi-tab SDK initialization and only render the Fitness tab (badge updated). Multiple UI/features related to LLM/tools/vision/voice were removed (ChatTab, VisionTab, VoiceTab, ToolsTab, ModelBanner and related hooks/workers) and runanywhere-related dependencies were removed from package.json. Significant improvements to FitnessTab: add a pre-workout countdown, debounce consecutive-frame position detection, support hold-style exercises (plank) with hold and best-time tracking, refine rep counting to use debounced positions, various UI tweaks (labels, countdown overlay), and add NormalizedLandmark typing. Refs and state resets were added to make detection and session lifecycle more robust. Also added MediaPipe type declarations (src/types/mediapipe.d.ts) and new error snapshots (errors.txt, errors2.txt, errors3.txt) capturing current TypeScript issues to address. --- errors.txt | Bin 0 -> 734 bytes errors2.txt | 3 + errors3.txt | 3 + package-lock.json | 40 +- package.json | 5 +- src/App.tsx | 65 +-- src/components/ChatTab.tsx | 142 ------ src/components/FitnessTab.tsx | 222 +++++++--- src/components/ModelBanner.tsx | 39 -- src/components/ToolsTab.tsx | 462 -------------------- src/components/VisionTab.tsx | 267 ----------- src/components/VoiceTab.tsx | 221 ---------- src/fitness/poseEngine.ts | 94 ++-- src/hooks/useModelLoader.ts | 86 ---- src/runanywhere.ts | 149 ------- src/styles/index.css | 656 +++++----------------------- src/types/mediapipe.d.ts | 24 + src/workers/vlm-worker.ts | 10 - tests/web-starter-app-bugs.md | 31 -- tests/web-starter-app-test-suite.md | 481 -------------------- vite.config.ts | 72 +-- 21 files changed, 372 insertions(+), 2700 deletions(-) create mode 100644 errors.txt create mode 100644 errors2.txt create mode 100644 errors3.txt delete mode 100644 src/components/ChatTab.tsx delete mode 100644 src/components/ModelBanner.tsx delete mode 100644 src/components/ToolsTab.tsx delete mode 100644 src/components/VisionTab.tsx delete mode 100644 src/components/VoiceTab.tsx delete mode 100644 src/hooks/useModelLoader.ts delete mode 100644 src/runanywhere.ts create mode 100644 src/types/mediapipe.d.ts delete mode 100644 src/workers/vlm-worker.ts delete mode 100644 tests/web-starter-app-bugs.md delete mode 100644 tests/web-starter-app-test-suite.md diff --git a/errors.txt b/errors.txt new file mode 100644 index 0000000000000000000000000000000000000000..98605be6ac26f7b80af4998b0906c05546f65db4 GIT binary patch literal 734 zcmds!!A`?45JcyU#6S2}kZ41pY6>SL1ZO1pfzSkqnxs{15dC@J?V1Y*PTY{SS!Z|b zof&_AM2%XjAhznY<2$G!k|Nbxwd|}CSr4l8sG0ZPb)>m2wA6_fWyk7%w~DZZ$X4_igb{POSVD I=l{%$FJS+G!~g&Q literal 0 HcmV?d00001 diff --git a/errors2.txt b/errors2.txt new file mode 100644 index 00000000..8f148dc7 --- /dev/null +++ b/errors2.txt @@ -0,0 +1,3 @@ +src/components/FitnessTab.tsx(196,49): error TS7006: Parameter 'lm' implicitly has an 'any' type. +src/fitness/poseEngine.ts(17,8): error TS2307: Cannot find module '@mediapipe/tasks-vision' or its corresponding type declarations. +src/fitness/poseEngine.ts(18,41): error TS2307: Cannot find module '@mediapipe/tasks-vision' or its corresponding type declarations. diff --git a/errors3.txt b/errors3.txt new file mode 100644 index 00000000..8f148dc7 --- /dev/null +++ b/errors3.txt @@ -0,0 +1,3 @@ +src/components/FitnessTab.tsx(196,49): error TS7006: Parameter 'lm' implicitly has an 'any' type. +src/fitness/poseEngine.ts(17,8): error TS2307: Cannot find module '@mediapipe/tasks-vision' or its corresponding type declarations. +src/fitness/poseEngine.ts(18,41): error TS2307: Cannot find module '@mediapipe/tasks-vision' or its corresponding type declarations. diff --git a/package-lock.json b/package-lock.json index c12f5066..5e213404 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,14 @@ { - "name": "runanywhere-web-starter", + "name": "kine-sight", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "runanywhere-web-starter", + "name": "kine-sight", "version": "0.1.0", "dependencies": { "@mediapipe/tasks-vision": "^0.10.33", - "@runanywhere/web": "0.1.0-beta.10", - "@runanywhere/web-llamacpp": "0.1.0-beta.10", - "@runanywhere/web-onnx": "0.1.0-beta.10", "react": "^19.0.0", "react-dom": "^19.0.0" }, @@ -1160,39 +1157,6 @@ "win32" ] }, - "node_modules/@runanywhere/web": { - "version": "0.1.0-beta.10", - "resolved": "https://registry.npmjs.org/@runanywhere/web/-/web-0.1.0-beta.10.tgz", - "integrity": "sha512-GSnymtlu7Vx1+ufV3V8o6zgpiKGxFqHlfmq7ynyLGAZOkR+Hj96geryS1NMTTEZN34MHOZ0TjSM49NsMpfRCPg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@runanywhere/web-llamacpp": { - "version": "0.1.0-beta.10", - "resolved": "https://registry.npmjs.org/@runanywhere/web-llamacpp/-/web-llamacpp-0.1.0-beta.10.tgz", - "integrity": "sha512-8Kv0NHxD2eP+exfFvgRD9MTDcr7Xf4ZkVlGYVI4avtEdplevY9lK1Z6Xb9aEJRQ61J9byeGIdtT8NSo/mVCiAg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "@runanywhere/web": ">=0.1.0-beta.0" - } - }, - "node_modules/@runanywhere/web-onnx": { - "version": "0.1.0-beta.10", - "resolved": "https://registry.npmjs.org/@runanywhere/web-onnx/-/web-onnx-0.1.0-beta.10.tgz", - "integrity": "sha512-PYMvoE5ij0fK3sHoEYzwvp0FgZYQpX2Jdygfrs63+YYH+CtJ5vQVoukQfnHl/Knl8psL0WPY+Ukj5NvDp3yD9Q==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "@runanywhere/web": ">=0.1.0-beta.0" - } - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", diff --git a/package.json b/package.json index f3537a45..f25cffc2 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "runanywhere-web-starter", + "name": "kine-sight", "private": true, "version": "0.1.0", "type": "module", @@ -10,9 +10,6 @@ }, "dependencies": { "@mediapipe/tasks-vision": "^0.10.33", - "@runanywhere/web": "0.1.0-beta.10", - "@runanywhere/web-llamacpp": "0.1.0-beta.10", - "@runanywhere/web-onnx": "0.1.0-beta.10", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/src/App.tsx b/src/App.tsx index 5d67113d..c7c4368c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,76 +1,15 @@ -import { useState, useEffect } from 'react'; -import { initSDK, getAccelerationMode } from './runanywhere'; import { FitnessTab } from './components/FitnessTab'; -import { ChatTab } from './components/ChatTab'; -import { VisionTab } from './components/VisionTab'; -import { VoiceTab } from './components/VoiceTab'; -import { ToolsTab } from './components/ToolsTab'; - -type Tab = 'fitness' | 'chat' | 'vision' | 'voice' | 'tools'; export function App() { - const [sdkReady, setSdkReady] = useState(false); - const [sdkError, setSdkError] = useState(null); - const [activeTab, setActiveTab] = useState('fitness'); - - useEffect(() => { - initSDK() - .then(() => setSdkReady(true)) - .catch((err) => setSdkError(err instanceof Error ? err.message : String(err))); - }, []); - - if (sdkError) { - return ( -
-

SDK Error

-

{sdkError}

-
- ); - } - - if (!sdkReady) { - return ( -
-
-

Loading Kine-Sight AI...

-

Initializing on-device AI fitness engine

-
- ); - } - - const accel = getAccelerationMode(); - return (

Kine-Sight

- {accel && {accel === 'webgpu' ? 'WebGPU' : 'CPU'}} + AI Coach
- -
- {activeTab === 'fitness' && } - {activeTab === 'chat' && } - {activeTab === 'vision' && } - {activeTab === 'voice' && } - {activeTab === 'tools' && } +
); diff --git a/src/components/ChatTab.tsx b/src/components/ChatTab.tsx deleted file mode 100644 index 924daad6..00000000 --- a/src/components/ChatTab.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import { useState, useRef, useEffect, useCallback } from 'react'; -import { ModelCategory } from '@runanywhere/web'; -import { TextGeneration } from '@runanywhere/web-llamacpp'; -import { useModelLoader } from '../hooks/useModelLoader'; -import { ModelBanner } from './ModelBanner'; - -interface Message { - role: 'user' | 'assistant'; - text: string; - stats?: { tokens: number; tokPerSec: number; latencyMs: number }; -} - -export function ChatTab() { - const loader = useModelLoader(ModelCategory.Language); - const [messages, setMessages] = useState([]); - const [input, setInput] = useState(''); - const [generating, setGenerating] = useState(false); - const cancelRef = useRef<(() => void) | null>(null); - const listRef = useRef(null); - - // Auto-scroll to bottom when messages change - useEffect(() => { - listRef.current?.scrollTo({ top: listRef.current.scrollHeight, behavior: 'smooth' }); - }, [messages]); - - const send = useCallback(async () => { - const text = input.trim(); - if (!text || generating) return; - - // Ensure model is loaded - if (loader.state !== 'ready') { - const ok = await loader.ensure(); - if (!ok) return; - } - - setInput(''); - setMessages((prev) => [...prev, { role: 'user', text }]); - setGenerating(true); - - // Add empty assistant message for streaming - const assistantIdx = messages.length + 1; - setMessages((prev) => [...prev, { role: 'assistant', text: '' }]); - - try { - const { stream, result: resultPromise, cancel } = await TextGeneration.generateStream(text, { - maxTokens: 512, - temperature: 0.7, - }); - cancelRef.current = cancel; - - let accumulated = ''; - for await (const token of stream) { - accumulated += token; - setMessages((prev) => { - const updated = [...prev]; - updated[assistantIdx] = { role: 'assistant', text: accumulated }; - return updated; - }); - } - - const result = await resultPromise; - setMessages((prev) => { - const updated = [...prev]; - updated[assistantIdx] = { - role: 'assistant', - text: result.text || accumulated, - stats: { - tokens: result.tokensUsed, - tokPerSec: result.tokensPerSecond, - latencyMs: result.latencyMs, - }, - }; - return updated; - }); - } catch (err) { - const msg = err instanceof Error ? err.message : String(err); - setMessages((prev) => { - const updated = [...prev]; - updated[assistantIdx] = { role: 'assistant', text: `Error: ${msg}` }; - return updated; - }); - } finally { - cancelRef.current = null; - setGenerating(false); - } - }, [input, generating, messages.length, loader]); - - const handleCancel = () => { - cancelRef.current?.(); - }; - - return ( -
- - -
- {messages.length === 0 && ( -
-

Start a conversation

-

Type a message below to chat with on-device AI

-
- )} - {messages.map((msg, i) => ( -
-
-

{msg.text || '...'}

- {msg.stats && ( -
- {msg.stats.tokens} tokens ยท {msg.stats.tokPerSec.toFixed(1)} tok/s ยท {msg.stats.latencyMs.toFixed(0)}ms -
- )} -
-
- ))} -
- -
{ e.preventDefault(); send(); }} - > - setInput(e.target.value)} - disabled={generating} - /> - {generating ? ( - - ) : ( - - )} -
-
- ); -} diff --git a/src/components/FitnessTab.tsx b/src/components/FitnessTab.tsx index 0af5a8d6..0e5989ef 100644 --- a/src/components/FitnessTab.tsx +++ b/src/components/FitnessTab.tsx @@ -6,12 +6,17 @@ import { drawAngleBadge, LM, } from '../fitness/poseEngine'; -import type { ExerciseDef, PoseAnalysis, FormQuality } from '../fitness/poseEngine'; +import type { ExerciseDef, PoseAnalysis, FormQuality, ExercisePosition } from '../fitness/poseEngine'; +import type { NormalizedLandmark } from '@mediapipe/tasks-vision'; // --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- const CANVAS_FPS = 30; +/** Number of consecutive frames required to confirm a position change */ +const DEBOUNCE_FRAMES = 5; +/** Countdown duration in seconds before workout starts */ +const COUNTDOWN_SECONDS = 5; // --------------------------------------------------------------------------- // Types @@ -40,6 +45,9 @@ export function FitnessTab() { const [poseReady, setPoseReady] = useState(false); const [poseLoading, setPoseLoading] = useState(false); + // Countdown + const [countdown, setCountdown] = useState(null); + // Session const [isWorkout, setIsWorkout] = useState(false); const [phase, setPhase] = useState('idle'); @@ -54,6 +62,10 @@ export function FitnessTab() { }); const [elapsed, setElapsed] = useState(0); + // Plank-specific + const [plankHoldTime, setPlankHoldTime] = useState(0); + const [plankBestTime, setPlankBestTime] = useState(0); + // Refs const videoRef = useRef(null); const canvasRef = useRef(null); @@ -67,28 +79,20 @@ export function FitnessTab() { const lastTimeRef = useRef(0); const exerciseRef = useRef(null); + // Debounce refs โ€” for stable position detection + const pendingPositionRef = useRef('middle'); + const pendingFrameCountRef = useRef(0); + const confirmedPositionRef = useRef('middle'); + + // Plank refs + const plankHoldStartRef = useRef(0); + const plankBestRef = useRef(0); + // Keep refs in sync phaseRef.current = phase; isWorkoutRef.current = isWorkout; exerciseRef.current = selectedExercise; - // ------------------------------------------------------------------ - // Init MediaPipe Pose Landmarker - // ------------------------------------------------------------------ - const initPose = useCallback(async () => { - if (poseReady || poseLoading) return; - setPoseLoading(true); - setError(null); - try { - await getPoseLandmarker(); - setPoseReady(true); - } catch (err) { - setError(`Failed to load pose model: ${err instanceof Error ? err.message : String(err)}`); - } finally { - setPoseLoading(false); - } - }, [poseReady, poseLoading]); - // ------------------------------------------------------------------ // Camera โ€” native getUserMedia // ------------------------------------------------------------------ @@ -190,7 +194,7 @@ export function FitnessTab() { const landmarks = results.landmarks[0]; // Mirror landmarks for display - const mirroredLandmarks = landmarks.map(lm => ({ + const mirroredLandmarks = landmarks.map((lm: NormalizedLandmark) => ({ ...lm, x: 1 - lm.x, })); @@ -214,18 +218,62 @@ export function FitnessTab() { setFormQuality(analysis.form); setFeedback(analysis.feedback); + // Skip rep logic if landmarks aren't confident + if (!analysis.confident) { + setFeedback('๐Ÿ” Move into the frame clearly...'); + rafRef.current = requestAnimationFrame(detect); + return; + } + if (analysis.form === 'bad') { formDuringRepRef.current = false; } - // Rep counting state machine - if (exercise.id !== 'plank') { + // ---- Plank: track hold time instead of reps ---- + if (exercise.isHold) { + if (analysis.form === 'good') { + if (plankHoldStartRef.current === 0) { + plankHoldStartRef.current = Date.now(); + } + const holdSec = Math.floor((Date.now() - plankHoldStartRef.current) / 1000); + setPlankHoldTime(holdSec); + if (holdSec > plankBestRef.current) { + plankBestRef.current = holdSec; + setPlankBestTime(holdSec); + } + } else { + // Form broke, reset hold timer + plankHoldStartRef.current = 0; + setPlankHoldTime(0); + } + rafRef.current = requestAnimationFrame(detect); + return; + } + + // ---- Debounced position detection ---- + // Only transition when N consecutive frames confirm same position + if (analysis.position === pendingPositionRef.current) { + pendingFrameCountRef.current++; + } else { + pendingPositionRef.current = analysis.position; + pendingFrameCountRef.current = 1; + } + + // Only update confirmed position after debounce threshold + if ( + pendingFrameCountRef.current >= DEBOUNCE_FRAMES && + pendingPositionRef.current !== confirmedPositionRef.current + ) { + const newPosition = pendingPositionRef.current; + confirmedPositionRef.current = newPosition; + + // Rep counting state machine (using confirmed/debounced positions) const prevPhase = phaseRef.current; - if (analysis.position === 'down' && (prevPhase === 'up' || prevPhase === 'idle')) { + if (newPosition === 'down' && (prevPhase === 'up' || prevPhase === 'idle')) { setPhase('down'); phaseRef.current = 'down'; - } else if (analysis.position === 'up') { + } else if (newPosition === 'up') { if (prevPhase === 'down') { // Rep completed! (down โ†’ up) setPhase('up'); @@ -255,8 +303,7 @@ export function FitnessTab() { } } } else { - // No pose detected โ€” just draw the video - // (already drawn above) + // No pose detected setFeedback('๐Ÿ” Step into frame so I can see you!'); } @@ -281,6 +328,31 @@ export function FitnessTab() { } } + // ------------------------------------------------------------------ + // Countdown helper + // ------------------------------------------------------------------ + const runCountdown = useCallback((): Promise => { + return new Promise((resolve) => { + let count = COUNTDOWN_SECONDS; + setCountdown(count); + + const interval = setInterval(() => { + count--; + if (count > 0) { + setCountdown(count); + } else { + setCountdown(0); // Show "GO!" + clearInterval(interval); + // Brief delay to show "GO!" then resolve + setTimeout(() => { + setCountdown(null); + resolve(); + }, 600); + } + }, 1000); + }); + }, []); + // ------------------------------------------------------------------ // Workout session management // ------------------------------------------------------------------ @@ -307,12 +379,19 @@ export function FitnessTab() { await startCamera(); } - setIsWorkout(true); - isWorkoutRef.current = true; + // Reset debounce state + pendingPositionRef.current = 'middle'; + pendingFrameCountRef.current = 0; + confirmedPositionRef.current = 'middle'; + + // Reset plank state + plankHoldStartRef.current = 0; + setPlankHoldTime(0); + setPhase('idle'); phaseRef.current = 'idle'; setFormQuality('unknown'); - setFeedback('๐ŸŽฏ Get into position! Pose detection starting...'); + setFeedback(''); formDuringRepRef.current = true; const freshStats: SessionStats = { correctReps: 0, @@ -324,6 +403,14 @@ export function FitnessTab() { statsRef.current = freshStats; setElapsed(0); + // Run countdown + await runCountdown(); + + // Now start the actual workout + setIsWorkout(true); + isWorkoutRef.current = true; + setFeedback('๐ŸŽฏ Get moving! Pose detection active.'); + // Start detection loop startDetectionLoop(); @@ -331,13 +418,14 @@ export function FitnessTab() { timerRef.current = setInterval(() => { setElapsed(prev => prev + 1); }, 1000); - }, [poseReady, startCamera, startDetectionLoop]); + }, [poseReady, startCamera, startDetectionLoop, runCountdown]); const stopWorkout = useCallback(() => { setIsWorkout(false); isWorkoutRef.current = false; setPhase('idle'); phaseRef.current = 'idle'; + setCountdown(null); if (rafRef.current) { cancelAnimationFrame(rafRef.current); @@ -362,6 +450,7 @@ export function FitnessTab() { setStats(freshStats); statsRef.current = freshStats; setElapsed(0); + setPlankHoldTime(0); }, [stopWorkout, stopCamera]); // ------------------------------------------------------------------ @@ -371,6 +460,8 @@ export function FitnessTab() { const accuracy = totalReps > 0 ? Math.round((stats.correctReps / totalReps) * 100) : 0; const formatTime = (s: number) => `${Math.floor(s / 60).toString().padStart(2, '0')}:${(s % 60).toString().padStart(2, '0')}`; + const isPlank = selectedExercise?.isHold === true; + // ------------------------------------------------------------------ // Render โ€” Exercise Selection // ------------------------------------------------------------------ @@ -386,7 +477,7 @@ export function FitnessTab() {
๐Ÿ‹๏ธโ€โ™‚๏ธ
-

AI Fitness Trainer

+

AI Fitness Coach

Select an exercise to start your workout

Powered by MediaPipe Pose โ€ข Real-time skeleton tracking @@ -458,6 +549,21 @@ export function FitnessTab() {

)} + {/* Countdown overlay */} + {countdown !== null && ( +
+
+ {countdown > 0 ? ( + <> +
{countdown}
+
Get Ready
+ + ) : ( +
GO!
+ )} +
+ )} + {/* Form quality indicator */} {isWorkout && (
@@ -466,7 +572,7 @@ export function FitnessTab() { )} {/* Phase indicator */} - {isWorkout && phase !== 'idle' && ( + {isWorkout && phase !== 'idle' && !isPlank && (
{phase === 'up' ? 'โฌ†๏ธ UP' : 'โฌ‡๏ธ DOWN'}
@@ -474,32 +580,42 @@ export function FitnessTab() {
- {/* Rep Counter */} -
-
-
- {totalReps} - REPS -
+ {/* Rep Counter OR Plank Hold Timer */} + {isPlank ? ( +
+
{formatTime(plankHoldTime)}
+
Hold Time
+ {plankBestTime > 0 && ( +
๐Ÿ† Best: {formatTime(plankBestTime)}
+ )}
-
-
- โœ… - {stats.correctReps} - Correct -
-
- โŒ - {stats.incorrectReps} - Incorrect + ) : ( +
+
+
+ {totalReps} + REPS +
-
- ๐ŸŽฏ - {accuracy}% - Accuracy +
+
+ โœ… + {stats.correctReps} + Correct +
+
+ โŒ + {stats.incorrectReps} + Incorrect +
+
+ ๐ŸŽฏ + {accuracy}% + Accuracy +
-
+ )} {/* Feedback */} {feedback && ( diff --git a/src/components/ModelBanner.tsx b/src/components/ModelBanner.tsx deleted file mode 100644 index 81dc9658..00000000 --- a/src/components/ModelBanner.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import type { LoaderState } from '../hooks/useModelLoader'; - -interface Props { - state: LoaderState; - progress: number; - error: string | null; - onLoad: () => void; - label: string; -} - -export function ModelBanner({ state, progress, error, onLoad, label }: Props) { - if (state === 'ready') return null; - - return ( -
- {state === 'idle' && ( - <> - No {label} model loaded. - - - )} - {state === 'downloading' && ( - <> - Downloading {label} model... {(progress * 100).toFixed(0)}% -
-
-
- - )} - {state === 'loading' && Loading {label} model into engine...} - {state === 'error' && ( - <> - Error: {error} - - - )} -
- ); -} diff --git a/src/components/ToolsTab.tsx b/src/components/ToolsTab.tsx deleted file mode 100644 index 2448d9e8..00000000 --- a/src/components/ToolsTab.tsx +++ /dev/null @@ -1,462 +0,0 @@ -import { ModelCategory } from '@runanywhere/web'; -import { - ToolCalling, - ToolCallFormat, - toToolValue, - getStringArg, - getNumberArg, - type ToolDefinition, - type ToolCall, - type ToolResult, - type ToolCallingResult, - type ToolValue, -} from '@runanywhere/web-llamacpp'; -import { useState, useRef, useEffect, useCallback } from 'react'; - -import { useModelLoader } from '../hooks/useModelLoader'; -import { ModelBanner } from './ModelBanner'; - -// --------------------------------------------------------------------------- -// Built-in demo tools -// --------------------------------------------------------------------------- - -const DEMO_TOOLS: { def: ToolDefinition; executor: Parameters[1] }[] = [ - { - def: { - name: 'get_weather', - description: 'Gets the current weather for a city. Returns temperature in Fahrenheit and a short condition.', - parameters: [ - { name: 'location', type: 'string', description: 'City name (e.g. "San Francisco")', required: true }, - ], - category: 'Utility', - }, - executor: async (args) => { - const city = getStringArg(args, 'location') ?? 'Unknown'; - const conditions = ['Sunny', 'Partly Cloudy', 'Overcast', 'Rainy', 'Windy', 'Foggy']; - const temp = Math.round(45 + Math.random() * 50); - const condition = conditions[Math.floor(Math.random() * conditions.length)]; - return { - location: toToolValue(city), - temperature_f: toToolValue(temp), - condition: toToolValue(condition), - humidity_pct: toToolValue(Math.round(30 + Math.random() * 60)), - }; - }, - }, - { - def: { - name: 'calculate', - description: 'Evaluates a mathematical expression and returns the numeric result.', - parameters: [ - { name: 'expression', type: 'string', description: 'Math expression (e.g. "2 + 3 * 4")', required: true }, - ], - category: 'Math', - }, - executor: async (args): Promise> => { - const expr = getStringArg(args, 'expression') ?? '0'; - try { - const sanitized = expr.replace(/[^0-9+\-*/().%\s^]/g, ''); - const val = Function(`"use strict"; return (${sanitized})`)(); - return { result: toToolValue(Number(val)), expression: toToolValue(expr) }; - } catch { - return { error: toToolValue(`Invalid expression: ${expr}`) }; - } - }, - }, - { - def: { - name: 'get_time', - description: 'Returns the current date and time, optionally for a specific timezone.', - parameters: [ - { name: 'timezone', type: 'string', description: 'IANA timezone (e.g. "America/New_York"). Defaults to UTC.', required: false }, - ], - category: 'Utility', - }, - executor: async (args): Promise> => { - const tz = getStringArg(args, 'timezone') ?? 'UTC'; - try { - const now = new Date(); - const formatted = now.toLocaleString('en-US', { timeZone: tz, dateStyle: 'full', timeStyle: 'long' }); - return { datetime: toToolValue(formatted), timezone: toToolValue(tz) }; - } catch { - return { datetime: toToolValue(new Date().toISOString()), timezone: toToolValue('UTC'), note: toToolValue('Fell back to UTC โ€” invalid timezone') }; - } - }, - }, - { - def: { - name: 'random_number', - description: 'Generates a random integer between min and max (inclusive).', - parameters: [ - { name: 'min', type: 'number', description: 'Minimum value', required: true }, - { name: 'max', type: 'number', description: 'Maximum value', required: true }, - ], - category: 'Math', - }, - executor: async (args) => { - const min = getNumberArg(args, 'min') ?? 1; - const max = getNumberArg(args, 'max') ?? 100; - const value = Math.floor(Math.random() * (max - min + 1)) + min; - return { value: toToolValue(value), min: toToolValue(min), max: toToolValue(max) }; - }, - }, -]; - -// --------------------------------------------------------------------------- -// Types for the execution trace -// --------------------------------------------------------------------------- - -interface TraceStep { - type: 'user' | 'tool_call' | 'tool_result' | 'response'; - content: string; - detail?: ToolCall | ToolResult; -} - -// --------------------------------------------------------------------------- -// Custom tool form state -// --------------------------------------------------------------------------- - -interface ParamDraft { - name: string; - type: 'string' | 'number' | 'boolean'; - description: string; - required: boolean; -} - -const EMPTY_PARAM: ParamDraft = { name: '', type: 'string', description: '', required: true }; - -// --------------------------------------------------------------------------- -// Component -// --------------------------------------------------------------------------- - -export function ToolsTab() { - const loader = useModelLoader(ModelCategory.Language); - const [input, setInput] = useState(''); - const [generating, setGenerating] = useState(false); - const [autoExecute, setAutoExecute] = useState(true); - const [trace, setTrace] = useState([]); - const [registeredTools, setRegisteredTools] = useState([]); - const [showToolForm, setShowToolForm] = useState(false); - const [showRegistry, setShowRegistry] = useState(false); - const traceRef = useRef(null); - - // Custom tool form state - const [toolName, setToolName] = useState(''); - const [toolDesc, setToolDesc] = useState(''); - const [toolParams, setToolParams] = useState([{ ...EMPTY_PARAM }]); - - // Register demo tools on mount - useEffect(() => { - ToolCalling.clearTools(); - for (const { def, executor } of DEMO_TOOLS) { - ToolCalling.registerTool(def, executor); - } - setRegisteredTools(ToolCalling.getRegisteredTools()); - return () => { ToolCalling.clearTools(); }; - }, []); - - // Auto-scroll trace - useEffect(() => { - traceRef.current?.scrollTo({ top: traceRef.current.scrollHeight, behavior: 'smooth' }); - }, [trace]); - - const refreshRegistry = useCallback(() => { - setRegisteredTools(ToolCalling.getRegisteredTools()); - }, []); - - // ------------------------------------------------------------------------- - // Generate with tools - // ------------------------------------------------------------------------- - - const send = useCallback(async () => { - const text = input.trim(); - if (!text || generating) return; - - if (loader.state !== 'ready') { - const ok = await loader.ensure(); - if (!ok) return; - } - - setInput(''); - setGenerating(true); - setTrace([{ type: 'user', content: text }]); - - try { - const result: ToolCallingResult = await ToolCalling.generateWithTools(text, { - autoExecute, - maxToolCalls: 5, - temperature: 0.3, - maxTokens: 512, - format: ToolCallFormat.Default, - }); - - // Build trace from result - const steps: TraceStep[] = [{ type: 'user', content: text }]; - - for (let i = 0; i < result.toolCalls.length; i++) { - const call = result.toolCalls[i]; - const argSummary = Object.entries(call.arguments) - .map(([k, v]) => `${k}=${JSON.stringify('value' in v ? v.value : v)}`) - .join(', '); - steps.push({ - type: 'tool_call', - content: `${call.toolName}(${argSummary})`, - detail: call, - }); - - if (result.toolResults[i]) { - const res = result.toolResults[i]; - const resultStr = res.success && res.result - ? JSON.stringify(Object.fromEntries(Object.entries(res.result).map(([k, v]) => [k, 'value' in v ? v.value : v])), null, 2) - : res.error ?? 'Unknown error'; - steps.push({ - type: 'tool_result', - content: res.success ? resultStr : `Error: ${resultStr}`, - detail: res, - }); - } - } - - if (result.text) { - steps.push({ type: 'response', content: result.text }); - } - - setTrace(steps); - } catch (err) { - const msg = err instanceof Error ? err.message : String(err); - setTrace((prev) => [...prev, { type: 'response', content: `Error: ${msg}` }]); - } finally { - setGenerating(false); - } - }, [input, generating, autoExecute, loader]); - - // ------------------------------------------------------------------------- - // Register custom tool - // ------------------------------------------------------------------------- - - const addParam = () => setToolParams((p) => [...p, { ...EMPTY_PARAM }]); - - const updateParam = (idx: number, field: keyof ParamDraft, value: string | boolean) => { - setToolParams((prev) => prev.map((p, i) => (i === idx ? { ...p, [field]: value } : p))); - }; - - const removeParam = (idx: number) => { - setToolParams((prev) => prev.filter((_, i) => i !== idx)); - }; - - const registerCustomTool = () => { - const name = toolName.trim().replace(/\s+/g, '_').toLowerCase(); - const desc = toolDesc.trim(); - if (!name || !desc) return; - - const params = toolParams - .filter((p) => p.name.trim()) - .map((p) => ({ - name: p.name.trim(), - type: p.type as 'string' | 'number' | 'boolean', - description: p.description.trim() || p.name.trim(), - required: p.required, - })); - - const def: ToolDefinition = { name, description: desc, parameters: params, category: 'Custom' }; - - // Mock executor that returns the args back as acknowledgement - const executor = async (args: Record): Promise> => { - const result: Record = { - status: toToolValue('executed'), - tool: toToolValue(name), - }; - for (const [k, v] of Object.entries(args)) { - result[`input_${k}`] = v; - } - return result; - }; - - ToolCalling.registerTool(def, executor); - refreshRegistry(); - setToolName(''); - setToolDesc(''); - setToolParams([{ ...EMPTY_PARAM }]); - setShowToolForm(false); - }; - - const unregisterTool = (name: string) => { - ToolCalling.unregisterTool(name); - refreshRegistry(); - }; - - // ------------------------------------------------------------------------- - // Render - // ------------------------------------------------------------------------- - - return ( -
- - - {/* Toolbar */} -
- - - -
- - {/* Tool registry panel */} - {showRegistry && ( -
-

Registered Tools

- {registeredTools.length === 0 &&

No tools registered

} - {registeredTools.map((t) => ( -
-
- {t.name} - {t.category && {t.category}} - -
-

{t.description}

- {t.parameters.length > 0 && ( -
- {t.parameters.map((p) => ( - - {p.name}: {p.type}{p.required ? ' *' : ''} - - ))} -
- )} -
- ))} -
- )} - - {/* Custom tool form */} - {showToolForm && ( -
-

Register Custom Tool

- setToolName(e.target.value)} - /> - setToolDesc(e.target.value)} - /> -
- Parameters - {toolParams.map((p, i) => ( -
- updateParam(i, 'name', e.target.value)} - /> - - updateParam(i, 'description', e.target.value)} - /> - - {toolParams.length > 1 && ( - - )} -
- ))} - -
-
- - -
-

- Custom tools use a mock executor that echoes back the arguments. Replace with real logic in code. -

-
- )} - - {/* Execution trace */} -
- {trace.length === 0 && ( -
-

Tool Calling

-

{'Ask a question that requires tools โ€” e.g. "What\'s the weather in Tokyo?" or "What is 42 * 17?"'}

-
- - - - -
-
- )} - {trace.map((step, i) => ( -
-
- {step.type === 'user' && '๐Ÿ‘ค User'} - {step.type === 'tool_call' && '๐Ÿ”ง Tool Call'} - {step.type === 'tool_result' && '๐Ÿ“ฆ Result'} - {step.type === 'response' && '๐Ÿค– Response'} -
-
-
{step.content}
-
-
- ))} - {generating && ( -
-
โณ Generating...
-
- )} -
- - {/* Input */} -
{ e.preventDefault(); send(); }}> - setInput(e.target.value)} - disabled={generating} - /> - -
-
- ); -} diff --git a/src/components/VisionTab.tsx b/src/components/VisionTab.tsx deleted file mode 100644 index 469ad66c..00000000 --- a/src/components/VisionTab.tsx +++ /dev/null @@ -1,267 +0,0 @@ -import { useState, useRef, useEffect, useCallback } from 'react'; -import { ModelCategory, VideoCapture } from '@runanywhere/web'; -import { VLMWorkerBridge } from '@runanywhere/web-llamacpp'; -import { useModelLoader } from '../hooks/useModelLoader'; -import { ModelBanner } from './ModelBanner'; - -const LIVE_INTERVAL_MS = 2500; -const LIVE_MAX_TOKENS = 30; -const SINGLE_MAX_TOKENS = 80; -const CAPTURE_DIM = 256; // CLIP resizes internally; larger is wasted work - -interface VisionResult { - text: string; - totalMs: number; -} - -export function VisionTab() { - const loader = useModelLoader(ModelCategory.Multimodal); - const [cameraActive, setCameraActive] = useState(false); - const [processing, setProcessing] = useState(false); - const [liveMode, setLiveMode] = useState(false); - const [result, setResult] = useState(null); - const [error, setError] = useState(null); - const [prompt, setPrompt] = useState('Describe what you see briefly.'); - - const videoMountRef = useRef(null); - const captureRef = useRef(null); - const processingRef = useRef(false); - const liveIntervalRef = useRef | null>(null); - const liveModeRef = useRef(false); - - // Keep refs in sync with state so interval callbacks see latest values - processingRef.current = processing; - liveModeRef.current = liveMode; - - // ------------------------------------------------------------------ - // Camera - // ------------------------------------------------------------------ - const startCamera = useCallback(async () => { - if (captureRef.current?.isCapturing) return; - - setError(null); - - try { - const cam = new VideoCapture({ facingMode: 'environment' }); - await cam.start(); - captureRef.current = cam; - - const mount = videoMountRef.current; - if (mount) { - const el = cam.videoElement; - el.style.width = '100%'; - el.style.borderRadius = '12px'; - mount.appendChild(el); - } - - setCameraActive(true); - } catch (err) { - const msg = err instanceof Error ? err.message : String(err); - - if (msg.includes('NotAllowed') || msg.includes('Permission')) { - setError( - 'Camera permission denied. On macOS, check System Settings โ†’ Privacy & Security โ†’ Camera and ensure your browser is allowed.', - ); - } else if (msg.includes('NotFound') || msg.includes('DevicesNotFound')) { - setError('No camera found on this device.'); - } else if (msg.includes('NotReadable') || msg.includes('TrackStartError')) { - setError('Camera is in use by another application.'); - } else { - setError(`Camera error: ${msg}`); - } - } - }, []); - - // Cleanup on unmount - useEffect(() => { - return () => { - if (liveIntervalRef.current) clearInterval(liveIntervalRef.current); - const cam = captureRef.current; - if (cam) { - cam.stop(); - cam.videoElement.parentNode?.removeChild(cam.videoElement); - captureRef.current = null; - } - }; - }, []); - - // ------------------------------------------------------------------ - // Core: capture + infer - // ------------------------------------------------------------------ - const describeFrame = useCallback(async (maxTokens: number) => { - if (processingRef.current) return; - - const cam = captureRef.current; - if (!cam?.isCapturing) return; - - // Ensure model loaded - if (loader.state !== 'ready') { - const ok = await loader.ensure(); - if (!ok) return; - } - - const frame = cam.captureFrame(CAPTURE_DIM); - if (!frame) return; - - setProcessing(true); - processingRef.current = true; - setError(null); - - const t0 = performance.now(); - - try { - const bridge = VLMWorkerBridge.shared; - if (!bridge.isModelLoaded) { - throw new Error('VLM model not loaded in worker'); - } - - const res = await bridge.process( - frame.rgbPixels, - frame.width, - frame.height, - prompt, - { maxTokens, temperature: 0.6 }, - ); - - setResult({ text: res.text, totalMs: performance.now() - t0 }); - } catch (err) { - const msg = err instanceof Error ? err.message : String(err); - const isWasmCrash = msg.includes('memory access out of bounds') - || msg.includes('RuntimeError'); - - if (isWasmCrash) { - setResult({ text: 'Recovering from memory error... next frame will retry.', totalMs: 0 }); - } else { - setError(msg); - if (liveModeRef.current) stopLive(); - } - } finally { - setProcessing(false); - processingRef.current = false; - } - }, [loader, prompt]); - - // ------------------------------------------------------------------ - // Single-shot - // ------------------------------------------------------------------ - const describeSingle = useCallback(async () => { - if (!captureRef.current?.isCapturing) { - await startCamera(); - return; - } - await describeFrame(SINGLE_MAX_TOKENS); - }, [startCamera, describeFrame]); - - // ------------------------------------------------------------------ - // Live mode - // ------------------------------------------------------------------ - const startLive = useCallback(async () => { - if (!captureRef.current?.isCapturing) { - await startCamera(); - } - - setLiveMode(true); - liveModeRef.current = true; - - // Immediately describe first frame - describeFrame(LIVE_MAX_TOKENS); - - // Then poll every 2.5s โ€” skips ticks while inference is running - liveIntervalRef.current = setInterval(() => { - if (!processingRef.current && liveModeRef.current) { - describeFrame(LIVE_MAX_TOKENS); - } - }, LIVE_INTERVAL_MS); - }, [startCamera, describeFrame]); - - const stopLive = useCallback(() => { - setLiveMode(false); - liveModeRef.current = false; - if (liveIntervalRef.current) { - clearInterval(liveIntervalRef.current); - liveIntervalRef.current = null; - } - }, []); - - const toggleLive = useCallback(() => { - if (liveMode) { - stopLive(); - } else { - startLive(); - } - }, [liveMode, startLive, stopLive]); - - // ------------------------------------------------------------------ - // Render - // ------------------------------------------------------------------ - return ( -
- - -
- {!cameraActive && ( -
-

๐Ÿ“ท Camera Preview

-

Tap below to start the camera

-
- )} -
-
- - setPrompt(e.target.value)} - disabled={liveMode} - /> - -
- {!cameraActive ? ( - - ) : ( - <> - - - - )} -
- - {error && ( -
- Error: {error} -
- )} - - {result && ( -
- {liveMode && LIVE} -

Result

-

{result.text}

- {result.totalMs > 0 && ( -
{(result.totalMs / 1000).toFixed(1)}s
- )} -
- )} -
- ); -} diff --git a/src/components/VoiceTab.tsx b/src/components/VoiceTab.tsx deleted file mode 100644 index e00fab6c..00000000 --- a/src/components/VoiceTab.tsx +++ /dev/null @@ -1,221 +0,0 @@ -import { useState, useRef, useCallback, useEffect } from 'react'; -import { VoicePipeline, ModelCategory, ModelManager, AudioCapture, AudioPlayback, SpeechActivity } from '@runanywhere/web'; -import { VAD } from '@runanywhere/web-onnx'; -import { useModelLoader } from '../hooks/useModelLoader'; -import { ModelBanner } from './ModelBanner'; - -type VoiceState = 'idle' | 'loading-models' | 'listening' | 'processing' | 'speaking'; - -export function VoiceTab() { - const llmLoader = useModelLoader(ModelCategory.Language, true); - const sttLoader = useModelLoader(ModelCategory.SpeechRecognition, true); - const ttsLoader = useModelLoader(ModelCategory.SpeechSynthesis, true); - const vadLoader = useModelLoader(ModelCategory.Audio, true); - - const [voiceState, setVoiceState] = useState('idle'); - const [transcript, setTranscript] = useState(''); - const [response, setResponse] = useState(''); - const [audioLevel, setAudioLevel] = useState(0); - const [error, setError] = useState(null); - - const micRef = useRef(null); - const pipelineRef = useRef(null); - const vadUnsub = useRef<(() => void) | null>(null); - - // Cleanup on unmount - useEffect(() => { - return () => { - micRef.current?.stop(); - vadUnsub.current?.(); - }; - }, []); - - // Ensure all 4 models are loaded - const ensureModels = useCallback(async (): Promise => { - setVoiceState('loading-models'); - setError(null); - - const results = await Promise.all([ - vadLoader.ensure(), - sttLoader.ensure(), - llmLoader.ensure(), - ttsLoader.ensure(), - ]); - - if (results.every(Boolean)) { - setVoiceState('idle'); - return true; - } - - setError('Failed to load one or more voice models'); - setVoiceState('idle'); - return false; - }, [vadLoader, sttLoader, llmLoader, ttsLoader]); - - // Start listening - const startListening = useCallback(async () => { - setTranscript(''); - setResponse(''); - setError(null); - - // Load models if needed - const anyMissing = !ModelManager.getLoadedModel(ModelCategory.Audio) - || !ModelManager.getLoadedModel(ModelCategory.SpeechRecognition) - || !ModelManager.getLoadedModel(ModelCategory.Language) - || !ModelManager.getLoadedModel(ModelCategory.SpeechSynthesis); - - if (anyMissing) { - const ok = await ensureModels(); - if (!ok) return; - } - - setVoiceState('listening'); - - const mic = new AudioCapture({ sampleRate: 16000 }); - micRef.current = mic; - - if (!pipelineRef.current) { - pipelineRef.current = new VoicePipeline(); - } - - // Start VAD + mic - VAD.reset(); - - vadUnsub.current = VAD.onSpeechActivity((activity) => { - if (activity === SpeechActivity.Ended) { - const segment = VAD.popSpeechSegment(); - if (segment && segment.samples.length > 1600) { - processSpeech(segment.samples); - } - } - }); - - await mic.start( - (chunk) => { VAD.processSamples(chunk); }, - (level) => { setAudioLevel(level); }, - ); - }, [ensureModels]); - - // Process a speech segment through the full pipeline - const processSpeech = useCallback(async (audioData: Float32Array) => { - const pipeline = pipelineRef.current; - if (!pipeline) return; - - // Stop mic during processing - micRef.current?.stop(); - vadUnsub.current?.(); - setVoiceState('processing'); - - try { - const result = await pipeline.processTurn(audioData, { - maxTokens: 60, - temperature: 0.7, - systemPrompt: 'You are a helpful voice assistant. Keep responses concise โ€” 1-2 sentences max.', - }, { - onTranscription: (text) => { - setTranscript(text); - }, - onResponseToken: (_token, accumulated) => { - setResponse(accumulated); - }, - onResponseComplete: (text) => { - setResponse(text); - }, - onSynthesisComplete: async (audio, sampleRate) => { - setVoiceState('speaking'); - const player = new AudioPlayback({ sampleRate }); - await player.play(audio, sampleRate); - player.dispose(); - }, - onStateChange: (s) => { - if (s === 'processingSTT') setVoiceState('processing'); - if (s === 'generatingResponse') setVoiceState('processing'); - if (s === 'playingTTS') setVoiceState('speaking'); - }, - }); - - if (result) { - setTranscript(result.transcription); - setResponse(result.response); - } - } catch (err) { - setError(err instanceof Error ? err.message : String(err)); - } - - setVoiceState('idle'); - setAudioLevel(0); - }, []); - - const stopListening = useCallback(() => { - micRef.current?.stop(); - vadUnsub.current?.(); - setVoiceState('idle'); - setAudioLevel(0); - }, []); - - // Which loaders are still loading? - const pendingLoaders = [ - { label: 'VAD', loader: vadLoader }, - { label: 'STT', loader: sttLoader }, - { label: 'LLM', loader: llmLoader }, - { label: 'TTS', loader: ttsLoader }, - ].filter((l) => l.loader.state !== 'ready'); - - return ( -
- {pendingLoaders.length > 0 && voiceState === 'idle' && ( - l.label).join(', ')})`} - /> - )} - - {error &&
{error}
} - -
-
-
-
- -

- {voiceState === 'idle' && 'Tap to start listening'} - {voiceState === 'loading-models' && 'Loading models...'} - {voiceState === 'listening' && 'Listening... speak now'} - {voiceState === 'processing' && 'Processing...'} - {voiceState === 'speaking' && 'Speaking...'} -

- - {voiceState === 'idle' || voiceState === 'loading-models' ? ( - - ) : voiceState === 'listening' ? ( - - ) : null} -
- - {transcript && ( -
-

You said:

-

{transcript}

-
- )} - - {response && ( -
-

AI response:

-

{response}

-
- )} -
- ); -} diff --git a/src/fitness/poseEngine.ts b/src/fitness/poseEngine.ts index 0a555cbd..9b799f65 100644 --- a/src/fitness/poseEngine.ts +++ b/src/fitness/poseEngine.ts @@ -6,6 +6,8 @@ * - Joint angle calculation from 3 keypoints * - Exercise-specific position detection via angle thresholds * - Skeleton drawing helpers + * - Visibility / confidence helpers + * - Hysteresis thresholds for stable rep counting */ import { @@ -46,6 +48,8 @@ export interface PoseAnalysis { angle: number; // primary angle used for detection landmarks: NormalizedLandmark[]; feedback: string; + /** Whether the key landmarks are visible enough for reliable analysis */ + confident: boolean; } export interface ExerciseDef { @@ -53,8 +57,27 @@ export interface ExerciseDef { name: string; icon: string; tips: string[]; + /** Whether this exercise is a static hold (like plank) */ + isHold?: boolean; /** Returns analysis for the given landmarks */ analyze: (lm: NormalizedLandmark[]) => PoseAnalysis; + /** Which landmark indices are critical for this exercise */ + keyLandmarks: number[]; +} + +// --------------------------------------------------------------------------- +// Visibility helpers +// --------------------------------------------------------------------------- +const MIN_VISIBILITY = 0.5; + +/** Check if all the specified landmarks have sufficient visibility */ +export function areLandmarksVisible( + lm: NormalizedLandmark[], + indices: number[], +): boolean { + return indices.every( + i => lm[i] && (lm[i].visibility ?? 0) >= MIN_VISIBILITY, + ); } // --------------------------------------------------------------------------- @@ -84,7 +107,7 @@ function avgSideAngle( } // --------------------------------------------------------------------------- -// Exercise definitions with angle-based analysis +// Exercise definitions with angle-based analysis + hysteresis thresholds // --------------------------------------------------------------------------- export const EXERCISES: ExerciseDef[] = [ { @@ -92,24 +115,25 @@ export const EXERCISES: ExerciseDef[] = [ name: 'Squats', icon: '๐Ÿ‹๏ธ', tips: ['Keep back straight', 'Knees behind toes', 'Thighs parallel to ground'], - analyze(lm) { - // Primary: knee angle (hip โ†’ knee โ†’ ankle) + keyLandmarks: [LM.LEFT_HIP, LM.RIGHT_HIP, LM.LEFT_KNEE, LM.RIGHT_KNEE, LM.LEFT_ANKLE, LM.RIGHT_ANKLE], + analyze(lm: NormalizedLandmark[]) { + const confident = areLandmarksVisible(lm, this.keyLandmarks); const kneeAngle = avgSideAngle( lm, LM.LEFT_HIP, LM.LEFT_KNEE, LM.LEFT_ANKLE, LM.RIGHT_HIP, LM.RIGHT_KNEE, LM.RIGHT_ANKLE, ); - // Form: hip angle (shoulder โ†’ hip โ†’ knee) โ€” check for leaning too far forward const hipAngle = avgSideAngle( lm, LM.LEFT_SHOULDER, LM.LEFT_HIP, LM.LEFT_KNEE, LM.RIGHT_SHOULDER, LM.RIGHT_HIP, LM.RIGHT_KNEE, ); + // Hysteresis: enter down at <90, exit down at >110; enter up at >160, exit up at <145 let position: ExercisePosition = 'middle'; - if (kneeAngle < 100) position = 'down'; - else if (kneeAngle > 155) position = 'up'; + if (kneeAngle < 90) position = 'down'; + else if (kneeAngle > 160) position = 'up'; const form: FormQuality = hipAngle > 60 ? 'good' : 'bad'; const feedback = position === 'down' @@ -118,7 +142,7 @@ export const EXERCISES: ExerciseDef[] = [ ? 'โฌ†๏ธ Stand tall!' : '๐Ÿ”„ Keep going...'; - return { position, form, angle: Math.round(kneeAngle), landmarks: lm, feedback }; + return { position, form, angle: Math.round(kneeAngle), landmarks: lm, feedback, confident }; }, }, { @@ -126,8 +150,9 @@ export const EXERCISES: ExerciseDef[] = [ name: 'Bicep Curls', icon: '๐Ÿ’ช', tips: ['Keep elbows close to body', 'Full range of motion', 'Control the movement'], - analyze(lm) { - // Primary: elbow angle (shoulder โ†’ elbow โ†’ wrist) + keyLandmarks: [LM.LEFT_SHOULDER, LM.RIGHT_SHOULDER, LM.LEFT_ELBOW, LM.RIGHT_ELBOW, LM.LEFT_WRIST, LM.RIGHT_WRIST], + analyze(lm: NormalizedLandmark[]) { + const confident = areLandmarksVisible(lm, this.keyLandmarks); const elbowAngle = avgSideAngle( lm, LM.LEFT_SHOULDER, LM.LEFT_ELBOW, LM.LEFT_WRIST, @@ -135,10 +160,9 @@ export const EXERCISES: ExerciseDef[] = [ ); let position: ExercisePosition = 'middle'; - if (elbowAngle < 60) position = 'up'; // curled - else if (elbowAngle > 140) position = 'down'; // extended + if (elbowAngle < 50) position = 'up'; // curled (tighter threshold) + else if (elbowAngle > 150) position = 'down'; // extended (tighter threshold) - // Form: check if elbows stay near body (elbow x close to hip x) const elbowDrift = Math.abs(lm[LM.LEFT_ELBOW].x - lm[LM.LEFT_HIP].x); const form: FormQuality = elbowDrift < 0.15 ? 'good' : 'bad'; const feedback = position === 'up' @@ -147,7 +171,7 @@ export const EXERCISES: ExerciseDef[] = [ ? 'โฌ‡๏ธ Fully extend arms' : '๐Ÿ”„ Control the movement...'; - return { position, form, angle: Math.round(elbowAngle), landmarks: lm, feedback }; + return { position, form, angle: Math.round(elbowAngle), landmarks: lm, feedback, confident }; }, }, { @@ -155,8 +179,9 @@ export const EXERCISES: ExerciseDef[] = [ name: 'Push-ups', icon: '๐Ÿซธ', tips: ['Keep body straight', 'Chest near ground', 'Full arm extension'], - analyze(lm) { - // Primary: elbow angle + keyLandmarks: [LM.LEFT_SHOULDER, LM.RIGHT_SHOULDER, LM.LEFT_ELBOW, LM.RIGHT_ELBOW, LM.LEFT_WRIST, LM.RIGHT_WRIST], + analyze(lm: NormalizedLandmark[]) { + const confident = areLandmarksVisible(lm, this.keyLandmarks); const elbowAngle = avgSideAngle( lm, LM.LEFT_SHOULDER, LM.LEFT_ELBOW, LM.LEFT_WRIST, @@ -164,10 +189,9 @@ export const EXERCISES: ExerciseDef[] = [ ); let position: ExercisePosition = 'middle'; - if (elbowAngle < 90) position = 'down'; - else if (elbowAngle > 150) position = 'up'; + if (elbowAngle < 80) position = 'down'; + else if (elbowAngle > 155) position = 'up'; - // Form: body alignment (shoulder โ†’ hip โ†’ ankle angle should be ~180) const bodyAngle = avgSideAngle( lm, LM.LEFT_SHOULDER, LM.LEFT_HIP, LM.LEFT_ANKLE, @@ -180,7 +204,7 @@ export const EXERCISES: ExerciseDef[] = [ ? 'โฌ†๏ธ Arms extended!' : '๐Ÿ”„ Push through...'; - return { position, form, angle: Math.round(elbowAngle), landmarks: lm, feedback }; + return { position, form, angle: Math.round(elbowAngle), landmarks: lm, feedback, confident }; }, }, { @@ -188,17 +212,17 @@ export const EXERCISES: ExerciseDef[] = [ name: 'Lunges', icon: '๐Ÿฆต', tips: ['Front knee at 90ยฐ', 'Back knee near floor', 'Keep torso upright'], - analyze(lm) { - // Use the minimum knee angle (the front leg will have the smaller angle) + keyLandmarks: [LM.LEFT_HIP, LM.RIGHT_HIP, LM.LEFT_KNEE, LM.RIGHT_KNEE, LM.LEFT_ANKLE, LM.RIGHT_ANKLE], + analyze(lm: NormalizedLandmark[]) { + const confident = areLandmarksVisible(lm, this.keyLandmarks); const leftKnee = calcAngle(lm[LM.LEFT_HIP], lm[LM.LEFT_KNEE], lm[LM.LEFT_ANKLE]); const rightKnee = calcAngle(lm[LM.RIGHT_HIP], lm[LM.RIGHT_KNEE], lm[LM.RIGHT_ANKLE]); const minKnee = Math.min(leftKnee, rightKnee); let position: ExercisePosition = 'middle'; - if (minKnee < 100) position = 'down'; - else if (minKnee > 155) position = 'up'; + if (minKnee < 95) position = 'down'; + else if (minKnee > 160) position = 'up'; - // Form: torso should be upright (shoulder-hip vertical alignment) const torsoLean = Math.abs(lm[LM.LEFT_SHOULDER].x - lm[LM.LEFT_HIP].x); const form: FormQuality = torsoLean < 0.1 ? 'good' : 'bad'; const feedback = position === 'down' @@ -207,7 +231,7 @@ export const EXERCISES: ExerciseDef[] = [ ? 'โฌ†๏ธ Stand tall!' : '๐Ÿ”„ Lunge deeper...'; - return { position, form, angle: Math.round(minKnee), landmarks: lm, feedback }; + return { position, form, angle: Math.round(minKnee), landmarks: lm, feedback, confident }; }, }, { @@ -215,8 +239,9 @@ export const EXERCISES: ExerciseDef[] = [ name: 'Shoulder Press', icon: '๐Ÿ™†', tips: ['Press directly overhead', 'Don\'t arch back', 'Full extension at top'], - analyze(lm) { - // Primary: elbow angle + keyLandmarks: [LM.LEFT_SHOULDER, LM.RIGHT_SHOULDER, LM.LEFT_ELBOW, LM.RIGHT_ELBOW, LM.LEFT_WRIST, LM.RIGHT_WRIST], + analyze(lm: NormalizedLandmark[]) { + const confident = areLandmarksVisible(lm, this.keyLandmarks); const elbowAngle = avgSideAngle( lm, LM.LEFT_SHOULDER, LM.LEFT_ELBOW, LM.LEFT_WRIST, @@ -224,10 +249,9 @@ export const EXERCISES: ExerciseDef[] = [ ); let position: ExercisePosition = 'middle'; - if (elbowAngle > 155) position = 'up'; // overhead - else if (elbowAngle < 100) position = 'down'; // at shoulders + if (elbowAngle > 160) position = 'up'; + else if (elbowAngle < 95) position = 'down'; - // Form: wrists should be roughly above shoulders (not too far out) const wristOffset = Math.abs( (lm[LM.LEFT_WRIST].x + lm[LM.RIGHT_WRIST].x) / 2 - (lm[LM.LEFT_SHOULDER].x + lm[LM.RIGHT_SHOULDER].x) / 2 @@ -239,16 +263,18 @@ export const EXERCISES: ExerciseDef[] = [ ? 'โฌ‡๏ธ Ready position' : '๐Ÿ”„ Press up...'; - return { position, form, angle: Math.round(elbowAngle), landmarks: lm, feedback }; + return { position, form, angle: Math.round(elbowAngle), landmarks: lm, feedback, confident }; }, }, { id: 'plank', name: 'Plank', icon: '๐Ÿง˜', + isHold: true, tips: ['Keep body in straight line', 'Engage core', 'Don\'t drop hips'], - analyze(lm) { - // Body alignment: shoulder โ†’ hip โ†’ ankle angle + keyLandmarks: [LM.LEFT_SHOULDER, LM.RIGHT_SHOULDER, LM.LEFT_HIP, LM.RIGHT_HIP, LM.LEFT_ANKLE, LM.RIGHT_ANKLE], + analyze(lm: NormalizedLandmark[]) { + const confident = areLandmarksVisible(lm, this.keyLandmarks); const bodyAngle = avgSideAngle( lm, LM.LEFT_SHOULDER, LM.LEFT_HIP, LM.LEFT_ANKLE, @@ -261,7 +287,7 @@ export const EXERCISES: ExerciseDef[] = [ ? '๐Ÿง˜ Great plank! Body is straight!' : 'โš ๏ธ Keep hips up โ€” straighten your body!'; - return { position, form, angle: Math.round(bodyAngle), landmarks: lm, feedback }; + return { position, form, angle: Math.round(bodyAngle), landmarks: lm, feedback, confident }; }, }, ]; diff --git a/src/hooks/useModelLoader.ts b/src/hooks/useModelLoader.ts deleted file mode 100644 index de164f14..00000000 --- a/src/hooks/useModelLoader.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { useState, useCallback, useRef } from 'react'; -import { ModelManager, ModelCategory, EventBus } from '@runanywhere/web'; - -export type LoaderState = 'idle' | 'downloading' | 'loading' | 'ready' | 'error'; - -interface ModelLoaderResult { - state: LoaderState; - progress: number; - error: string | null; - ensure: () => Promise; -} - -/** - * Hook to download + load models for a given category. - * Tracks download progress and loading state. - * - * @param category - Which model category to ensure is loaded. - * @param coexist - If true, only unload same-category models (allows STT+LLM+TTS to coexist). - */ -export function useModelLoader(category: ModelCategory, coexist = false): ModelLoaderResult { - const [state, setState] = useState(() => - ModelManager.getLoadedModel(category) ? 'ready' : 'idle', - ); - const [progress, setProgress] = useState(0); - const [error, setError] = useState(null); - const loadingRef = useRef(false); - - const ensure = useCallback(async (): Promise => { - // Already loaded - if (ModelManager.getLoadedModel(category)) { - setState('ready'); - return true; - } - - if (loadingRef.current) return false; - loadingRef.current = true; - - try { - // Find a model for this category - const models = ModelManager.getModels().filter((m) => m.modality === category); - if (models.length === 0) { - setError(`No ${category} model registered`); - setState('error'); - return false; - } - - const model = models[0]; - - // Download if needed - if (model.status !== 'downloaded' && model.status !== 'loaded') { - setState('downloading'); - setProgress(0); - - const unsub = EventBus.shared.on('model.downloadProgress', (evt) => { - if (evt.modelId === model.id) { - setProgress(evt.progress ?? 0); - } - }); - - await ModelManager.downloadModel(model.id); - unsub(); - setProgress(1); - } - - // Load - setState('loading'); - const ok = await ModelManager.loadModel(model.id, { coexist }); - if (ok) { - setState('ready'); - return true; - } else { - setError('Failed to load model'); - setState('error'); - return false; - } - } catch (err) { - setError(err instanceof Error ? err.message : String(err)); - setState('error'); - return false; - } finally { - loadingRef.current = false; - } - }, [category, coexist]); - - return { state, progress, error, ensure }; -} diff --git a/src/runanywhere.ts b/src/runanywhere.ts deleted file mode 100644 index 8755a47c..00000000 --- a/src/runanywhere.ts +++ /dev/null @@ -1,149 +0,0 @@ -/** - * RunAnywhere SDK initialization and model catalog. - * - * This module: - * 1. Initializes the core SDK (TypeScript-only, no WASM) - * 2. Registers the LlamaCPP backend (loads LLM/VLM WASM) - * 3. Registers the ONNX backend (sherpa-onnx โ€” STT/TTS/VAD) - * 4. Registers the model catalog and wires up VLM worker - * - * Import this module once at app startup. - */ - -import { - RunAnywhere, - SDKEnvironment, - ModelManager, - ModelCategory, - LLMFramework, - type CompactModelDef, -} from '@runanywhere/web'; - -import { LlamaCPP, VLMWorkerBridge } from '@runanywhere/web-llamacpp'; -import { ONNX } from '@runanywhere/web-onnx'; - -// Vite bundles the worker as a standalone JS chunk and returns its URL. -// @ts-ignore โ€” Vite-specific ?worker&url query -import vlmWorkerUrl from './workers/vlm-worker?worker&url'; - -// --------------------------------------------------------------------------- -// Model catalog -// --------------------------------------------------------------------------- - -const MODELS: CompactModelDef[] = [ - // LLM โ€” Liquid AI LFM2 350M (small + fast for chat) - { - id: 'lfm2-350m-q4_k_m', - name: 'LFM2 350M Q4_K_M', - repo: 'LiquidAI/LFM2-350M-GGUF', - files: ['LFM2-350M-Q4_K_M.gguf'], - framework: LLMFramework.LlamaCpp, - modality: ModelCategory.Language, - memoryRequirement: 250_000_000, - }, - // LLM โ€” Liquid AI LFM2 1.2B Tool (optimized for tool calling & function calling) - { - id: 'lfm2-1.2b-tool-q4_k_m', - name: 'LFM2 1.2B Tool Q4_K_M', - repo: 'LiquidAI/LFM2-1.2B-Tool-GGUF', - files: ['LFM2-1.2B-Tool-Q4_K_M.gguf'], - framework: LLMFramework.LlamaCpp, - modality: ModelCategory.Language, - memoryRequirement: 800_000_000, - }, - // VLM โ€” Liquid AI LFM2-VL 450M (vision + language) - { - id: 'lfm2-vl-450m-q4_0', - name: 'LFM2-VL 450M Q4_0', - repo: 'runanywhere/LFM2-VL-450M-GGUF', - files: ['LFM2-VL-450M-Q4_0.gguf', 'mmproj-LFM2-VL-450M-Q8_0.gguf'], - framework: LLMFramework.LlamaCpp, - modality: ModelCategory.Multimodal, - memoryRequirement: 500_000_000, - }, - // VLM - Meta Pose2D - { - id: 'meta-pose2d', - name: 'Meta Pose2D', - repo: 'facebook/sapiens-pose-0.3b-GGUF', - files: ['sapiens_0.3b_goliath_best_goliath_AP_573.pth'], - framework: LLMFramework.LlamaCpp, - modality: ModelCategory.Multimodal, - memoryRequirement: 500_000_000, - }, - // STT (sherpa-onnx archive) - { - id: 'sherpa-onnx-whisper-tiny.en', - name: 'Whisper Tiny English (ONNX)', - url: 'https://huggingface.co/runanywhere/sherpa-onnx-whisper-tiny.en/resolve/main/sherpa-onnx-whisper-tiny.en.tar.gz', - framework: LLMFramework.ONNX, - modality: ModelCategory.SpeechRecognition, - memoryRequirement: 105_000_000, - artifactType: 'archive' as const, - }, - // TTS (sherpa-onnx archive) - { - id: 'vits-piper-en_US-lessac-medium', - name: 'Piper TTS US English (Lessac)', - url: 'https://huggingface.co/runanywhere/vits-piper-en_US-lessac-medium/resolve/main/vits-piper-en_US-lessac-medium.tar.gz', - framework: LLMFramework.ONNX, - modality: ModelCategory.SpeechSynthesis, - memoryRequirement: 65_000_000, - artifactType: 'archive' as const, - }, - // VAD (single ONNX file) - { - id: 'silero-vad-v5', - name: 'Silero VAD v5', - url: 'https://huggingface.co/runanywhere/silero-vad-v5/resolve/main/silero_vad.onnx', - files: ['silero_vad.onnx'], - framework: LLMFramework.ONNX, - modality: ModelCategory.Audio, - memoryRequirement: 5_000_000, - }, -]; - -// --------------------------------------------------------------------------- -// Initialization -// --------------------------------------------------------------------------- - -let _initPromise: Promise | null = null; - -/** Initialize the RunAnywhere SDK. Safe to call multiple times. */ -export async function initSDK(): Promise { - if (_initPromise) return _initPromise; - - _initPromise = (async () => { - // Step 1: Initialize core SDK (TypeScript-only, no WASM) - await RunAnywhere.initialize({ - environment: SDKEnvironment.Development, - debug: true, - }); - - // Step 2: Register backends (loads WASM automatically) - await LlamaCPP.register(); - await ONNX.register(); - - // Step 3: Register model catalog - RunAnywhere.registerModels(MODELS); - - // Step 4: Wire up VLM worker - VLMWorkerBridge.shared.workerUrl = vlmWorkerUrl; - RunAnywhere.setVLMLoader({ - get isInitialized() { return VLMWorkerBridge.shared.isInitialized; }, - init: () => VLMWorkerBridge.shared.init(), - loadModel: (params) => VLMWorkerBridge.shared.loadModel(params), - unloadModel: () => VLMWorkerBridge.shared.unloadModel(), - }); - })(); - - return _initPromise; -} - -/** Get acceleration mode after init. */ -export function getAccelerationMode(): string | null { - return LlamaCPP.isRegistered ? LlamaCPP.accelerationMode : null; -} - -// Re-export for convenience -export { RunAnywhere, ModelManager, ModelCategory, VLMWorkerBridge }; diff --git a/src/styles/index.css b/src/styles/index.css index d03b4c1c..c1bdbf9d 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -1,5 +1,5 @@ /* --------------------------------------------------------------------------- - * RunAnywhere Web Starter โ€” Design System + * Kine-Sight AI Fitness Coach โ€” Design System * Dark theme, modern, responsive * --------------------------------------------------------------------------- */ @@ -64,37 +64,6 @@ body { text-transform: uppercase; } -/* --------------------------------------------------------------------------- - * Tab bar - * --------------------------------------------------------------------------- */ - -.tab-bar { - display: flex; - border-bottom: 1px solid var(--border); -} - -.tab-bar button { - flex: 1; - padding: 10px; - background: none; - border: none; - border-bottom: 2px solid transparent; - color: var(--text-muted); - font-size: 14px; - font-weight: 500; - cursor: pointer; - transition: all 0.2s; -} - -.tab-bar button.active { - color: var(--primary); - border-bottom-color: var(--primary); -} - -.tab-bar button:hover:not(.active) { - color: var(--text); -} - .tab-content { flex: 1; overflow: hidden; @@ -137,7 +106,7 @@ body { @keyframes spin { to { transform: rotate(360deg); } } /* --------------------------------------------------------------------------- - * Model banner + * Model banner (used for MediaPipe loading) * --------------------------------------------------------------------------- */ .model-banner { @@ -202,20 +171,9 @@ body { .btn-lg { padding: 12px 32px; font-size: 16px; } /* --------------------------------------------------------------------------- - * Chat tab + * Empty state * --------------------------------------------------------------------------- */ -.chat-panel { position: relative; } - -.message-list { - flex: 1; - overflow-y: auto; - padding: 16px; - display: flex; - flex-direction: column; - gap: 12px; -} - .empty-state { display: flex; flex-direction: column; @@ -229,476 +187,6 @@ body { .empty-state h3 { color: var(--text); font-size: 18px; } -.message { display: flex; } -.message-user { justify-content: flex-end; } -.message-assistant { justify-content: flex-start; } - -.message-bubble { - max-width: 85%; - padding: 10px 14px; - border-radius: var(--radius); - font-size: 14px; - line-height: 1.5; - word-break: break-word; - white-space: pre-wrap; -} - -.message-user .message-bubble { - background: var(--primary); - color: white; - border-bottom-right-radius: 4px; -} - -.message-assistant .message-bubble { - background: var(--bg-card); - border-bottom-left-radius: 4px; -} - -.message-stats { - margin-top: 6px; - font-size: 11px; - color: var(--text-muted); - opacity: 0.7; -} - -.chat-input { - display: flex; - gap: 8px; - padding: 12px 16px; - border-top: 1px solid var(--border); - background: var(--bg); -} - -.chat-input input { - flex: 1; - padding: 10px 14px; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--bg-input); - color: var(--text); - font-size: 14px; - outline: none; -} - -.chat-input input:focus { border-color: var(--primary); } - -/* --------------------------------------------------------------------------- - * Vision tab - * --------------------------------------------------------------------------- */ - -.vision-panel { padding: 16px; gap: 12px; overflow-y: auto; } - -.vision-camera { - border-radius: var(--radius); - background: var(--bg-card); - min-height: 200px; - display: flex; - align-items: center; - justify-content: center; - overflow: hidden; -} - -.vision-prompt { - width: 100%; - padding: 10px 14px; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--bg-input); - color: var(--text); - font-size: 14px; - outline: none; -} - -.vision-prompt:focus { border-color: var(--primary); } - -.vision-actions { - display: flex; - justify-content: center; - gap: 8px; -} - -.btn-live-active { - background: var(--red); - border-color: var(--red); - color: white; - animation: pulse-live 1.5s ease-in-out infinite; -} - -@keyframes pulse-live { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.7; } -} - -.live-badge { - display: inline-block; - font-size: 10px; - font-weight: 700; - padding: 2px 6px; - border-radius: 4px; - background: var(--red); - color: white; - letter-spacing: 1px; - margin-bottom: 6px; -} - -.vision-result { - padding: 14px; - background: var(--bg-card); - border-radius: var(--radius); - font-size: 14px; - line-height: 1.5; -} - -.vision-result h4 { - font-size: 12px; - color: var(--text-muted); - margin-bottom: 6px; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -/* --------------------------------------------------------------------------- - * Voice tab - * --------------------------------------------------------------------------- */ - -.voice-panel { padding: 16px; gap: 16px; overflow-y: auto; } - -.voice-center { - display: flex; - flex-direction: column; - align-items: center; - gap: 16px; - padding: 32px 0; -} - -.voice-orb { - width: 120px; - height: 120px; - border-radius: 50%; - background: var(--bg-card); - display: flex; - align-items: center; - justify-content: center; - transition: transform 0.2s, box-shadow 0.2s; - transform: scale(calc(1 + var(--level, 0) * 0.3)); -} - -.voice-orb[data-state="listening"] { - box-shadow: 0 0 40px rgba(255, 85, 0, 0.3); -} - -.voice-orb[data-state="processing"], -.voice-orb[data-state="speaking"] { - box-shadow: 0 0 40px rgba(34, 197, 94, 0.3); -} - -.voice-orb-inner { - width: 80px; - height: 80px; - border-radius: 50%; - background: linear-gradient(135deg, var(--primary), var(--primary-hover)); - opacity: 0.8; -} - -.voice-orb[data-state="listening"] .voice-orb-inner { - animation: pulse 1.5s ease-in-out infinite; -} - -@keyframes pulse { - 0%, 100% { transform: scale(1); opacity: 0.8; } - 50% { transform: scale(1.1); opacity: 1; } -} - -.voice-status { - font-size: 14px; - color: var(--text-muted); -} - -.voice-transcript, -.voice-response { - padding: 14px; - background: var(--bg-card); - border-radius: var(--radius); - font-size: 14px; - line-height: 1.5; -} - -.voice-transcript h4, -.voice-response h4 { - font-size: 12px; - color: var(--text-muted); - margin-bottom: 6px; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -/* --------------------------------------------------------------------------- - * Tools tab - * --------------------------------------------------------------------------- */ - -.tools-panel { position: relative; } - -.tools-toolbar { - display: flex; - align-items: center; - gap: 8px; - padding: 10px 16px; - border-bottom: 1px solid var(--border); -} - -.tools-toggle { - margin-left: auto; - display: flex; - align-items: center; - gap: 6px; - font-size: 12px; - color: var(--text-muted); - cursor: pointer; - user-select: none; -} - -.tools-toggle input { accent-color: var(--primary); } - -/* Tool registry */ -.tools-registry { - padding: 12px 16px; - border-bottom: 1px solid var(--border); - background: var(--bg-card); - max-height: 240px; - overflow-y: auto; -} - -.tools-registry h4 { - font-size: 12px; - color: var(--text-muted); - text-transform: uppercase; - letter-spacing: 0.5px; - margin-bottom: 10px; -} - -.tool-card { - padding: 10px; - margin-bottom: 8px; - background: var(--bg-input); - border-radius: var(--radius-sm); -} - -.tool-card:last-child { margin-bottom: 0; } - -.tool-card-header { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 4px; -} - -.tool-card-header strong { font-size: 13px; } - -.tool-category { - font-size: 10px; - padding: 1px 6px; - border-radius: 4px; - background: var(--primary); - color: white; - font-weight: 600; - text-transform: uppercase; -} - -.tool-remove { - margin-left: auto; - padding: 0 6px; - font-size: 16px; - line-height: 1; - border: none; - background: none; - color: var(--text-muted); - cursor: pointer; -} - -.tool-remove:hover { color: var(--red); } - -.tool-card-desc { - font-size: 12px; - color: var(--text-muted); - line-height: 1.4; - margin-bottom: 6px; -} - -.tool-params { - display: flex; - flex-wrap: wrap; - gap: 4px; -} - -.tool-param { - font-size: 11px; - padding: 2px 6px; - border-radius: 4px; - background: var(--bg); - color: var(--text-muted); - font-family: 'SF Mono', 'Fira Code', monospace; -} - -/* Custom tool form */ -.tools-form { - padding: 12px 16px; - border-bottom: 1px solid var(--border); - background: var(--bg-card); - display: flex; - flex-direction: column; - gap: 8px; -} - -.tools-form h4 { - font-size: 12px; - color: var(--text-muted); - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.tools-input { - padding: 8px 10px; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--bg-input); - color: var(--text); - font-size: 13px; - outline: none; - width: 100%; -} - -.tools-input:focus { border-color: var(--primary); } -.tools-input-sm { padding: 6px 8px; font-size: 12px; } - -.tools-form-section { - display: flex; - flex-direction: column; - gap: 6px; -} - -.tools-form-label { - font-size: 11px; - color: var(--text-muted); - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.tools-param-row { - display: flex; - gap: 6px; - align-items: center; -} - -.tools-param-row .tools-input-sm { flex: 1; min-width: 0; } -.tools-param-row select { flex: 0 0 80px; } - -.tools-checkbox { - display: flex; - align-items: center; - gap: 3px; - font-size: 11px; - color: var(--text-muted); - white-space: nowrap; - cursor: pointer; -} - -.tools-checkbox input { accent-color: var(--primary); } - -.tools-form-actions { - display: flex; - gap: 8px; -} - -.tools-form-hint { - font-size: 11px; - color: var(--text-muted); - font-style: italic; -} - -/* Execution trace */ -.tools-trace { - flex: 1; - overflow-y: auto; - padding: 16px; - display: flex; - flex-direction: column; - gap: 10px; -} - -.tools-examples { - display: flex; - flex-wrap: wrap; - gap: 6px; - margin-top: 8px; - justify-content: center; -} - -.trace-step { - border-radius: var(--radius-sm); - overflow: hidden; -} - -.trace-label { - font-size: 11px; - font-weight: 600; - padding: 6px 10px; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.trace-content { - padding: 8px 10px; -} - -.trace-content pre { - font-family: 'SF Mono', 'Fira Code', monospace; - font-size: 12px; - line-height: 1.5; - white-space: pre-wrap; - word-break: break-word; -} - -.trace-step.trace-user { - background: var(--primary); - color: white; -} - -.trace-step.trace-user .trace-label { background: rgba(0,0,0,0.15); } - -.trace-step.trace-tool_call { - background: #1a2744; - border: 1px solid #2563EB; -} - -.trace-step.trace-tool_call .trace-label { - background: #2563EB; - color: white; -} - -.trace-step.trace-tool_result { - background: #1a2e1a; - border: 1px solid var(--green); -} - -.trace-step.trace-tool_result .trace-label { - background: var(--green); - color: white; -} - -.trace-step.trace-response { - background: var(--bg-card); -} - -.trace-step.trace-response .trace-label { - background: var(--bg-input); - color: var(--text-muted); -} - -.trace-step.trace-loading .trace-label { - color: var(--text-muted); - animation: pulse 1.5s ease-in-out infinite; -} - .text-muted { color: var(--text-muted); font-size: 13px; } /* --------------------------------------------------------------------------- @@ -1104,42 +592,6 @@ body { background: #DC2626; } -/* VLM debug panel */ -.fitness-vlm-debug { - margin: 8px 16px; - padding: 8px 12px; - background: rgba(255, 255, 255, 0.03); - border: 1px solid rgba(255, 255, 255, 0.06); - border-radius: var(--radius-sm); - display: flex; - flex-wrap: wrap; - gap: 6px; - align-items: center; - font-size: 11px; -} - -.vlm-debug-label { - font-weight: 600; - color: var(--text-muted); -} - -.vlm-debug-text { - color: var(--text-muted); - font-style: italic; - flex: 1; - min-width: 100px; -} - -.vlm-debug-phase { - font-size: 10px; - font-weight: 700; - padding: 2px 8px; - background: var(--bg-card); - border-radius: 10px; - color: var(--primary); - letter-spacing: 0.5px; -} - /* Skeleton canvas overlay */ .skeleton-canvas { width: 100%; @@ -1188,5 +640,107 @@ body { color: white; } +/* --------------------------------------------------------------------------- + * Countdown Timer Overlay + * --------------------------------------------------------------------------- */ + +.countdown-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background: rgba(15, 23, 42, 0.85); + backdrop-filter: blur(6px); + z-index: 20; + border-radius: var(--radius); +} + +.countdown-number { + font-size: 120px; + font-weight: 900; + line-height: 1; + background: linear-gradient(135deg, var(--primary), #FF8A50); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + animation: countdown-pop 1s ease-out; +} + +.countdown-label { + font-size: 16px; + font-weight: 600; + color: var(--text-muted); + margin-top: 12px; + text-transform: uppercase; + letter-spacing: 3px; +} + +.countdown-go { + font-size: 80px; + font-weight: 900; + color: var(--green); + animation: countdown-pop 0.5s ease-out; + text-shadow: 0 0 40px rgba(34, 197, 94, 0.5); +} + +@keyframes countdown-pop { + 0% { transform: scale(1.6); opacity: 0; } + 50% { transform: scale(0.95); opacity: 1; } + 100% { transform: scale(1); opacity: 1; } +} + +/* Countdown ring animation */ +.countdown-ring { + position: absolute; + width: 180px; + height: 180px; + border-radius: 50%; + border: 4px solid transparent; + border-top-color: var(--primary); + animation: countdown-spin 1s linear; +} + +@keyframes countdown-spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +/* --------------------------------------------------------------------------- + * Plank hold timer + * --------------------------------------------------------------------------- */ +.plank-hold-section { + padding: 16px; + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; +} +.plank-hold-time { + font-size: 48px; + font-weight: 800; + font-family: 'SF Mono', 'Fira Code', monospace; + background: linear-gradient(135deg, var(--primary), #FF8A50); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.plank-hold-label { + font-size: 11px; + font-weight: 700; + color: var(--text-muted); + letter-spacing: 3px; + text-transform: uppercase; +} + +.plank-hold-best { + font-size: 13px; + color: var(--text-muted); +} diff --git a/src/types/mediapipe.d.ts b/src/types/mediapipe.d.ts new file mode 100644 index 00000000..98b60349 --- /dev/null +++ b/src/types/mediapipe.d.ts @@ -0,0 +1,24 @@ +declare module '@mediapipe/tasks-vision' { + export class PoseLandmarker { + static createFromOptions(vision: FilesetResolver, options: any): Promise; + static POSE_CONNECTIONS: any; + detectForVideo(video: HTMLVideoElement, timestamp: number): { landmarks: NormalizedLandmark[][] }; + } + + export class FilesetResolver { + static forVisionTasks(url: string): Promise; + } + + export class DrawingUtils { + constructor(ctx: CanvasRenderingContext2D); + drawConnectors(landmarks: NormalizedLandmark[], connections: any, options?: any): void; + drawLandmarks(landmarks: NormalizedLandmark[], options?: any): void; + } + + export interface NormalizedLandmark { + x: number; + y: number; + z: number; + visibility?: number; + } +} diff --git a/src/workers/vlm-worker.ts b/src/workers/vlm-worker.ts deleted file mode 100644 index a6a09115..00000000 --- a/src/workers/vlm-worker.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * VLM Web Worker entry point. - * - * This file is bundled by Vite as a standalone worker chunk via the - * `?worker&url` import in runanywhere.ts. All VLM inference runs off - * the main thread so the camera and UI stay responsive. - */ -import { startVLMWorkerRuntime } from '@runanywhere/web-llamacpp'; - -startVLMWorkerRuntime(); diff --git a/tests/web-starter-app-bugs.md b/tests/web-starter-app-bugs.md deleted file mode 100644 index f43a72ee..00000000 --- a/tests/web-starter-app-bugs.md +++ /dev/null @@ -1,31 +0,0 @@ -# Web Starter App โ€” Bug Report - -**Test Date:** 2026-02-22 -**Test Method:** Playwright MCP (automated browser testing via Cursor) -**Test Environment:** macOS, Chromium (Playwright), Vite dev server (localhost:5177) -**SDK Version:** `@runanywhere/web@0.1.0-beta.9` (fresh `npm install`) - ---- - -## Summary - -| Metric | Value | -|--------|-------| -| Total tests executed | 85+ | -| Tests passed | 55 | -| Tests skipped | 20 (require hardware: camera, mic, model download) | -| Bugs found | **0** | -| Critical bugs | **0** | -| Console errors | **0** | -| Console warnings | **0** | -| Network failures | **0** | - -**No bugs were found during the comprehensive test run with fresh dependencies.** - ---- - -## Historical Note - -An initial test run with stale `node_modules` (installed Feb 16, vs `package-lock.json` updated Feb 21) showed the acceleration badge not rendering. After `npm install` (7 packages changed), this issue was resolved โ€” the fresh build includes working WebGPU WASM binaries and the badge correctly shows "WebGPU". - -**Lesson:** Always ensure `node_modules` match `package-lock.json` before testing. diff --git a/tests/web-starter-app-test-suite.md b/tests/web-starter-app-test-suite.md deleted file mode 100644 index 79145009..00000000 --- a/tests/web-starter-app-test-suite.md +++ /dev/null @@ -1,481 +0,0 @@ -# Web Starter App โ€” Comprehensive Test Suite - -This test suite validates the RunAnywhere Web Starter App after the multi-package refactor -(`@runanywhere/web` core + `@runanywhere/web-llamacpp` + `@runanywhere/web-onnx`). - ---- - -## Test Categories - -### A. App Load & SDK Initialization - -1. Navigate to http://localhost:5173 โ€” verify the page loads without a blank screen -2. Verify the loading spinner appears initially ("Loading RunAnywhere SDK..." + "Initializing on-device AI engine") -3. After SDK initializes, verify the loading spinner disappears and the main UI renders -4. Verify no SDK error screen is shown (no "SDK Error" heading visible) -5. Verify console logs contain SDK initialization success indicators -6. Verify console contains backend registration logs for LlamaCpp and ONNX backends -7. Verify no critical JavaScript errors in console (exclude expected WASM/SharedArrayBuffer warnings) - -### B. Header & Acceleration Badge - -1. Verify the app header renders with text "RunAnywhere AI" -2. Verify the acceleration badge appears in the header (either "CPU" or "WebGPU") -3. Verify the badge persists across tab navigation (visible on all tabs) -4. Verify badge has correct styling (small badge appearance, not a full button) - -### C. Tab Bar & Navigation - -1. Verify 3 tabs render in the tab bar: "๐Ÿ’ฌ Chat", "๐Ÿ“ท Vision", "๐ŸŽ™๏ธ Voice" -2. Verify Chat tab is active/selected by default on load -3. Click Vision tab โ€” verify it becomes active (highlighted) and Chat becomes inactive -4. Click Voice tab โ€” verify it becomes active and Vision becomes inactive -5. Click Chat tab โ€” verify it returns to active state -6. Navigate rapidly between all 3 tabs (Chat โ†’ Vision โ†’ Voice โ†’ Chat โ†’ Vision) โ€” verify no crashes or blank states -7. Verify only one tab panel is visible at a time (previous tab content is unmounted) -8. Verify tab buttons have consistent sizing and layout - -### D. Chat Tab โ€” UI Elements - -1. Navigate to Chat tab โ€” verify the tab panel renders -2. Verify the ModelBanner is shown with text about "No LLM model loaded" and a "Download & Load" button -3. Verify the empty state renders: heading "Start a conversation" and subtext "Type a message below to chat with on-device AI" -4. Verify the message input form renders at the bottom with placeholder "Message..." -5. Verify the Send button renders and is initially disabled (since input is empty) -6. Type text in the input โ€” verify the Send button becomes enabled -7. Clear the input โ€” verify the Send button becomes disabled again -8. Verify the input field can accept text and displays it correctly -9. Verify the message list area is scrollable (has proper layout for future messages) - -### E. Chat Tab โ€” Interaction Behavior - -1. With no model loaded, type a message and press Send โ€” verify ModelBanner triggers download/load sequence -2. Verify that the "Download & Load" button on the banner is clickable -3. Verify that typing in the input and pressing Enter submits (or attempts to submit) the message -4. Verify that the Stop button does NOT appear when no generation is in progress -5. Verify the input field is not disabled when idle (no generation in progress) - -### F. Vision Tab โ€” UI Elements - -1. Navigate to Vision tab โ€” verify the tab panel renders -2. Verify the ModelBanner shows for VLM model with "Download & Load" button (label: "VLM") -3. Verify the camera preview area renders with empty state: "๐Ÿ“ท Camera Preview" heading and "Tap below to start the camera" subtext -4. Verify the prompt input field renders with placeholder "What do you want to know about the image?" -5. Verify the prompt field has a default value of "Describe what you see briefly." -6. Verify the "Start Camera" button renders and is visible -7. Verify the "Describe" button is NOT visible when camera is not active -8. Verify the "Live" button is NOT visible when camera is not active -9. Verify the result display area is not visible when no result exists - -### G. Vision Tab โ€” Camera Interaction - -1. Click "Start Camera" โ€” verify the button text changes (camera starts) -2. After camera starts, verify the "Describe" button appears -3. After camera starts, verify the "โ–ถ Live" toggle button appears -4. Verify the prompt input remains editable when camera is active -5. Edit the prompt text โ€” verify the value updates -6. Verify the empty state ("๐Ÿ“ท Camera Preview") disappears when camera is active - -### H. Vision Tab โ€” Live Mode - -1. With camera active, click "โ–ถ Live" โ€” verify the button changes to "โน Stop Live" -2. Verify the "โน Stop Live" button has active/pulsing styling (red background) -3. Verify the prompt input becomes disabled during live mode -4. Click "โน Stop Live" โ€” verify it returns to "โ–ถ Live" and prompt input becomes editable again - -### I. Voice Tab โ€” UI Elements - -1. Navigate to Voice tab โ€” verify the tab panel renders -2. Verify the ModelBanner shows for voice models (multiple models: VAD, STT, LLM, TTS) -3. Verify the voice orb renders (centered circular element) -4. Verify the voice orb has idle styling (no glow animation) -5. Verify the status text shows "Tap to start listening" in idle state -6. Verify the "Start Listening" button renders and is enabled -7. Verify the transcript section is NOT visible initially (no transcript yet) -8. Verify the response section is NOT visible initially (no AI response yet) - -### J. Voice Tab โ€” Interaction Behavior - -1. Click "Start Listening" โ€” verify the button changes to "Stop" (or model loading begins) -2. If models not loaded, verify the status text changes to "Loading models..." and button shows disabled state -3. Verify that after model loading, the orb styling changes to listening state (orange glow) -4. Verify the status text updates to "Listening... speak now" when active -5. Click "Stop" during listening โ€” verify it returns to idle state - -### K. ModelBanner Component โ€” States - -1. On Chat tab (no model loaded): Verify banner text "No LLM model loaded." is shown -2. On Chat tab: Verify "Download & Load" button is rendered in the banner -3. On Vision tab (no model loaded): Verify banner text mentions "VLM" -4. On Voice tab (no models loaded): Verify banner mentions voice model types -5. Verify banner disappears when model is in "ready" state -6. Verify banner shows progress bar during download (if download is triggered) -7. Verify banner shows "Loading ... model into engine..." text during model loading -8. Verify banner shows error text with "Retry" button on failure - -### L. Styling & Theming - -1. Verify the app has a dark theme (dark navy background, light text) -2. Verify the primary accent color is orange (#FF5500) โ€” visible on buttons, active tab -3. Verify buttons have consistent styling (border radius, padding) -4. Verify the tab bar has proper visual separation from content area -5. Verify message bubbles would have different alignment (user right, assistant left) -6. Verify the loading spinner animation plays smoothly -7. Verify the app is centered and has max-width constraint (mobile-friendly layout, ~600px) -8. Verify responsive layout works at common viewport widths (375px, 768px, 1024px) - -### M. Cross-Tab State Behavior - -1. Start on Chat tab, switch to Vision tab, switch back to Chat โ€” verify Chat tab re-renders clean (empty state or with messages if any) -2. Start on Chat tab, switch to Voice, switch back โ€” verify no stale state -3. Verify switching tabs does not cause duplicate ModelBanners -4. Verify each tab shows its own ModelBanner independently (different model categories) - -### N. Error Handling & Edge Cases - -1. Verify no uncaught promise rejections in console on initial load -2. Verify switching tabs rapidly (10+ switches) causes no errors -3. Verify the app doesn't freeze or become unresponsive during tab switches -4. Verify that the app handles missing WebGPU gracefully (falls back to CPU badge) -5. Verify no CORS-related errors in the console - -### O. Console Error Audit - -1. After all tests, collect all console errors -2. Classify: expected (WASM fallback, SharedArrayBuffer) vs unexpected (real bugs) -3. Report any JavaScript TypeError or ReferenceError as bugs -4. Report any React rendering errors as bugs -5. Report any network/fetch failures as bugs - -### P. Telemetry & Network Audit (Supabase) - -1. Capture ALL network requests made during the full test session (page load through tab navigation) -2. Filter for any requests to Supabase endpoints (e.g., `supabase.co`, `supabase.io`, or any `rest/v1/`, `auth/v1/` paths) -3. Filter for any requests to analytics/telemetry services (e.g., `analytics`, `events`, `track`, `metrics`) -4. Verify whether the SDK (`@runanywhere/web`) sends any outbound telemetry on initialization -5. Verify whether model download/load events trigger any telemetry calls -6. Document all external (non-localhost) network requests with method, URL, and status -7. Report if zero telemetry is found (i.e., no Supabase or analytics calls detected) - ---- - -## Test Results (2025-02-22 โ€” Playwright MCP Run, Fresh Build) - -> **IMPORTANT:** The initial test run used stale `node_modules` (installed Feb 16). After running -> `npm install` (7 packages changed), the test was re-run with fresh dependencies. The results -> below reflect the **fresh build** which includes the new telemetry layer and WebGPU WASM fix. - -### A. App Load & SDK Initialization โ€” Results - -| # | Test | Result | Notes | -|---|------|--------|-------| -| A.1 | Page loads at localhost | โœ… PASS | Page loaded at http://localhost:5177 | -| A.2 | Loading spinner appears | โœ… PASS | "Loading RunAnywhere SDK..." + "Initializing on-device AI engine" shown | -| A.3 | Spinner disappears, main UI renders | โœ… PASS | Main UI with header + tabs + content rendered after ~15s | -| A.4 | No SDK error screen | โœ… PASS | No "SDK Error" heading visible | -| A.5 | Console logs show SDK init success | โœ… PASS | `[RunAnywhere:RunAnywhere] RunAnywhere Web SDK initialized successfully` | -| A.6 | Backend registration logs | โœ… PASS | LlamaCpp: `Backend 'llamacpp' registered โ€” capabilities: [llm, vlm, ...]`; ONNX: `Backend 'onnx' registered โ€” capabilities: [stt, tts, vad]` | -| A.7 | No critical JS errors | โœ… PASS | 0 errors, 0 warnings in fresh build (WebGPU WASM loads successfully) | - -### B. Header & Acceleration Badge โ€” Results - -| # | Test | Result | Notes | -|---|------|--------|-------| -| B.1 | Header renders "RunAnywhere AI" | โœ… PASS | `

RunAnywhere AI

` in header | -| B.2 | Acceleration badge appears | โœ… PASS | Badge shows **"WebGPU"** (fixed in fresh build โ€” stale deps had missing WASM) | -| B.3 | Badge persists across tabs | โœ… PASS | Badge is in header, visible across all tab navigations | -| B.4 | Badge has correct styling | โœ… PASS | Small badge appearance next to heading | - -### C. Tab Bar & Navigation โ€” Results - -| # | Test | Result | Notes | -|---|------|--------|-------| -| C.1 | 3 tabs render | โœ… PASS | "๐Ÿ’ฌ Chat", "๐Ÿ“ท Vision", "๐ŸŽ™๏ธ Voice" | -| C.2 | Chat tab active by default | โœ… PASS | Chat button has `[active]` attribute | -| C.3 | Vision tab activates on click | โœ… PASS | Vision shows `[active]`, Chat loses it | -| C.4 | Voice tab activates on click | โœ… PASS | Voice shows `[active]`, Vision loses it | -| C.5 | Chat tab re-activates | โœ… PASS | Returns to active state correctly | -| C.6 | Rapid navigation (12 clicks) | โœ… PASS | No crashes, correct tab shown, 1 panel visible | -| C.7 | Only one tab panel visible | โœ… PASS | Panel count always 1 across all switches | -| C.8 | Tab buttons consistent sizing | โœ… PASS | All tabs: 200ร—41px, padding 10px | - -### D. Chat Tab โ€” UI Elements โ€” Results - -| # | Test | Result | Notes | -|---|------|--------|-------| -| D.1 | Tab panel renders | โœ… PASS | `.chat-panel` element present | -| D.2 | ModelBanner with "No LLM model loaded" + Download button | โœ… PASS | Banner text + "Download & Load" button present | -| D.3 | Empty state renders | โœ… PASS | "Start a conversation" + "Type a message below to chat with on-device AI" | -| D.4 | Message input renders | โœ… PASS | Placeholder "Message..." | -| D.5 | Send button disabled initially | โœ… PASS | `[disabled]` attribute present | -| D.6 | Type text โ†’ Send enables | โœ… PASS | Typed "Hello AI" โ†’ Send button enabled | -| D.7 | Clear input โ†’ Send disables | โœ… PASS | Cleared input โ†’ Send button disabled again | -| D.8 | Input accepts and displays text | โœ… PASS | Value shown correctly in input | -| D.9 | Message list scrollable | โœ… PASS | `overflow-y: auto` on `.message-list` | - -### E. Chat Tab โ€” Interaction Behavior โ€” Results - -| # | Test | Result | Notes | -|---|------|--------|-------| -| E.1 | Send without model โ†’ triggers load | โœ… PASS | Clicked "Download & Load" โ†’ model downloaded from HuggingFace (302โ†’200) โ†’ loaded in 3012ms | -| E.2 | Download & Load button clickable | โœ… PASS | Button clicked, triggered full download+load pipeline | -| E.3 | Enter submits message | โœ… PASS | Typed "Hello, what is 2+2?" โ†’ Send โ†’ AI responded "2 + 2 equals 4." (4 tokens, 6.0 tok/s, 670ms) | -| E.4 | Stop button NOT shown (no generation) | โœ… PASS | Only "Download & Load" + "Send" buttons in panel when idle | -| E.5 | Input not disabled when idle | โœ… PASS | `input.disabled === false` | - -### F. Vision Tab โ€” UI Elements โ€” Results - -| # | Test | Result | Notes | -|---|------|--------|-------| -| F.1 | Tab panel renders | โœ… PASS | `.vision-panel` present | -| F.2 | ModelBanner "VLM" with Download button | โœ… PASS | "No VLM model loaded." + "Download & Load" | -| F.3 | Camera preview empty state | โœ… PASS | "๐Ÿ“ท Camera Preview" + "Tap below to start the camera" | -| F.4 | Prompt input renders | โœ… PASS | Placeholder "What do you want to know about the image?" | -| F.5 | Default prompt value | โœ… PASS | "Describe what you see briefly." | -| F.6 | Start Camera button visible | โœ… PASS | Button present and enabled | -| F.7 | Describe button NOT visible (no camera) | โœ… PASS | Not in DOM | -| F.8 | Live button NOT visible (no camera) | โœ… PASS | Not in DOM | -| F.9 | Result area not visible | โœ… PASS | `.vision-result` not in DOM | - -### G. Vision Tab โ€” Camera Interaction โ€” Results - -| # | Test | Result | Notes | -|---|------|--------|-------| -| G.1-G.6 | Camera tests | โญ SKIP | Requires camera hardware โ€” cannot test in headless Playwright | - -### H. Vision Tab โ€” Live Mode โ€” Results - -| # | Test | Result | Notes | -|---|------|--------|-------| -| H.1-H.4 | Live mode tests | โญ SKIP | Requires camera hardware โ€” cannot test in headless Playwright | - -### I. Voice Tab โ€” UI Elements โ€” Results - -| # | Test | Result | Notes | -|---|------|--------|-------| -| I.1 | Tab panel renders | โœ… PASS | `.voice-panel` present | -| I.2 | ModelBanner for voice models | โœ… PASS | "No Voice (VAD, STT, LLM, TTS) model loaded." | -| I.3 | Voice orb renders | โœ… PASS | `.voice-orb` exists with `display: flex` | -| I.4 | Orb idle styling (no glow) | โœ… PASS | `box-shadow: none`, `data-state="idle"` | -| I.5 | Status text "Tap to start listening" | โœ… PASS | Exact text match | -| I.6 | Start Listening button enabled | โœ… PASS | Present and `disabled === false` | -| I.7 | Transcript NOT visible | โœ… PASS | `.voice-transcript` not in DOM | -| I.8 | Response NOT visible | โœ… PASS | `.voice-response` not in DOM | - -### J. Voice Tab โ€” Interaction Behavior โ€” Results - -| # | Test | Result | Notes | -|---|------|--------|-------| -| J.1-J.5 | Voice interaction tests | โญ SKIP | Requires microphone + model download โ€” cannot fully test | - -### K. ModelBanner Component โ€” States โ€” Results - -| # | Test | Result | Notes | -|---|------|--------|-------| -| K.1 | Chat: "No LLM model loaded." | โœ… PASS | Exact text match | -| K.2 | Chat: "Download & Load" button | โœ… PASS | Button present, clicked, worked | -| K.3 | Vision: Banner mentions "VLM" | โœ… PASS | "No VLM model loaded." | -| K.4 | Voice: Banner mentions voice types | โœ… PASS | "No Voice (VAD, STT, LLM, TTS) model loaded." | -| K.5 | Banner disappears when ready | โœ… PASS | After LLM model loaded (3012ms), banner disappeared completely | -| K.6 | Progress bar during download | โœ… PASS | Model was OPFS-cached so download was instant; banner transitioned directly to loading state | -| K.7 | "Loading ... model into engine..." | โœ… PASS | Banner showed "Loading LLM model into engine..." during model load | -| K.8 | Error text with Retry button | โญ SKIP | Would require triggering a download failure scenario | - -### L. Styling & Theming โ€” Results - -| # | Test | Result | Notes | -|---|------|--------|-------| -| L.1 | Dark theme | โœ… PASS | Body BG: `rgb(15, 23, 42)` (dark navy), header text: `rgb(241, 245, 249)` (light) | -| L.2 | Primary accent #FF5500 | โœ… PASS | Primary button BG: `rgb(255, 85, 0)`, active tab border: `rgb(255, 85, 0)` | -| L.3 | Buttons consistent styling | โœ… PASS | All tabs: consistent 200ร—41px, padding 10px | -| L.4 | Tab bar visual separation | โœ… PASS | `border-bottom: 1px solid rgb(51, 65, 85)` | -| L.5 | Message bubble alignment | โญ SKIP | No messages to test (requires model) | -| L.6 | Loading spinner animation | โœ… PASS | Observed during SDK init | -| L.7 | Max-width ~600px | โœ… PASS | `max-width: 600px` | -| L.8 | Responsive layout | โœ… PASS | Tested at 375px, 768px, 1024px โ€” all render correctly | - -### M. Cross-Tab State Behavior โ€” Results - -| # | Test | Result | Notes | -|---|------|--------|-------| -| M.1 | Chat โ†’ Vision โ†’ Chat: clean re-render | โœ… PASS | 1 banner, 1 panel, empty state shown | -| M.2 | Chat โ†’ Voice โ†’ Chat: no stale state | โœ… PASS | 1 banner, clean state | -| M.3 | No duplicate ModelBanners | โœ… PASS | Each tab: exactly 1 banner | -| M.4 | Each tab has own ModelBanner | โœ… PASS | Chat: "LLM", Vision: "VLM", Voice: "VAD, STT, LLM, TTS" | - -### N. Error Handling & Edge Cases โ€” Results - -| # | Test | Result | Notes | -|---|------|--------|-------| -| N.1 | No uncaught promise rejections | โœ… PASS | No unhandled rejections in console | -| N.2 | Rapid tab switching (12+) no errors | โœ… PASS | 12 rapid clicks, no errors | -| N.3 | App doesn't freeze during switches | โœ… PASS | Responsive throughout | -| N.4 | Missing WebGPU fallback | โœ… PASS | Graceful fallback: WebGPU 404 โ†’ CPU WASM loaded | -| N.5 | No CORS errors | โœ… PASS | No CORS-related console errors | - -### O. Console Error Audit โ€” Results - -| Category | Count | Details | -|----------|-------|---------| -| Total console messages | 92+ | Full session including model load + generation | -| Errors | 0 | โœ… None | -| Warnings | 4 | Expected GGML warnings (context size, sched_reserve) โ€” non-critical | -| TypeError / ReferenceError | 0 | โœ… None | -| React rendering errors | 0 | โœ… None | -| Network/fetch failures | 0 | โœ… All requests 200/201/304 | - -**Verdict:** Zero errors across the entire session including model download, load, and text generation. The 4 warnings are internal llama.cpp GGML diagnostics (context window sizing) โ€” expected and non-critical. - -### P. Telemetry & Network Audit โ€” Results - -**Total network requests observed at init: 63** - -| Category | Count | Details | -|----------|-------|---------| -| Requests to `supabase.co` / `supabase.io` | **0** | No outbound Supabase calls at initialization | -| Requests to analytics endpoints | **0** | No analytics/telemetry POSTs at initialization | -| External (non-localhost) requests | **0** | ALL 63 requests are to `localhost:5177` | -| Failed requests | 0 | All 200 OK | - -#### Telemetry Infrastructure (Present but Dormant at Init) - -The **fresh build** (`npm install` โ€” 7 packages changed) revealed a full telemetry layer in the SDK that was **missing from stale deps**: - -**New files in `@runanywhere/web@0.1.0-beta.9`:** -- `dist/services/HTTPService.js` โ€” Centralized HTTP transport with Supabase dev routing -- `dist/services/AnalyticsEmitter.js` โ€” Abstract telemetry emission interface - -**New files in `@runanywhere/web-llamacpp@0.1.0-beta.9`:** -- `dist/Foundation/TelemetryService.js` โ€” C++ telemetry manager bridge (WASM โ†” browser fetch) -- `dist/Foundation/WASMAnalyticsEmitter.js` โ€” Routes analytics events via C++ ccall() -- `dist/Foundation/AnalyticsEventsBridge.js` โ€” Forwards C++ events to EventBus + TelemetryService - -#### Telemetry Initialization Flow (confirmed in console logs) - -| Step | Log Message | What Happens | -|------|-------------|--------------| -| 1 | `[TelemetryService] Telemetry HTTP callback registered` | C++ telemetry manager gets an HTTP callback | -| 2 | `[HTTPService] Development mode configured with Supabase` | HTTPService receives Supabase URL/key from WASM dev config | -| 3 | `[TelemetryService] HTTPService configured with WASM dev config (Supabase)` | Supabase credentials read from compiled WASM binary | -| 4 | `[TelemetryService] TelemetryService initialized (env=development, device=52a855f8...)` | Device UUID generated/persisted in localStorage | -| 5 | `[AnalyticsEventsBridge] Analytics events callback registered` | C++ analytics events โ†’ EventBus + TelemetryService | -| 6 | `[AnalyticsEmitter] Analytics emitter backend registered` | WASMAnalyticsEmitter wired as backend | - -#### Architecture: How Telemetry Flows to Supabase - -``` -User Action (model download/STT/TTS/VAD) - โ†“ -AnalyticsEmitter.emit*() (TypeScript singleton) - โ†“ -WASMAnalyticsEmitter (Emscripten ccall โ†’ C++) - โ†“ -C++ rac_analytics_emit_*() โ†’ rac_telemetry_manager - โ†“ -HTTP callback (registered by TelemetryService) - โ†“ -TelemetryService.performHttpPost() - โ†“ -HTTPService.shared.post(endpoint, body) (browser fetch) - โ†“ -Supabase REST API: POST /rest/v1/telemetry_events - (with apikey + Authorization headers) -``` - -#### Key Configuration Details - -- **Supabase credentials:** Compiled into the WASM binary (`rac_wasm_dev_config_*`), NOT in app source -- **Environment:** `development` โ†’ routes to Supabase; `staging`/`production` โ†’ routes to Railway backend -- **Device ID:** Persisted in `localStorage` under key `rac_device_id` (UUID generated via `crypto.randomUUID()`) -- **Telemetry table:** `rest/v1/telemetry_events` (V2 schema with column filtering for Supabase compatibility) -- **Error handling:** All telemetry failures are silently caught โ€” telemetry must never crash the app - -#### When Telemetry Calls Fire (NOT at init) - -Telemetry POSTs to Supabase are triggered by these events: -- `emitModelDownloadStarted(modelId)` โ€” when model download begins -- `emitModelDownloadCompleted(modelId, fileSizeBytes, durationMs)` โ€” when download finishes -- `emitModelDownloadFailed(modelId, errorMessage)` โ€” when download fails -- `emitSTTTranscriptionCompleted(...)` โ€” after successful speech-to-text -- `emitSTTTranscriptionFailed(...)` โ€” after failed speech-to-text -- `emitTTSSynthesisCompleted(...)` โ€” after successful text-to-speech -- `emitTTSSynthesisFailed(...)` โ€” after failed text-to-speech -- `emitVADSpeechStarted()` / `emitVADSpeechEnded(...)` โ€” voice activity detection events -- `emitSTTModelLoadCompleted(...)` / `emitTTSVoiceLoadCompleted(...)` โ€” model load events - -#### Live Supabase Data Verification (Queried During Test) - -Directly queried the Supabase `telemetry_events` table using the SDK's own credentials: - -**`telemetry_events` table โ€” summary of 50 most recent rows:** - -| Event Type | Count | Notes | -|-----------|-------|-------| -| `llm.model.load.started` | 14 | Model load lifecycle start | -| `llm.model.load.completed` | 8 | Successful loads (processing_time: 1.6sโ€“16.8s) | -| `llm.model.load.failed` | 5 | Failed loads (e.g., "Model load failed" for smollm2-360m) | -| `llm.generation.started` | 6 | Text generation begins | -| `llm.generation.first_token` | 6 | Time to first token tracked | -| `llm.generation.completed` | 6 | Full generation (processing_time: 4.7sโ€“73.7s) | -| `model.download.started` | 2 | Model downloads from HuggingFace | -| `model.extraction.started` | 1 | Archive extraction (iOS) | -| `device.registered` | 2 | Device registration events | - -**By Platform:** - -| Platform | Events | SDK Version | -|----------|--------|-------------| -| web | 25 | `0.1.0-beta.8` | -| ios | 25 | `0.16.0` | - -**`sdk_devices` table โ€” 5 registered devices:** - -| Platform | SDK Version | Registered | -|----------|-------------|------------| -| android | 0.2.0 | 2026-02-21 | -| android | 0.2.0 | 2026-02-21 | -| android | 0.2.0 | 2026-02-21 | - -**Sample web telemetry entries (from prior session, device `26a35b3f...`):** - -| Time | Event | Model | Processing Time | -|------|-------|-------|----------------| -| 01:45:41 | `llm.generation.completed` | lfm2-350m-q4_k_m | 73,725ms | -| 01:44:29 | `llm.generation.first_token` | lfm2-350m-q4_k_m | โ€” | -| 01:44:27 | `llm.generation.started` | lfm2-350m-q4_k_m | โ€” | -| 01:44:16 | `llm.generation.completed` | lfm2-350m-q4_k_m | 4,754ms | -| 01:39:52 | `llm.model.load.completed` | lfm2-350m-q4_k_m | 16,831ms | -| 01:39:35 | `llm.model.load.started` | lfm2-350m-q4_k_m | โ€” | -| 01:38:56 | `model.download.started` | โ€” | โ€” | - -#### This Test Session โ€” Verified Supabase Entries (device `52a855f8...`) - -After clicking "Download & Load" and sending one chat message, **6 Supabase POST requests** were made (all returned **201 Created**): - -| # | Time (UTC) | Event Type | Model | Processing Time | Success | -|---|-----------|-----------|-------|----------------|---------| -| 1 | 02:44:35 | `model.download.started` | โ€” | โ€” | โ€” | -| 2 | 02:44:39 | `llm.model.load.started` | lfm2-350m-q4_k_m | โ€” | โ€” | -| 3 | 02:44:42 | `llm.model.load.completed` | lfm2-350m-q4_k_m | **3,011ms** | true | -| 4 | 02:45:42 | `llm.generation.started` | lfm2-350m-q4_k_m | โ€” | โ€” | -| 5 | 02:45:42 | `llm.generation.first_token` | lfm2-350m-q4_k_m | โ€” | โ€” | -| 6 | 02:45:42 | `llm.generation.completed` | lfm2-350m-q4_k_m | **667ms** | true | - -All entries include: `platform: "web"`, `sdk_version: "0.1.0-beta.8"`, `framework: "llamacpp"`, `session_id` for generation events. - -**Network requests summary for this session:** - -| Destination | Method | Count | Status | -|-------------|--------|-------|--------| -| `localhost:5177` (app assets) | GET | 64 | 200/304 | -| `fhtgjtxuoikwwouxqzrn.supabase.co/rest/v1/telemetry_events` | POST | **6** | **201 Created** | -| `huggingface.co` (model download) | GET | 1 | 302 โ†’ 200 | - -**Conclusion:** The full telemetry pipeline is **verified end-to-end**. Model download, load, and text generation each produce telemetry events that are successfully POSTed to the Supabase `telemetry_events` table. The chat response ("2 + 2 equals 4.", 4 tokens, 6.0 tok/s, 670ms) was generated on-device via WebGPU and the corresponding telemetry was confirmed in the database. - ---- - -## Bug Report File - -Bugs found during testing will be written to: -`tests/web-starter-app-bugs.md` diff --git a/vite.config.ts b/vite.config.ts index b0e55c23..9f0055bc 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,80 +1,14 @@ -import { defineConfig, type Plugin } from 'vite'; +import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; -import path from 'path'; -import fs from 'fs'; -import { fileURLToPath } from 'url'; - -const __dir = path.dirname(fileURLToPath(import.meta.url)); - -/** - * Copies WASM binaries from the @runanywhere npm packages into dist/assets/ - * so they're served alongside the bundled JS at runtime. - * - * In dev mode, Vite serves node_modules directly so this only - * matters for production builds. - */ -function copyWasmPlugin(): Plugin { - const llamacppWasm = path.resolve(__dir, 'node_modules/@runanywhere/web-llamacpp/wasm'); - const onnxWasm = path.resolve(__dir, 'node_modules/@runanywhere/web-onnx/wasm'); - - return { - name: 'copy-wasm', - writeBundle(options) { - const outDir = options.dir ?? path.resolve(__dir, 'dist'); - const assetsDir = path.join(outDir, 'assets'); - fs.mkdirSync(assetsDir, { recursive: true }); - - // LlamaCpp WASM binaries (LLM/VLM) - const llamacppFiles = [ - { src: 'racommons-llamacpp.wasm', dest: 'racommons-llamacpp.wasm' }, - { src: 'racommons-llamacpp.js', dest: 'racommons-llamacpp.js' }, - { src: 'racommons-llamacpp-webgpu.wasm', dest: 'racommons-llamacpp-webgpu.wasm' }, - { src: 'racommons-llamacpp-webgpu.js', dest: 'racommons-llamacpp-webgpu.js' }, - ]; - - for (const { src, dest } of llamacppFiles) { - const srcPath = path.join(llamacppWasm, src); - if (fs.existsSync(srcPath)) { - fs.copyFileSync(srcPath, path.join(assetsDir, dest)); - const sizeMB = (fs.statSync(srcPath).size / 1_000_000).toFixed(1); - console.log(` โœ“ Copied ${dest} (${sizeMB} MB)`); - } else { - console.warn(` โš  Not found: ${srcPath}`); - } - } - - // Sherpa-ONNX: copy all files in sherpa/ subdirectory (STT/TTS/VAD) - const sherpaDir = path.join(onnxWasm, 'sherpa'); - const sherpaOut = path.join(assetsDir, 'sherpa'); - if (fs.existsSync(sherpaDir)) { - fs.mkdirSync(sherpaOut, { recursive: true }); - for (const file of fs.readdirSync(sherpaDir)) { - const src = path.join(sherpaDir, file); - fs.copyFileSync(src, path.join(sherpaOut, file)); - const sizeMB = (fs.statSync(src).size / 1_000_000).toFixed(1); - console.log(` โœ“ Copied sherpa/${file} (${sizeMB} MB)`); - } - } - }, - }; -} export default defineConfig({ - plugins: [react(), copyWasmPlugin()], + plugins: [react()], server: { headers: { - // Cross-Origin Isolation โ€” required for SharedArrayBuffer / multi-threaded WASM. - // Without these headers the SDK falls back to single-threaded mode. + // Cross-Origin Isolation โ€” required for SharedArrayBuffer (MediaPipe) 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'credentialless', }, }, - assetsInclude: ['**/*.wasm'], worker: { format: 'es' }, - optimizeDeps: { - // Exclude WASM-bearing packages from pre-bundling so their - // import.meta.url resolves correctly to node_modules paths - // (needed for automatic WASM file discovery at ../../wasm/). - exclude: ['@runanywhere/web-llamacpp', '@runanywhere/web-onnx'], - }, }); From 0689264fa528ee5b93099a1996e134c194e5d46e Mon Sep 17 00:00:00 2001 From: proCreator_01 <82655626+AaryakCreator@users.noreply.github.com> Date: Mon, 23 Mar 2026 00:02:50 +0530 Subject: [PATCH 04/14] Added Exercise Demo component, icon and PWA setup Add a new ExerciseDemo React component with styles (src/components/ExerciseDemo.tsx, src/styles/exercise-demo.css) and minor style tweaks in src/styles/index.css. Add public/icon.svg and update index.html to use the new favicon, include apple-touch-icon and mobile/PWA meta tags (apple status bar style and description). Add vite-plugin-pwa to package.json and wire up PWA support in vite.config.ts. Also include a small change in FitnessTab.tsx. --- index.html | 6 +- package-lock.json | 6490 ++++++++++++++++++++++++++----- package.json | 3 +- public/icon.svg | 5 + src/components/ExerciseDemo.tsx | 32 + src/components/FitnessTab.tsx | 86 +- src/styles/exercise-demo.css | 119 + src/styles/index.css | 32 +- vite.config.ts | 29 +- 9 files changed, 5737 insertions(+), 1065 deletions(-) create mode 100644 public/icon.svg create mode 100644 src/components/ExerciseDemo.tsx create mode 100644 src/styles/exercise-demo.css diff --git a/index.html b/index.html index 22c83192..86f3f96f 100644 --- a/index.html +++ b/index.html @@ -5,8 +5,12 @@ + + + Kine-Sight AI Fitness Trainer - + +
diff --git a/package-lock.json b/package-lock.json index 5e213404..306c5279 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,26 @@ "@types/react-dom": "^19.0.0", "@vitejs/plugin-react": "^4.3.0", "typescript": "^5.6.0", - "vite": "^6.0.0" + "vite": "^6.0.0", + "vite-plugin-pwa": "^1.2.0" + } + }, + "node_modules/@apideck/better-ajv-errors": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-schema": "^0.4.0", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "ajv": ">=8" } }, "node_modules/@babel/code-frame": { @@ -93,6 +112,19 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-compilation-targets": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", @@ -110,6 +142,63 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", + "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", + "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "regexpu-core": "^6.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", + "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "debug": "^4.4.3", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.11" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@babel/helper-globals": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", @@ -120,6 +209,20 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-module-imports": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", @@ -152,6 +255,19 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-plugin-utils": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", @@ -162,6 +278,56 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", + "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -192,6 +358,21 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", + "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helpers": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", @@ -222,26 +403,27 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", + "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-react-jsx-source": { + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", "dev": true, "license": "MIT", "dependencies": { @@ -251,1463 +433,5205 @@ "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" } }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", + "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "aix" - ], "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", + "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", + "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.29.0" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", + "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-remap-async-to-generator": "^7.27.1" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" - ], + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", + "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", + "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", + "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], + "node_modules/@babel/plugin-transform-classes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", + "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/traverse": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", + "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/template": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", + "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", + "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=18" - } + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", + "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", + "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", + "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], + "node_modules/@babel/plugin-transform-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", + "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", + "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz", + "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@mediapipe/tasks-vision": { - "version": "0.10.33", - "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.33.tgz", - "integrity": "sha512-Hh41r6ezzz6aeT1e3aJHjLaeVKtz7yhHLjVMQpoxg2fEgA8HRByS2KNMU28ahtDjJRJNCyDHWb2tJWVAyshyMw==", - "license": "Apache-2.0" - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.27", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", - "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.58.0.tgz", - "integrity": "sha512-mr0tmS/4FoVk1cnaeN244A/wjvGDNItZKR8hRhnmCzygyRXYtKF5jVDSIILR1U97CTzAYmbgIj/Dukg62ggG5w==", - "cpu": [ - "arm" - ], + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", + "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", + "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", + "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", + "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", + "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", + "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", + "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", + "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", + "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", + "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", + "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", + "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.2.tgz", + "integrity": "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.28.6", + "@babel/plugin-syntax-import-attributes": "^7.28.6", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.29.0", + "@babel/plugin-transform-async-to-generator": "^7.28.6", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.6", + "@babel/plugin-transform-class-properties": "^7.28.6", + "@babel/plugin-transform-class-static-block": "^7.28.6", + "@babel/plugin-transform-classes": "^7.28.6", + "@babel/plugin-transform-computed-properties": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-dotall-regex": "^7.28.6", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.6", + "@babel/plugin-transform-exponentiation-operator": "^7.28.6", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.28.6", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.28.6", + "@babel/plugin-transform-modules-systemjs": "^7.29.0", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", + "@babel/plugin-transform-numeric-separator": "^7.28.6", + "@babel/plugin-transform-object-rest-spread": "^7.28.6", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.28.6", + "@babel/plugin-transform-optional-chaining": "^7.28.6", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.28.6", + "@babel/plugin-transform-private-property-in-object": "^7.28.6", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.29.0", + "@babel/plugin-transform-regexp-modifiers": "^7.28.6", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.28.6", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.28.6", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.15", + "babel-plugin-polyfill-corejs3": "^0.14.0", + "babel-plugin-polyfill-regenerator": "^0.6.6", + "core-js-compat": "^3.48.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mediapipe/tasks-vision": { + "version": "0.10.33", + "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.33.tgz", + "integrity": "sha512-Hh41r6ezzz6aeT1e3aJHjLaeVKtz7yhHLjVMQpoxg2fEgA8HRByS2KNMU28ahtDjJRJNCyDHWb2tJWVAyshyMw==", + "license": "Apache-2.0" + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz", + "integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-terser": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", + "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.58.0.tgz", + "integrity": "sha512-mr0tmS/4FoVk1cnaeN244A/wjvGDNItZKR8hRhnmCzygyRXYtKF5jVDSIILR1U97CTzAYmbgIj/Dukg62ggG5w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.58.0.tgz", + "integrity": "sha512-+s++dbp+/RTte62mQD9wLSbiMTV+xr/PeRJEc/sFZFSBRlHPNPVaf5FXlzAL77Mr8FtSfQqCN+I598M8U41ccQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.58.0.tgz", + "integrity": "sha512-MFWBwTcYs0jZbINQBXHfSrpSQJq3IUOakcKPzfeSznONop14Pxuqa0Kg19GD0rNBMPQI2tFtu3UzapZpH0Uc1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.58.0.tgz", + "integrity": "sha512-yiKJY7pj9c9JwzuKYLFaDZw5gma3fI9bkPEIyofvVfsPqjCWPglSHdpdwXpKGvDeYDms3Qal8qGMEHZ1M/4Udg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.58.0.tgz", + "integrity": "sha512-x97kCoBh5MOevpn/CNK9W1x8BEzO238541BGWBc315uOlN0AD/ifZ1msg+ZQB05Ux+VF6EcYqpiagfLJ8U3LvQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.58.0.tgz", + "integrity": "sha512-Aa8jPoZ6IQAG2eIrcXPpjRcMjROMFxCt1UYPZZtCxRV68WkuSigYtQ/7Zwrcr2IvtNJo7T2JfDXyMLxq5L4Jlg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.58.0.tgz", + "integrity": "sha512-Ob8YgT5kD/lSIYW2Rcngs5kNB/44Q2RzBSPz9brf2WEtcGR7/f/E9HeHn1wYaAwKBni+bdXEwgHvUd0x12lQSA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.58.0.tgz", + "integrity": "sha512-K+RI5oP1ceqoadvNt1FecL17Qtw/n9BgRSzxif3rTL2QlIu88ccvY+Y9nnHe/cmT5zbH9+bpiJuG1mGHRVwF4Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.58.0.tgz", + "integrity": "sha512-T+17JAsCKUjmbopcKepJjHWHXSjeW7O5PL7lEFaeQmiVyw4kkc5/lyYKzrv6ElWRX/MrEWfPiJWqbTvfIvjM1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.58.0.tgz", + "integrity": "sha512-cCePktb9+6R9itIJdeCFF9txPU7pQeEHB5AbHu/MKsfH/k70ZtOeq1k4YAtBv9Z7mmKI5/wOLYjQ+B9QdxR6LA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.58.0.tgz", + "integrity": "sha512-iekUaLkfliAsDl4/xSdoCJ1gnnIXvoNz85C8U8+ZxknM5pBStfZjeXgB8lXobDQvvPRCN8FPmmuTtH+z95HTmg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.58.0.tgz", + "integrity": "sha512-68ofRgJNl/jYJbxFjCKE7IwhbfxOl1muPN4KbIqAIe32lm22KmU7E8OPvyy68HTNkI2iV/c8y2kSPSm2mW/Q9Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.58.0.tgz", + "integrity": "sha512-dpz8vT0i+JqUKuSNPCP5SYyIV2Lh0sNL1+FhM7eLC457d5B9/BC3kDPp5BBftMmTNsBarcPcoz5UGSsnCiw4XQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.58.0.tgz", + "integrity": "sha512-4gdkkf9UJ7tafnweBCR/mk4jf3Jfl0cKX9Np80t5i78kjIH0ZdezUv/JDI2VtruE5lunfACqftJ8dIMGN4oHew==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.58.0.tgz", + "integrity": "sha512-YFS4vPnOkDTD/JriUeeZurFYoJhPf9GQQEF/v4lltp3mVcBmnsAdjEWhr2cjUCZzZNzxCG0HZOvJU44UGHSdzw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.58.0.tgz", + "integrity": "sha512-x2xgZlFne+QVNKV8b4wwaCS8pwq3y14zedZ5DqLzjdRITvreBk//4Knbcvm7+lWmms9V9qFp60MtUd0/t/PXPw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.58.0.tgz", + "integrity": "sha512-jIhrujyn4UnWF8S+DHSkAkDEO3hLX0cjzxJZPLF80xFyzyUIYgSMRcYQ3+uqEoyDD2beGq7Dj7edi8OnJcS/hg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.58.0.tgz", + "integrity": "sha512-+410Srdoh78MKSJxTQ+hZ/Mx+ajd6RjjPwBPNd0R3J9FtL6ZA0GqiiyNjCO9In0IzZkCNrpGymSfn+kgyPQocg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.58.0.tgz", + "integrity": "sha512-ZjMyby5SICi227y1MTR3VYBpFTdZs823Rs/hpakufleBoufoOIB6jtm9FEoxn/cgO7l6PM2rCEl5Kre5vX0QrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.58.0.tgz", + "integrity": "sha512-ds4iwfYkSQ0k1nb8LTcyXw//ToHOnNTJtceySpL3fa7tc/AsE+UpUFphW126A6fKBGJD5dhRvg8zw1rvoGFxmw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.58.0.tgz", + "integrity": "sha512-fd/zpJniln4ICdPkjWFhZYeY/bpnaN9pGa6ko+5WD38I0tTqk9lXMgXZg09MNdhpARngmxiCg0B0XUamNw/5BQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.58.0.tgz", + "integrity": "sha512-YpG8dUOip7DCz3nr/JUfPbIUo+2d/dy++5bFzgi4ugOGBIox+qMbbqt/JoORwvI/C9Kn2tz6+Bieoqd5+B1CjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.58.0.tgz", + "integrity": "sha512-b9DI8jpFQVh4hIXFr0/+N/TzLdpBIoPzjt0Rt4xJbW3mzguV3mduR9cNgiuFcuL/TeORejJhCWiAXe3E/6PxWA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.58.0.tgz", + "integrity": "sha512-CSrVpmoRJFN06LL9xhkitkwUcTZtIotYAF5p6XOR2zW0Zz5mzb3IPpcoPhB02frzMHFNo1reQ9xSF5fFm3hUsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.58.0.tgz", + "integrity": "sha512-QFsBgQNTnh5K0t/sBsjJLq24YVqEIVkGpfN2VHsnN90soZyhaiA9UUHufcctVNL4ypJY0wrwad0wslx2KJQ1/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@surma/rollup-plugin-off-main-thread": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", + "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "ejs": "^3.1.6", + "json5": "^2.2.0", + "magic-string": "^0.25.0", + "string.prototype.matchall": "^4.0.6" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", + "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", + "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8", + "core-js-compat": "^3.48.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", + "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", + "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001770", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz", + "integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-js-compat": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", + "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.302", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz", + "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/es-abstract": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true, + "license": "ISC" + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.58.0.tgz", - "integrity": "sha512-+s++dbp+/RTte62mQD9wLSbiMTV+xr/PeRJEc/sFZFSBRlHPNPVaf5FXlzAL77Mr8FtSfQqCN+I598M8U41ccQ==", - "cpu": [ - "arm64" + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.58.0.tgz", - "integrity": "sha512-MFWBwTcYs0jZbINQBXHfSrpSQJq3IUOakcKPzfeSznONop14Pxuqa0Kg19GD0rNBMPQI2tFtu3UzapZpH0Uc1Q==", - "cpu": [ - "arm64" - ], + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "engines": { + "node": ">= 0.4" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.58.0.tgz", - "integrity": "sha512-yiKJY7pj9c9JwzuKYLFaDZw5gma3fI9bkPEIyofvVfsPqjCWPglSHdpdwXpKGvDeYDms3Qal8qGMEHZ1M/4Udg==", - "cpu": [ - "x64" - ], + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.58.0.tgz", - "integrity": "sha512-x97kCoBh5MOevpn/CNK9W1x8BEzO238541BGWBc315uOlN0AD/ifZ1msg+ZQB05Ux+VF6EcYqpiagfLJ8U3LvQ==", - "cpu": [ - "arm64" - ], + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.58.0.tgz", - "integrity": "sha512-Aa8jPoZ6IQAG2eIrcXPpjRcMjROMFxCt1UYPZZtCxRV68WkuSigYtQ/7Zwrcr2IvtNJo7T2JfDXyMLxq5L4Jlg==", - "cpu": [ - "x64" - ], + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "engines": { + "node": ">=8" + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.58.0.tgz", - "integrity": "sha512-Ob8YgT5kD/lSIYW2Rcngs5kNB/44Q2RzBSPz9brf2WEtcGR7/f/E9HeHn1wYaAwKBni+bdXEwgHvUd0x12lQSA==", - "cpu": [ - "arm" - ], + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.58.0.tgz", - "integrity": "sha512-K+RI5oP1ceqoadvNt1FecL17Qtw/n9BgRSzxif3rTL2QlIu88ccvY+Y9nnHe/cmT5zbH9+bpiJuG1mGHRVwF4Q==", - "cpu": [ - "arm" - ], + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">= 0.4" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.58.0.tgz", - "integrity": "sha512-T+17JAsCKUjmbopcKepJjHWHXSjeW7O5PL7lEFaeQmiVyw4kkc5/lyYKzrv6ElWRX/MrEWfPiJWqbTvfIvjM1Q==", - "cpu": [ - "arm64" + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/pretty-bytes": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", + "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.58.0.tgz", - "integrity": "sha512-cCePktb9+6R9itIJdeCFF9txPU7pQeEHB5AbHu/MKsfH/k70ZtOeq1k4YAtBv9Z7mmKI5/wOLYjQ+B9QdxR6LA==", - "cpu": [ - "arm64" - ], + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">=6" + } }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.58.0.tgz", - "integrity": "sha512-iekUaLkfliAsDl4/xSdoCJ1gnnIXvoNz85C8U8+ZxknM5pBStfZjeXgB8lXobDQvvPRCN8FPmmuTtH+z95HTmg==", - "cpu": [ - "loong64" - ], + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "safe-buffer": "^5.1.0" + } }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.58.0.tgz", - "integrity": "sha512-68ofRgJNl/jYJbxFjCKE7IwhbfxOl1muPN4KbIqAIe32lm22KmU7E8OPvyy68HTNkI2iV/c8y2kSPSm2mW/Q9Q==", - "cpu": [ - "loong64" - ], + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.58.0.tgz", - "integrity": "sha512-dpz8vT0i+JqUKuSNPCP5SYyIV2Lh0sNL1+FhM7eLC457d5B9/BC3kDPp5BBftMmTNsBarcPcoz5UGSsnCiw4XQ==", - "cpu": [ - "ppc64" - ], + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.58.0.tgz", - "integrity": "sha512-4gdkkf9UJ7tafnweBCR/mk4jf3Jfl0cKX9Np80t5i78kjIH0ZdezUv/JDI2VtruE5lunfACqftJ8dIMGN4oHew==", - "cpu": [ - "ppc64" - ], + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "license": "MIT" }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.58.0.tgz", - "integrity": "sha512-YFS4vPnOkDTD/JriUeeZurFYoJhPf9GQQEF/v4lltp3mVcBmnsAdjEWhr2cjUCZzZNzxCG0HZOvJU44UGHSdzw==", - "cpu": [ - "riscv64" - ], + "node_modules/regenerate-unicode-properties": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.58.0.tgz", - "integrity": "sha512-x2xgZlFne+QVNKV8b4wwaCS8pwq3y14zedZ5DqLzjdRITvreBk//4Knbcvm7+lWmms9V9qFp60MtUd0/t/PXPw==", - "cpu": [ - "riscv64" - ], + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.58.0.tgz", - "integrity": "sha512-jIhrujyn4UnWF8S+DHSkAkDEO3hLX0cjzxJZPLF80xFyzyUIYgSMRcYQ3+uqEoyDD2beGq7Dj7edi8OnJcS/hg==", - "cpu": [ - "s390x" - ], + "node_modules/regexpu-core": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.2", + "regjsgen": "^0.8.0", + "regjsparser": "^0.13.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.2.1" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.58.0.tgz", - "integrity": "sha512-+410Srdoh78MKSJxTQ+hZ/Mx+ajd6RjjPwBPNd0R3J9FtL6ZA0GqiiyNjCO9In0IzZkCNrpGymSfn+kgyPQocg==", - "cpu": [ - "x64" - ], + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz", + "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.1.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.58.0.tgz", - "integrity": "sha512-ZjMyby5SICi227y1MTR3VYBpFTdZs823Rs/hpakufleBoufoOIB6jtm9FEoxn/cgO7l6PM2rCEl5Kre5vX0QrQ==", - "cpu": [ - "x64" - ], + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@rollup/rollup-openbsd-x64": { + "node_modules/rollup": { "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.58.0.tgz", - "integrity": "sha512-ds4iwfYkSQ0k1nb8LTcyXw//ToHOnNTJtceySpL3fa7tc/AsE+UpUFphW126A6fKBGJD5dhRvg8zw1rvoGFxmw==", - "cpu": [ - "x64" - ], + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.58.0.tgz", + "integrity": "sha512-wbT0mBmWbIvvq8NeEYWWvevvxnOyhKChir47S66WCxw1SXqhw7ssIYejnQEVt7XYQpsj2y8F9PM+Cr3SNEa0gw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.58.0", + "@rollup/rollup-android-arm64": "4.58.0", + "@rollup/rollup-darwin-arm64": "4.58.0", + "@rollup/rollup-darwin-x64": "4.58.0", + "@rollup/rollup-freebsd-arm64": "4.58.0", + "@rollup/rollup-freebsd-x64": "4.58.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.58.0", + "@rollup/rollup-linux-arm-musleabihf": "4.58.0", + "@rollup/rollup-linux-arm64-gnu": "4.58.0", + "@rollup/rollup-linux-arm64-musl": "4.58.0", + "@rollup/rollup-linux-loong64-gnu": "4.58.0", + "@rollup/rollup-linux-loong64-musl": "4.58.0", + "@rollup/rollup-linux-ppc64-gnu": "4.58.0", + "@rollup/rollup-linux-ppc64-musl": "4.58.0", + "@rollup/rollup-linux-riscv64-gnu": "4.58.0", + "@rollup/rollup-linux-riscv64-musl": "4.58.0", + "@rollup/rollup-linux-s390x-gnu": "4.58.0", + "@rollup/rollup-linux-x64-gnu": "4.58.0", + "@rollup/rollup-linux-x64-musl": "4.58.0", + "@rollup/rollup-openbsd-x64": "4.58.0", + "@rollup/rollup-openharmony-arm64": "4.58.0", + "@rollup/rollup-win32-arm64-msvc": "4.58.0", + "@rollup/rollup-win32-ia32-msvc": "4.58.0", + "@rollup/rollup-win32-x64-gnu": "4.58.0", + "@rollup/rollup-win32-x64-msvc": "4.58.0", + "fsevents": "~2.3.2" + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.58.0.tgz", - "integrity": "sha512-fd/zpJniln4ICdPkjWFhZYeY/bpnaN9pGa6ko+5WD38I0tTqk9lXMgXZg09MNdhpARngmxiCg0B0XUamNw/5BQ==", - "cpu": [ - "arm64" - ], + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.58.0.tgz", - "integrity": "sha512-YpG8dUOip7DCz3nr/JUfPbIUo+2d/dy++5bFzgi4ugOGBIox+qMbbqt/JoORwvI/C9Kn2tz6+Bieoqd5+B1CjA==", - "cpu": [ - "arm64" + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.58.0.tgz", - "integrity": "sha512-b9DI8jpFQVh4hIXFr0/+N/TzLdpBIoPzjt0Rt4xJbW3mzguV3mduR9cNgiuFcuL/TeORejJhCWiAXe3E/6PxWA==", - "cpu": [ - "ia32" - ], + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.58.0.tgz", - "integrity": "sha512-CSrVpmoRJFN06LL9xhkitkwUcTZtIotYAF5p6XOR2zW0Zz5mzb3IPpcoPhB02frzMHFNo1reQ9xSF5fFm3hUsQ==", - "cpu": [ - "x64" - ], + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.58.0.tgz", - "integrity": "sha512-QFsBgQNTnh5K0t/sBsjJLq24YVqEIVkGpfN2VHsnN90soZyhaiA9UUHufcctVNL4ypJY0wrwad0wslx2KJQ1/w==", - "cpu": [ - "x64" - ], + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.0.0" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.2" + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/@types/react": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", - "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "license": "MIT", "dependencies": { - "csstype": "^3.2.2" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "dev": true, "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@vitejs/plugin-react": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", - "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.28.0", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-beta.27", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.17.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": ">= 0.4" }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", - "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { - "node": ">=6.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/smob": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.6.1.tgz", + "integrity": "sha512-KAkBqZl3c2GvNgNhcoyJae1aKldDW0LO279wF9bk1PnluRTETKBq0WyzRXxEhoQLk56yHaOY4JCBEKDuJIET5g==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "deprecated": "The work that was done in this beta branch won't be included in future versions", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" + "whatwg-url": "^7.0.0" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": ">= 8" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001770", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz", - "integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==", + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", "dev": true, "license": "MIT" }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 0.4" } }, - "node_modules/electron-to-chromium": { - "version": "1.5.302", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz", - "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, "engines": { - "node": ">=12.0.0" + "node": ">= 0.4" }, - "peerDependencies": { - "picomatch": "^3 || ^4" + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "engines": { + "node": ">=4" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=10" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "node_modules/tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", "dev": true, "license": "MIT", + "dependencies": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", + "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, "bin": { - "jsesc": "bin/jsesc" + "terser": "bin/terser" }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", - "bin": { - "json5": "lib/cli.js" + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": ">=6" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "yallist": "^3.0.2" + "punycode": "^2.1.0" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", "dev": true, - "license": "MIT" + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">= 0.4" } }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/react": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", - "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", - "license": "MIT", + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, "engines": { - "node": ">=0.10.0" + "node": ">=14.17" } }, - "node_modules/react-dom": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", - "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, "license": "MIT", "dependencies": { - "scheduler": "^0.27.0" + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" }, - "peerDependencies": { - "react": "^19.2.4" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/react-refresh": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", - "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/rollup": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.58.0.tgz", - "integrity": "sha512-wbT0mBmWbIvvq8NeEYWWvevvxnOyhKChir47S66WCxw1SXqhw7ssIYejnQEVt7XYQpsj2y8F9PM+Cr3SNEa0gw==", + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.58.0", - "@rollup/rollup-android-arm64": "4.58.0", - "@rollup/rollup-darwin-arm64": "4.58.0", - "@rollup/rollup-darwin-x64": "4.58.0", - "@rollup/rollup-freebsd-arm64": "4.58.0", - "@rollup/rollup-freebsd-x64": "4.58.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.58.0", - "@rollup/rollup-linux-arm-musleabihf": "4.58.0", - "@rollup/rollup-linux-arm64-gnu": "4.58.0", - "@rollup/rollup-linux-arm64-musl": "4.58.0", - "@rollup/rollup-linux-loong64-gnu": "4.58.0", - "@rollup/rollup-linux-loong64-musl": "4.58.0", - "@rollup/rollup-linux-ppc64-gnu": "4.58.0", - "@rollup/rollup-linux-ppc64-musl": "4.58.0", - "@rollup/rollup-linux-riscv64-gnu": "4.58.0", - "@rollup/rollup-linux-riscv64-musl": "4.58.0", - "@rollup/rollup-linux-s390x-gnu": "4.58.0", - "@rollup/rollup-linux-x64-gnu": "4.58.0", - "@rollup/rollup-linux-x64-musl": "4.58.0", - "@rollup/rollup-openbsd-x64": "4.58.0", - "@rollup/rollup-openharmony-arm64": "4.58.0", - "@rollup/rollup-win32-arm64-msvc": "4.58.0", - "@rollup/rollup-win32-ia32-msvc": "4.58.0", - "@rollup/rollup-win32-x64-gnu": "4.58.0", - "@rollup/rollup-win32-x64-msvc": "4.58.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "crypto-random-string": "^2.0.0" }, "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "node": ">=8" } }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, + "license": "MIT", "engines": { - "node": ">=14.17" + "node": ">= 10.0.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4", + "yarn": "*" } }, "node_modules/update-browserslist-db": { @@ -1816,6 +5740,480 @@ } } }, + "node_modules/vite-plugin-pwa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-1.2.0.tgz", + "integrity": "sha512-a2xld+SJshT9Lgcv8Ji4+srFJL4k/1bVbd1x06JIkvecpQkwkvCncD1+gSzcdm3s+owWLpMJerG3aN5jupJEVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.6", + "pretty-bytes": "^6.1.1", + "tinyglobby": "^0.2.10", + "workbox-build": "^7.4.0", + "workbox-window": "^7.4.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vite-pwa/assets-generator": "^1.0.0", + "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "workbox-build": "^7.4.0", + "workbox-window": "^7.4.0" + }, + "peerDependenciesMeta": { + "@vite-pwa/assets-generator": { + "optional": true + } + } + }, + "node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/workbox-background-sync": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-7.4.0.tgz", + "integrity": "sha512-8CB9OxKAgKZKyNMwfGZ1XESx89GryWTfI+V5yEj8sHjFH8MFelUwYXEyldEK6M6oKMmn807GoJFUEA1sC4XS9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "7.4.0" + } + }, + "node_modules/workbox-broadcast-update": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-7.4.0.tgz", + "integrity": "sha512-+eZQwoktlvo62cI0b+QBr40v5XjighxPq3Fzo9AWMiAosmpG5gxRHgTbGGhaJv/q/MFVxwFNGh/UwHZ/8K88lA==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.0" + } + }, + "node_modules/workbox-build": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-7.4.0.tgz", + "integrity": "sha512-Ntk1pWb0caOFIvwz/hfgrov/OJ45wPEhI5PbTywQcYjyZiVhT3UrwwUPl6TRYbTm4moaFYithYnl1lvZ8UjxcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.24.4", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^5.2.0", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-replace": "^2.4.1", + "@rollup/plugin-terser": "^0.4.3", + "@surma/rollup-plugin-off-main-thread": "^2.2.3", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^11.0.1", + "lodash": "^4.17.20", + "pretty-bytes": "^5.3.0", + "rollup": "^2.79.2", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "7.4.0", + "workbox-broadcast-update": "7.4.0", + "workbox-cacheable-response": "7.4.0", + "workbox-core": "7.4.0", + "workbox-expiration": "7.4.0", + "workbox-google-analytics": "7.4.0", + "workbox-navigation-preload": "7.4.0", + "workbox-precaching": "7.4.0", + "workbox-range-requests": "7.4.0", + "workbox-recipes": "7.4.0", + "workbox-routing": "7.4.0", + "workbox-strategies": "7.4.0", + "workbox-streams": "7.4.0", + "workbox-sw": "7.4.0", + "workbox-window": "7.4.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/workbox-build/node_modules/@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + } + } + }, + "node_modules/workbox-build/node_modules/@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" + } + }, + "node_modules/workbox-build/node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/workbox-build/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/workbox-build/node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true, + "license": "MIT" + }, + "node_modules/workbox-build/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/workbox-build/node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/workbox-build/node_modules/rollup": { + "version": "2.80.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz", + "integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==", + "dev": true, + "license": "MIT", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/workbox-cacheable-response": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-7.4.0.tgz", + "integrity": "sha512-0Fb8795zg/x23ISFkAc7lbWes6vbw34DGFIMw31cwuHPgDEC/5EYm6m/ZkylLX0EnEbbOyOCLjKgFS/Z5g0HeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.0" + } + }, + "node_modules/workbox-core": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-7.4.0.tgz", + "integrity": "sha512-6BMfd8tYEnN4baG4emG9U0hdXM4gGuDU3ectXuVHnj71vwxTFI7WOpQJC4siTOlVtGqCUtj0ZQNsrvi6kZZTAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/workbox-expiration": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-7.4.0.tgz", + "integrity": "sha512-V50p4BxYhtA80eOvulu8xVfPBgZbkxJ1Jr8UUn0rvqjGhLDqKNtfrDfjJKnLz2U8fO2xGQJTx/SKXNTzHOjnHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "7.4.0" + } + }, + "node_modules/workbox-google-analytics": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-7.4.0.tgz", + "integrity": "sha512-MVPXQslRF6YHkzGoFw1A4GIB8GrKym/A5+jYDUSL+AeJw4ytQGrozYdiZqUW1TPQHW8isBCBtyFJergUXyNoWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-background-sync": "7.4.0", + "workbox-core": "7.4.0", + "workbox-routing": "7.4.0", + "workbox-strategies": "7.4.0" + } + }, + "node_modules/workbox-navigation-preload": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-7.4.0.tgz", + "integrity": "sha512-etzftSgdQfjMcfPgbfaZCfM2QuR1P+4o8uCA2s4rf3chtKTq/Om7g/qvEOcZkG6v7JZOSOxVYQiOu6PbAZgU6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.0" + } + }, + "node_modules/workbox-precaching": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-7.4.0.tgz", + "integrity": "sha512-VQs37T6jDqf1rTxUJZXRl3yjZMf5JX/vDPhmx2CPgDDKXATzEoqyRqhYnRoxl6Kr0rqaQlp32i9rtG5zTzIlNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.0", + "workbox-routing": "7.4.0", + "workbox-strategies": "7.4.0" + } + }, + "node_modules/workbox-range-requests": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-7.4.0.tgz", + "integrity": "sha512-3Vq854ZNuP6Y0KZOQWLaLC9FfM7ZaE+iuQl4VhADXybwzr4z/sMmnLgTeUZLq5PaDlcJBxYXQ3U91V7dwAIfvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.0" + } + }, + "node_modules/workbox-recipes": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-7.4.0.tgz", + "integrity": "sha512-kOkWvsAn4H8GvAkwfJTbwINdv4voFoiE9hbezgB1sb/0NLyTG4rE7l6LvS8lLk5QIRIto+DjXLuAuG3Vmt3cxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-cacheable-response": "7.4.0", + "workbox-core": "7.4.0", + "workbox-expiration": "7.4.0", + "workbox-precaching": "7.4.0", + "workbox-routing": "7.4.0", + "workbox-strategies": "7.4.0" + } + }, + "node_modules/workbox-routing": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-7.4.0.tgz", + "integrity": "sha512-C/ooj5uBWYAhAqwmU8HYQJdOjjDKBp9MzTQ+otpMmd+q0eF59K+NuXUek34wbL0RFrIXe/KKT+tUWcZcBqxbHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.0" + } + }, + "node_modules/workbox-strategies": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-7.4.0.tgz", + "integrity": "sha512-T4hVqIi5A4mHi92+5EppMX3cLaVywDp8nsyUgJhOZxcfSV/eQofcOA6/EMo5rnTNmNTpw0rUgjAI6LaVullPpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.0" + } + }, + "node_modules/workbox-streams": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-7.4.0.tgz", + "integrity": "sha512-QHPBQrey7hQbnTs5GrEVoWz7RhHJXnPT+12qqWM378orDMo5VMJLCkCM1cnCk+8Eq92lccx/VgRZ7WAzZWbSLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.0", + "workbox-routing": "7.4.0" + } + }, + "node_modules/workbox-sw": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-7.4.0.tgz", + "integrity": "sha512-ltU+Kr3qWR6BtbdlMnCjobZKzeV1hN+S6UvDywBrwM19TTyqA03X66dzw1tEIdJvQ4lYKkBFox6IAEhoSEZ8Xw==", + "dev": true, + "license": "MIT" + }, + "node_modules/workbox-window": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-7.4.0.tgz", + "integrity": "sha512-/bIYdBLAVsNR3v7gYGaV4pQW3M3kEPx5E8vDxGvxo6khTrGtSSCS7QiFKv9ogzBgZiy0OXLP9zO28U/1nF1mfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "7.4.0" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index f25cffc2..0732e70c 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@types/react-dom": "^19.0.0", "@vitejs/plugin-react": "^4.3.0", "typescript": "^5.6.0", - "vite": "^6.0.0" + "vite": "^6.0.0", + "vite-plugin-pwa": "^1.2.0" } } diff --git a/public/icon.svg b/public/icon.svg new file mode 100644 index 00000000..6a4513ad --- /dev/null +++ b/public/icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/ExerciseDemo.tsx b/src/components/ExerciseDemo.tsx new file mode 100644 index 00000000..5090a11e --- /dev/null +++ b/src/components/ExerciseDemo.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import '../styles/exercise-demo.css'; + +interface Props { + exerciseId: string; +} + +export function ExerciseDemo({ exerciseId }: Props) { + return ( +
+ + {/* Animated Background Ring */} + + + + {/* Head */} + + + {/* Body */} + + + {/* Arms */} + + + + {/* Legs */} + + + +
+ ); +} diff --git a/src/components/FitnessTab.tsx b/src/components/FitnessTab.tsx index 0e5989ef..0bfea472 100644 --- a/src/components/FitnessTab.tsx +++ b/src/components/FitnessTab.tsx @@ -1,13 +1,46 @@ import { useState, useRef, useEffect, useCallback } from 'react'; -import { - EXERCISES, - getPoseLandmarker, - drawSkeleton, - drawAngleBadge, - LM, -} from '../fitness/poseEngine'; +import { EXERCISES, getPoseLandmarker, drawSkeleton, drawAngleBadge, LM } from '../fitness/poseEngine'; import type { ExerciseDef, PoseAnalysis, FormQuality, ExercisePosition } from '../fitness/poseEngine'; import type { NormalizedLandmark } from '@mediapipe/tasks-vision'; +import { ExerciseDemo } from './ExerciseDemo'; + +// --------------------------------------------------------------------------- +// Audio Feedback (Global context to bypass browser autoplay rules) +// --------------------------------------------------------------------------- +let audioCtx: AudioContext | null = null; + +function initAudioContext() { + if (!audioCtx) { + const AudioContextClass = window.AudioContext || (window as any).webkitAudioContext; + if (AudioContextClass) audioCtx = new AudioContextClass(); + } + if (audioCtx?.state === 'suspended') { + audioCtx.resume(); + } +} + +function playCorrectBeep() { + if (!audioCtx) return; + try { + const osc = audioCtx.createOscillator(); + const gain = audioCtx.createGain(); + + osc.type = 'sine'; + osc.frequency.setValueAtTime(880, audioCtx.currentTime); + osc.frequency.exponentialRampToValueAtTime(1760, audioCtx.currentTime + 0.1); + + gain.gain.setValueAtTime(0.1, audioCtx.currentTime); + gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.1); + + osc.connect(gain); + gain.connect(audioCtx.destination); + osc.start(); + osc.stop(audioCtx.currentTime + 0.1); + } catch (err) { + console.warn('Audio beep failed', err); + } +} + // --------------------------------------------------------------------------- // Constants @@ -88,6 +121,9 @@ export function FitnessTab() { const plankHoldStartRef = useRef(0); const plankBestRef = useRef(0); + // Countdown masking ref + const isCountdownRef = useRef(false); + // Keep refs in sync phaseRef.current = phase; isWorkoutRef.current = isWorkout; @@ -229,6 +265,12 @@ export function FitnessTab() { formDuringRepRef.current = false; } + // Skip rep counting and time tracking if we are still counting down + if (isCountdownRef.current) { + rafRef.current = requestAnimationFrame(detect); + return; + } + // ---- Plank: track hold time instead of reps ---- if (exercise.isHold) { if (analysis.form === 'good') { @@ -280,6 +322,8 @@ export function FitnessTab() { phaseRef.current = 'up'; const isCorrect = formDuringRepRef.current; + if (isCorrect) playCorrectBeep(); + const newStats: SessionStats = { ...statsRef.current, correctReps: statsRef.current.correctReps + (isCorrect ? 1 : 0), @@ -357,6 +401,9 @@ export function FitnessTab() { // Workout session management // ------------------------------------------------------------------ const startWorkout = useCallback(async (exercise: ExerciseDef) => { + // Initialize audio context during user gesture to allow playback + initAudioContext(); + setSelectedExercise(exercise); exerciseRef.current = exercise; @@ -391,7 +438,7 @@ export function FitnessTab() { setPhase('idle'); phaseRef.current = 'idle'; setFormQuality('unknown'); - setFeedback(''); + setFeedback('๐ŸŽฏ Get ready! Pose detection active.'); formDuringRepRef.current = true; const freshStats: SessionStats = { correctReps: 0, @@ -403,17 +450,20 @@ export function FitnessTab() { statsRef.current = freshStats; setElapsed(0); - // Run countdown - await runCountdown(); - - // Now start the actual workout + // Now start the actual workout detection (UI enabled, logic muted by isCountdownRef) setIsWorkout(true); isWorkoutRef.current = true; - setFeedback('๐ŸŽฏ Get moving! Pose detection active.'); + isCountdownRef.current = true; - // Start detection loop + // Start detection loop immediately so user sees themself startDetectionLoop(); + // Run countdown + await runCountdown(); + + isCountdownRef.current = false; + setFeedback('๐ŸŽฏ Get moving!'); + // Start elapsed timer timerRef.current = setInterval(() => { setElapsed(prev => prev + 1); @@ -564,6 +614,14 @@ export function FitnessTab() {
)} + {/* Persistent exercise demonstration side-overlay */} + {isWorkout && ( +
+ +
Form Demo
+
+ )} + {/* Form quality indicator */} {isWorkout && (
diff --git a/src/styles/exercise-demo.css b/src/styles/exercise-demo.css new file mode 100644 index 00000000..4c7ee6de --- /dev/null +++ b/src/styles/exercise-demo.css @@ -0,0 +1,119 @@ +/* Stick Figure Animations for Exercise Demo */ + +.exercise-demo { + display: flex; + justify-content: center; + align-items: center; + color: var(--primary); + /* Use a nice timing function for all exercise motions */ + --motion-ease: cubic-bezier(0.4, 0, 0.2, 1); +} + +.stick-figure { + overflow: visible; + filter: drop-shadow(0 2px 4px rgba(0,0,0,0.5)); +} + +.demo-head, .demo-body, .demo-arm, .demo-leg { + transition: all 0.3s var(--motion-ease); +} + +.demo-bg { + opacity: 0.5; +} + +.demo-ring { + animation: pulse-ring 2s infinite var(--motion-ease); + transform-origin: center; +} + +@keyframes pulse-ring { + 0%, 100% { transform: scale(0.95); opacity: 0.3; } + 50% { transform: scale(1.02); opacity: 0.8; } +} + +/* Base animation duration */ +.demo-squats *, .demo-bicep-curls *, .demo-pushups *, .demo-lunges *, .demo-shoulder-press * { + animation-duration: 2.5s; + animation-timing-function: var(--motion-ease); + animation-iteration-count: infinite; +} + +/* Squats */ +@keyframes squat-body { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(18px); } +} +@keyframes squat-leg-l { + 0%, 100% { transform: rotate(0deg) scaleY(1); } + 50% { transform: rotate(-35deg) scaleY(0.5); } +} +@keyframes squat-leg-r { + 0%, 100% { transform: rotate(0deg) scaleY(1); } + 50% { transform: rotate(35deg) scaleY(0.5); } +} +.demo-squats .demo-head, .demo-squats .demo-body, .demo-squats .demo-arm { animation-name: squat-body; } +.demo-squats .demo-leg-l { animation-name: squat-leg-l; transform-origin: 50px 55px; } +.demo-squats .demo-leg-r { animation-name: squat-leg-r; transform-origin: 50px 55px; } + +/* Bicep Curls */ +@keyframes curl-arm-l { + 0%, 100% { transform: rotate(15deg); } + 50% { transform: rotate(-110deg); } +} +@keyframes curl-arm-r { + 0%, 100% { transform: rotate(-15deg); } + 50% { transform: rotate(110deg); } +} +.demo-bicep-curls .demo-arm-l { animation-name: curl-arm-l; transform-origin: 50px 34px; } +.demo-bicep-curls .demo-arm-r { animation-name: curl-arm-r; transform-origin: 50px 34px; } + +/* Pushups */ +@keyframes pushup-body { + 0%, 100% { transform: translateY(0) rotate(-75deg); } + 50% { transform: translateY(18px) rotate(-75deg); } +} +.demo-pushups .stick-figure { transform: translateY(20px); } +.demo-pushups .demo-head, .demo-pushups .demo-body, .demo-pushups .demo-leg { + animation-name: pushup-body; transform-origin: 50px 85px; +} +.demo-pushups .demo-arm-l, .demo-pushups .demo-arm-r { + transform: rotate(-150deg); transform-origin: 50px 34px; +} + +/* Lunges */ +@keyframes lunge-body { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(15px); } +} +@keyframes lunge-leg-l { + 0%, 100% { transform: rotate(-15deg) scaleY(1); } + 50% { transform: rotate(-45deg) scaleY(0.65); } +} +@keyframes lunge-leg-r { + 0%, 100% { transform: rotate(25deg) scaleY(1); } + 50% { transform: rotate(45deg) scaleY(0.65); } +} +.demo-lunges .demo-head, .demo-lunges .demo-body, .demo-lunges .demo-arm { animation-name: lunge-body; } +.demo-lunges .demo-leg-l { animation-name: lunge-leg-l; transform-origin: 50px 55px; } +.demo-lunges .demo-leg-r { animation-name: lunge-leg-r; transform-origin: 50px 55px; } + +/* Shoulder Press */ +@keyframes press-arm-l { + 0%, 100% { transform: translateY(0) rotate(140deg); } + 50% { transform: translateY(-12px) rotate(175deg); } +} +@keyframes press-arm-r { + 0%, 100% { transform: translateY(0) rotate(-140deg); } + 50% { transform: translateY(-12px) rotate(-175deg); } +} +.demo-shoulder-press .demo-arm-l { animation-name: press-arm-l; transform-origin: 50px 34px; } +.demo-shoulder-press .demo-arm-r { animation-name: press-arm-r; transform-origin: 50px 34px; } + +/* Plank */ +.demo-plank .stick-figure { + transform: translateY(20px) rotate(-80deg); transform-origin: center; +} +.demo-plank .demo-arm-l, .demo-plank .demo-arm-r { + transform: rotate(-100deg); transform-origin: 50px 34px; +} diff --git a/src/styles/index.css b/src/styles/index.css index c1bdbf9d..39955664 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -640,6 +640,34 @@ body { color: white; } +/* Persistent Demo Overlay */ +.demo-overlay-wrapper { + position: absolute; + top: 12px; + left: 12px; + background: rgba(15, 23, 42, 0.75); + backdrop-filter: blur(8px); + border-radius: var(--radius); + border: 1px solid rgba(255, 255, 255, 0.15); + z-index: 10; + display: flex; + flex-direction: column; + align-items: center; + padding: 8px 8px 4px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); + transform: scale(0.9); + transform-origin: top left; +} + +.demo-overlay-label { + font-size: 10px; + font-weight: 700; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 1px; + margin-top: 2px; +} + /* --------------------------------------------------------------------------- * Countdown Timer Overlay * --------------------------------------------------------------------------- */ @@ -654,8 +682,8 @@ body { flex-direction: column; align-items: center; justify-content: center; - background: rgba(15, 23, 42, 0.85); - backdrop-filter: blur(6px); + background: rgba(15, 23, 42, 0.4); + backdrop-filter: blur(4px); z-index: 20; border-radius: var(--radius); } diff --git a/vite.config.ts b/vite.config.ts index 9f0055bc..82ae4af8 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,8 +1,35 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; +import { VitePWA } from 'vite-plugin-pwa'; export default defineConfig({ - plugins: [react()], + plugins: [ + react(), + VitePWA({ + registerType: 'autoUpdate', + includeAssets: ['favicon.ico', 'icon.svg', 'apple-touch-icon.png'], + manifest: { + name: 'Kine-Sight AI Coach', + short_name: 'Kine-Sight', + description: 'AI Fitness Coach powered by MediaPipe pose tracking', + theme_color: '#0F172A', + background_color: '#0F172A', + display: 'standalone', + icons: [ + { + src: 'icon.svg', + sizes: '192x192', + type: 'image/svg+xml' + }, + { + src: 'icon.svg', + sizes: '512x512', + type: 'image/svg+xml' + } + ] + } + }) + ], server: { headers: { // Cross-Origin Isolation โ€” required for SharedArrayBuffer (MediaPipe) From a439f8676c191d688b3e81622b49946f014a2f3e Mon Sep 17 00:00:00 2001 From: Himanshu Ranjan Date: Mon, 23 Mar 2026 23:27:15 +0530 Subject: [PATCH 05/14] Front_end_implemented --- build_error.log | Bin 0 -> 3124 bytes build_error_cmd.log | 16 ++ index.html | 75 ++++++ src/App.tsx | 11 +- src/components/FitnessTab.tsx | 416 +++++++++++++++++++------------ src/fitness/poseEngine.ts | 2 +- src/styles/index.css | 1 - stitch_screens/ai_coach.html | 296 ++++++++++++++++++++++ stitch_screens/dashboard.html | 295 ++++++++++++++++++++++ stitch_screens/home_feed.html | 294 ++++++++++++++++++++++ stitch_screens/live_workout.html | 232 +++++++++++++++++ 11 files changed, 1461 insertions(+), 177 deletions(-) create mode 100644 build_error.log create mode 100644 build_error_cmd.log create mode 100644 stitch_screens/ai_coach.html create mode 100644 stitch_screens/dashboard.html create mode 100644 stitch_screens/home_feed.html create mode 100644 stitch_screens/live_workout.html diff --git a/build_error.log b/build_error.log new file mode 100644 index 0000000000000000000000000000000000000000..a394803cb7b79421833a5355c6c145d1c531d44d GIT binary patch literal 3124 zcmcJRTTc^F6ovP*iNAoCi6(;4mP=7!9*hPd0l6sZ6PTvdX#v~X&_U$IpRRsupPo)@ zr%E*DWZE+``?B`hmoq=U-?cmT%o6)#=hm~#DmGx(wlmA^xh+}MmbsRYIJGO@bnK%= zogCRFZz{Y?>;WsWFUaKB`Bh#zJ^PGyhP}+bx=f1PoE=*YFI{U{!#!k3CGI6hBf;lq z-soAAT@N`SO!&X#zRPaYQaf>t9ouIe+L67p7q-XKO=RSC!2Y#sTyWO}<-j#g>=pJD zd)?l;WyM~{!WB;&TsL+N9~rutYrM6W=oEUDF};%!1Jxl~MII1W-Dik*1KfJ^f@g{_ zAV}dsQJ&%Vl9-lV<`K`t?Y&`~A&NE<4Ub-Swz1FwpQ4OBUvRfLx{Ive!;&=q%~~DL z%8e{leQ778-)_C6aeHY={=J4Yli?D|O}JB?wu$1B4DY~G%Oi?X;~Mc+z50(va5qgf(faqjea*JLI(;8wFtIOj*5U4g?6_}Z?cyK5xp zhjQmc-gkDyx-9-Wr^aWLEdO~<6|<)9SpGbx#%Gj#gxOx`6;mQ~viOekqv}55B(He& zD5mMzR@A9{45(t&K$o}&>@=PFoDR#j##*%%erv2JTpRAA#?uPd5*oTzk!Z4O!CMP! zUDFJc?O4yMvV^Rj@+M62s^=(Ywih{*MrOP8$RQbjPi5?(wcy#-S&(|>l%GqUsnVsf zh(Dbbk%>jn7dCZ12#q?Yj$ZJgb4gZGqI<$hiR@ts;z>*DQCaGEzX+ME#koC=ZW>uQ zi3T8E#QV6l6b_$qrM3?9Wmv|eoJJDnLO3&Tha_aE49U9ZTIbk$DH3s~Q(uhdKDquM zD;r>_!OHRsR`$nozsEjiw(1#fd4^^qEz4cVsj90ie<64A+a}bIG}cBqS4ugP%ze5< zm8q|ik4&`x$KoWuHF{H(rCKkmEMc3?<7AzNew4=F)!HPor}W(t$Z9hxZ8j=R_}sHQ zF0%`&Q_hWW$`up6m?A=6Hc4jZS{jq6YePP7gE}CpGJC?;;wL7WEdFk)O=AhDL+y2) a`7rG?+Z1u=RrSfLPqI1pp)d6K#rhLyw(u$d literal 0 HcmV?d00001 diff --git a/build_error_cmd.log b/build_error_cmd.log new file mode 100644 index 00000000..89f7f35c --- /dev/null +++ b/build_error_cmd.log @@ -0,0 +1,16 @@ + +> kine-sight@0.1.0 build +> tsc -b && vite build + +failed to load config from C:\IMMORTAL\Codes\Projects\Kine_Sight_RunanywhereSDK\Kine-Sight\vite.config.ts +error during build: +Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'vite-plugin-pwa' imported from C:\IMMORTAL\Codes\Projects\Kine_Sight_RunanywhereSDK\Kine-Sight\node_modules\.vite-temp\vite.config.ts.timestamp-1774276316417-b2a6f69e74eaf.mjs + at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9) + at packageResolve (node:internal/modules/esm/resolve:767:81) + at moduleResolve (node:internal/modules/esm/resolve:853:18) + at defaultResolve (node:internal/modules/esm/resolve:983:11) + at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:783:12) + at #cachedDefaultResolve (node:internal/modules/esm/loader:707:25) + at ModuleLoader.resolve (node:internal/modules/esm/loader:690:38) + at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:307:38) + at ModuleJob._link (node:internal/modules/esm/module_job:183:49) diff --git a/index.html b/index.html index 86f3f96f..f92ff957 100644 --- a/index.html +++ b/index.html @@ -11,6 +11,81 @@ Kine-Sight AI Fitness Trainer + + + + + + + +
diff --git a/src/App.tsx b/src/App.tsx index c7c4368c..254acbf5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,15 +2,8 @@ import { FitnessTab } from './components/FitnessTab'; export function App() { return ( -
-
-

Kine-Sight

- AI Coach -
- -
- -
+
+
); } diff --git a/src/components/FitnessTab.tsx b/src/components/FitnessTab.tsx index 0bfea472..065196f3 100644 --- a/src/components/FitnessTab.tsx +++ b/src/components/FitnessTab.tsx @@ -513,40 +513,124 @@ export function FitnessTab() { const isPlank = selectedExercise?.isHold === true; // ------------------------------------------------------------------ - // Render โ€” Exercise Selection + // Render // ------------------------------------------------------------------ if (!selectedExercise) { return ( -
- {/* Pose model loading banner */} - {poseLoading && ( -
- Loading MediaPipe Pose model... +
+ {/* TopAppBar Shell */} +
+
+ KINETIC
- )} - -
-
๐Ÿ‹๏ธโ€โ™‚๏ธ
-

AI Fitness Coach

-

Select an exercise to start your workout

-

- Powered by MediaPipe Pose โ€ข Real-time skeleton tracking -

-
+
+ local_fire_department +
+
+ +
+ {/* AI Coach Hero Header */} +
+
+ Session Objective +
+
+

+ Let's calibrate your next move. +

+

+ I'm your Kinetic AI. Tell me your constraints, and I'll generate a precision-engineered routine for maximum athletic output. +

+
+ + {/* Dynamic Constraint Toggles */} +
+
+
+ schedule +

DURATION

+

30 mins

+
+
+ fitness_center +

EQUIPMENT

+

Dumbbells

+
+
+ target +

FOCUS

+

Core Focus

+
+
+ bolt +

INTENSITY

+

High (RPE 8)

+
+
+
+ + {/* Generated Routine Section (Bento Style) */} +
+
+
+

Available Routines

+
+ Select to Start +
+
+ + {/* Pose model loading banner */} + {poseLoading && ( +
+ Loading MediaPipe Pose model... +
+ )} -
- {EXERCISES.map(ex => ( - - ))} -
+ {EXERCISES.map((ex) => ( +
!poseLoading && startWorkout(ex)} + className={`group relative bg-surface-container-lowest rounded-xl overflow-hidden flex flex-col md:flex-row h-auto md:h-48 border border-transparent hover:border-primary/20 transition-all ${poseLoading ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`} + > +
+ {ex.icon} +
+
+
+
+
+

{ex.name}

+ Active AI +
+

+ {ex.tips && ex.tips.length > 0 ? ex.tips[0] : 'Use AI to check your form in real-time.'} +

+
+
+ +
+
+
+ ))} +
+ + {/* Sidebar: Performance Insights */} +
+
+
+
+ auto_awesome + Kinetic Coach Insight +
+

+ "Your form is the bridge between power and stability. The AI will guide you to perfection." +

+
+
+
+
); } @@ -555,165 +639,165 @@ export function FitnessTab() { // Render โ€” Active Workout // ------------------------------------------------------------------ return ( -
- {/* Pose model loading banner */} - {poseLoading && ( -
- Loading MediaPipe Pose model... -
- )} - - {/* Header */} -
-
- {selectedExercise.icon} - {selectedExercise.name} -
-
-
{currentAngle}ยฐ
-
{formatTime(elapsed)}
+
+ {/* TopAppBar */} +
+
+
+ KINETIC +
+
+ {selectedExercise.name} +
-
- - {/* Camera Feed + Skeleton Overlay */} -
-
- {/* Hidden video element โ€” MediaPipe reads from this */} -
+
- {/* Rep Counter OR Plank Hold Timer */} - {isPlank ? ( -
-
{formatTime(plankHoldTime)}
-
Hold Time
- {plankBestTime > 0 && ( -
๐Ÿ† Best: {formatTime(plankBestTime)}
- )} -
- ) : ( -
-
-
- {totalReps} - REPS -
+ {/* Main Metric: Reps or Plank Time */} +
+ {isPlank ? ( + <> + +
+ {formatTime(plankHoldTime)} +
+ {plankBestTime > 0 && ๐Ÿ† Best: {formatTime(plankBestTime)}} +
+ timer +
+ + ) : ( + <> + +
+ {totalReps} +
+
+ fitness_center +
+ + )}
-
-
- โœ… - {stats.correctReps} - Correct -
-
- โŒ - {stats.incorrectReps} - Incorrect -
-
- ๐ŸŽฏ - {accuracy}% - Accuracy + + {/* Accuracy Split (Bento Card Large) */} +
+ +
+
+
+ Correct + {stats.correctReps} +
+
+
0 ? (stats.correctReps / totalReps) * 100 : 0}%` }}>
+
+
+
+
+ Incorrect + {stats.incorrectReps} +
+
+
0 ? (stats.incorrectReps / totalReps) * 100 : 0}%` }}>
+
+
-
- )} - - {/* Feedback */} - {feedback && ( -
-

{feedback}

-
- )} - - {/* Tips */} -
-

๐Ÿ’ก Tips for {selectedExercise.name}

-
    - {selectedExercise.tips.map((tip, i) => ( -
  • {tip}
  • - ))} -
-
- {/* Error */} - {error && ( -
- Error: {error} -
- )} - - {/* Controls */} -
- {isWorkout ? ( - - ) : ( - - )} - -
+ {/* Controls (Action Bar) */} +
+ + {isWorkout ? ( + + ) : ( + + )} +
+ +
); } diff --git a/src/fitness/poseEngine.ts b/src/fitness/poseEngine.ts index 9b799f65..b7f44879 100644 --- a/src/fitness/poseEngine.ts +++ b/src/fitness/poseEngine.ts @@ -132,7 +132,7 @@ export const EXERCISES: ExerciseDef[] = [ // Hysteresis: enter down at <90, exit down at >110; enter up at >160, exit up at <145 let position: ExercisePosition = 'middle'; - if (kneeAngle < 90) position = 'down'; + if (kneeAngle < 80) position = 'down'; else if (kneeAngle > 160) position = 'up'; const form: FormQuality = hipAngle > 60 ? 'good' : 'bad'; diff --git a/src/styles/index.css b/src/styles/index.css index 39955664..3cd5106f 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -25,7 +25,6 @@ body { background: var(--bg); color: var(--text); -webkit-font-smoothing: antialiased; - overflow: hidden; } /* --------------------------------------------------------------------------- diff --git a/stitch_screens/ai_coach.html b/stitch_screens/ai_coach.html new file mode 100644 index 00000000..7ba2e1ac --- /dev/null +++ b/stitch_screens/ai_coach.html @@ -0,0 +1,296 @@ + + + + + +KINETIC | AI Fitness Coach + + + + + + + + + + +
+
+
+user profile picture +
+KINETIC +
+
+local_fire_department +
+
+
+ +
+
+Session Objective +
+
+

+ Let's calibrate your next move. +

+

+ I'm your Kinetic AI. Tell me your constraints, and I'll generate a precision-engineered routine for maximum athletic output. +

+
+ +
+
+
+schedule +

DURATION

+

30 mins

+
+
+fitness_center +

EQUIPMENT

+

Dumbbells

+
+
+target +

FOCUS

+

Core Focus

+
+
+bolt +

INTENSITY

+

High (RPE 8)

+
+
+
+ +
+
+
+ + +
+
+ +
+ +
+
+

Generated Routine

+
+Precision Tier +
+
+ +
+
+Weighted Plank +
+
+
+
+

Weighted Plank Reach

+3 Sets +
+

Engage transverse abdominis by reaching forward slowly while maintaining a neutral spine. Control the dumbbell drag.

+
+
+
+REPS +12 Per Side +
+
+REST +45s +
+
+
+
+ +
+
+Dumbbell Russian Twists +
+
+
+
+

Dynamic Russian Twists

+4 Sets +
+

Focus on the rotation from the ribs, not just moving the arms. Keep heels 2 inches off the ground for constant tension.

+
+
+
+WEIGHT +15 lbs +
+
+REPS +20 Total +
+
+
+
+ +
+
+Leg Raises +
+
+
+
+

L-Sit Compression

+3 Sets +
+

Explosive lift followed by a 3-second negative descent. This targets the lower-abdominal kinetic chain.

+
+
+
+TIME +30s AMRAP +
+
+INTENSITY +MAX +
+
+
+
+
+ +
+ +
+
+
+auto_awesome +Kinetic Coach Insight +
+

+ "Your core is the bridge between power and stability. Today's RPE 8 intensity will force neural adaptation. Don't sacrifice form for speed." +

+
+ +
+
Projected Load
+
+
+
+Neural Fatigue +65% +
+
+
+
+
+
+
+Metabolic Stress +82% +
+
+
+
+
+
+
+ + +
+
+
+ + + \ No newline at end of file diff --git a/stitch_screens/dashboard.html b/stitch_screens/dashboard.html new file mode 100644 index 00000000..b279d283 --- /dev/null +++ b/stitch_screens/dashboard.html @@ -0,0 +1,295 @@ + + + + + + + + + + + + + + + +
+
+
+ +
+KINETIC +
+
+local_fire_department +
+
+
+ +
+
+

TOTAL WORKOUTS

+

42

+
+trending_up ++12% THIS MONTH +
+
+
+

FORM ACCURACY

+
+

94

+% +
+
+
+
+
+
+

TOTAL TIME

+
+

1,280

+MIN +
+

TOP 5% OF ATHLETES

+
+
+ +
+ +
+
+
+

KINETIC CONSISTENCY

+

FORM IMPROVEMENT โ€” LAST 30 DAYS

+
+
+30D +90D +
+
+ +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Oct 01 +Today +
+
+ +
+
+bolt +
+
+

IMPROVEMENT FOCUS

+

SQUAT DEPTH & BALANCE

+
+
+
+Current +82ยฐ Parallel +
+
+Target +90ยฐ Deep +
+ +
+
+
+ +
+
+

RECENT ACTIVITY

+VIEW ALL +
+
+ +
+
+
+
+fitness_center +
+
+

Full Body Power Blast

+

Yesterday โ€ข 52 min โ€ข 420 kcal

+
+
+
+92% +ACCURACY +
+
+ +
+

FORM CORRECTIONS (3)

+
+
+
+Barbell Row +Critical +
+

Spine rounded at max extension. Maintain neutral posture to avoid lumbar stress.

+
+
+
+Overhead Press +Refinement +
+

Core engagement dropped during final set. Breathe on descent.

+
+
+
+
+ +
+
+
+directions_run +
+
+

Interval Sprint Session

+

Oct 22 โ€ข 35 min โ€ข 380 kcal

+
+
+
+ +chevron_right +
+
+ +
+
+
+self_improvement +
+
+

Active Recovery Yoga

+

Oct 20 โ€ข 20 min โ€ข 110 kcal

+
+
+
+ +chevron_right +
+
+
+
+
+ + + \ No newline at end of file diff --git a/stitch_screens/home_feed.html b/stitch_screens/home_feed.html new file mode 100644 index 00000000..d13bbfe9 --- /dev/null +++ b/stitch_screens/home_feed.html @@ -0,0 +1,294 @@ + + + + + +Kinetic | AI Fitness Trainer + + + + + + + + + + +
+
+
+ +
+KINETIC +
+
+
+local_fire_department +12 +
+notifications +
+
+
+ +
+
+

October 2023

+calendar_today +
+
+
+Mon +16 +
+
+Tue +17 +
+
+Wed +18 +
+
+
+Thu +19 +
+
+Fri +20 +
+
+Sat +21 +
+
+Sun +22 +
+
+
+ +
+ +
+ +
+
+
+AI Optimized +65 Min โ€ข High Intensity +
+

Upper Body
Hypertrophy

+
+
+
+ +
+
++3 +
+
+ +
+
+
+
+ +
+
+

Upcoming Schedule

+ +
+
+ +
+ +
+
+Tomorrow โ€ข 08:00 AM +
+
+
+

Metabolic Conditioning

+
+
+timer +45M +
+
+bolt +Extreme +
+
+
+chevron_right +
+
+
+ +
+
+Friday โ€ข 07:30 AM +
+
+
+

Posterior Chain Focus

+
+
+timer +55M +
+
+fitness_center +Medium +
+
+
+chevron_right +
+
+
+ +
+
+Saturday โ€ข 10:00 AM +
+
+
+

Active Recovery & Flow

+
+
+timer +30M +
+
+self_improvement +Low +
+
+
+chevron_right +
+
+
+
+
+ +
+
+Weekly Volume +12.4tons +
+trending_up +12% vs last week +
+
+
+Avg. Intensity +84% +
+horizontal_rule Steady pace +
+
+
+
+ + + \ No newline at end of file diff --git a/stitch_screens/live_workout.html b/stitch_screens/live_workout.html new file mode 100644 index 00000000..288b8742 --- /dev/null +++ b/stitch_screens/live_workout.html @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + +
+
+
+
+user profile picture +
+KINETIC +
+
+local_fire_department +
+
+
+ +
+ +
+
+workout view +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+warning +

Keep your back straight

+
+
+
+
+ +
+
+

AI Confidence

+

98.4%

+
+
+
+ +
+ +
+ +
+12:44 +
+
+ +
+ +
+24 ++3 this set +
+
+fitness_center +
+
+ +
+ +
+
+
+Correct +18 +
+
+
+
+
+
+
+Incorrect +6 +
+
+
+
+
+
+
+ +
+ + +
+
+
+ + + \ No newline at end of file From 8cc4f190dd20422dfdedf8c8f05bd1bbdc0fe92d Mon Sep 17 00:00:00 2001 From: Himanshu Ranjan Date: Mon, 23 Mar 2026 23:41:51 +0530 Subject: [PATCH 06/14] ui refinment --- src/components/FitnessTab.tsx | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/src/components/FitnessTab.tsx b/src/components/FitnessTab.tsx index 065196f3..76ee30bc 100644 --- a/src/components/FitnessTab.tsx +++ b/src/components/FitnessTab.tsx @@ -543,31 +543,7 @@ export function FitnessTab() {

- {/* Dynamic Constraint Toggles */} -
-
-
- schedule -

DURATION

-

30 mins

-
-
- fitness_center -

EQUIPMENT

-

Dumbbells

-
-
- target -

FOCUS

-

Core Focus

-
-
- bolt -

INTENSITY

-

High (RPE 8)

-
-
-
+ {/* Generated Routine Section (Bento Style) */}
@@ -763,7 +739,7 @@ export function FitnessTab() { {stats.correctReps}
-
0 ? (stats.correctReps / totalReps) * 100 : 0}%` }}>
+
0 ? (stats.correctReps / totalReps) * 100 : 0}%` }}>
@@ -772,7 +748,7 @@ export function FitnessTab() { {stats.incorrectReps}
-
0 ? (stats.incorrectReps / totalReps) * 100 : 0}%` }}>
+
0 ? (stats.incorrectReps / totalReps) * 100 : 0}%` }}>
From 5e346275b4110918b0ca6793cd3f4d2b3073fbeb Mon Sep 17 00:00:00 2001 From: Himanshu Ranjan Date: Mon, 23 Mar 2026 23:56:34 +0530 Subject: [PATCH 07/14] Name change --- src/components/FitnessTab.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/FitnessTab.tsx b/src/components/FitnessTab.tsx index 76ee30bc..6f1ec62a 100644 --- a/src/components/FitnessTab.tsx +++ b/src/components/FitnessTab.tsx @@ -521,7 +521,7 @@ export function FitnessTab() { {/* TopAppBar Shell */}
- KINETIC + KINESIGHT
local_fire_department @@ -539,7 +539,7 @@ export function FitnessTab() { Let's calibrate your next move.

- I'm your Kinetic AI. Tell me your constraints, and I'll generate a precision-engineered routine for maximum athletic output. + I'm your KineSight AI. Tell me your constraints, and I'll generate a precision-engineered routine for maximum athletic output.

@@ -598,7 +598,7 @@ export function FitnessTab() {
auto_awesome - Kinetic Coach Insight + KineSight Coach Insight

"Your form is the bridge between power and stability. The AI will guide you to perfection." @@ -620,7 +620,7 @@ export function FitnessTab() {

- KINETIC + KINESIGHT
{selectedExercise.name} From 0844bd4fe2ed2fbbf0f27627ac641eb4564812c5 Mon Sep 17 00:00:00 2001 From: Aaryak <82655626+AaryakCreator@users.noreply.github.com> Date: Fri, 3 Apr 2026 00:06:01 +0530 Subject: [PATCH 08/14] Enhance README with setup and usage instructions Added installation instructions and usage details to README. --- README.md | 163 +++++++++++++++++------------------------------------- 1 file changed, 50 insertions(+), 113 deletions(-) diff --git a/README.md b/README.md index f03fd205..3689688a 100644 --- a/README.md +++ b/README.md @@ -1,130 +1,67 @@ -# RunAnywhere Web Starter App +# ๐Ÿ‹๏ธโ€โ™‚๏ธ Kine-Sight: Your AI-Powered Digital Fitness Trainer -A minimal React + TypeScript starter app demonstrating **on-device AI in the browser** using the [`@runanywhere/web`](https://www.npmjs.com/package/@runanywhere/web) SDK. All inference runs locally via WebAssembly โ€” no server, no API key, 100% private. +[![Live Demo](https://img.shields.io/badge/Live_Demo-kine--sight.vercel.app-blue?style=for-the-badge)](https://kine-sight.vercel.app) -## Features +**Kine-Sight** is a cutting-edge digital fitness trainer with an "eye." By leveraging on-device Vision-Language Models (VLMs), it watches your form, tracks your progress, catches your mistakes, and provides real-time, actionable feedback to help you perfect your fitness journey. -| Tab | What it does | -|-----|-------------| -| **Chat** | Stream text from an on-device LLM (LFM2 350M) | -| **Vision** | Point your camera and describe what the VLM sees (LFM2-VL 450M) | -| **Voice** | Speak naturally โ€” VAD detects speech, STT transcribes, LLM responds, TTS speaks back | +Everything runs **100% locally in your browser** via WebAssemblyโ€”meaning zero server costs, zero API keys, and complete privacy for your camera feed. -## Quick Start +## โœจ Features -```bash -npm install -npm run dev -``` +- **๐Ÿ‘€ Real-Time Vision Tracking:** Uses your device's camera to analyze your posture, reps, and movements in real-time. +- **๐Ÿ—ฃ๏ธ Interactive Feedback:** Get instant corrections and motivational feedback when your form breaks down. +- **๐Ÿ”’ 100% Private & Secure:** Powered by `@runanywhere/web`, all AI inference happens locally on your device. Your camera feed never leaves your browser. +- **โšก Blazing Fast On-Device AI:** Utilizes optimized WASM engines to run Vision (LFM2-VL) and Text (LFM2) models directly in the web browser. +- **๐ŸŽ™๏ธ Voice Integration:** Speak to your AI trainer naturally, and it will respond via text-to-speech. -Open [http://localhost:5173](http://localhost:5173). Models are downloaded on first use and cached in the browser's Origin Private File System (OPFS). +## ๐Ÿ› ๏ธ Tech Stack -## How It Works +- **Frontend:** React, TypeScript, Vite +- **Styling:** CSS / HTML5 +- **Local AI Engine:** `@runanywhere/web` (llama.cpp, whisper.cpp, sherpa-onnx) +- **Deployment:** Vercel -``` -@runanywhere/web (npm package) - โ”œโ”€โ”€ WASM engine (llama.cpp, whisper.cpp, sherpa-onnx) - โ”œโ”€โ”€ Model management (download, OPFS cache, load/unload) - โ””โ”€โ”€ TypeScript API (TextGeneration, STT, TTS, VAD, VLM, VoicePipeline) -``` +## ๐Ÿš€ Quick Start -The app imports everything from `@runanywhere/web`: +### Prerequisites +- Node.js (v18 or higher) +- npm or yarn +- A modern web browser (Chrome 120+ or Edge 120+ recommended) -```typescript -import { RunAnywhere, SDKEnvironment } from '@runanywhere/web'; -import { TextGeneration, VLMWorkerBridge } from '@runanywhere/web-llamacpp'; +### Installation -await RunAnywhere.initialize({ environment: SDKEnvironment.Development }); +1. **Clone the repository:** + ```bash + git clone [https://github.com/himanshuranjan2552/Kine-Sight.git](https://github.com/himanshuranjan2552/Kine-Sight.git) + cd Kine-Sight +2. **Install dependencies:** + ```bash + npm install +3. **Start the development server:** + ```bash + npm run dev +4. **Open in Browser:** + Navigate to http://localhost:5173. -// Stream LLM text -const { stream } = await TextGeneration.generateStream('Hello!', { maxTokens: 200 }); -for await (const token of stream) { console.log(token); } +(Note: AI Models will be downloaded on first use and cached locally in your browser's Origin Private File System). -// VLM: describe an image -const result = await VLMWorkerBridge.shared.process(rgbPixels, width, height, 'Describe this.'); -``` -## Project Structure +## ๐Ÿง  How It Works Under the Hood -``` -src/ -โ”œโ”€โ”€ main.tsx # React root -โ”œโ”€โ”€ App.tsx # Tab navigation (Chat | Vision | Voice) -โ”œโ”€โ”€ runanywhere.ts # SDK init + model catalog + VLM worker -โ”œโ”€โ”€ workers/ -โ”‚ โ””โ”€โ”€ vlm-worker.ts # VLM Web Worker entry (2 lines) -โ”œโ”€โ”€ hooks/ -โ”‚ โ””โ”€โ”€ useModelLoader.ts # Shared model download/load hook -โ”œโ”€โ”€ components/ -โ”‚ โ”œโ”€โ”€ ChatTab.tsx # LLM streaming chat -โ”‚ โ”œโ”€โ”€ VisionTab.tsx # Camera + VLM inference -โ”‚ โ”œโ”€โ”€ VoiceTab.tsx # Full voice pipeline -โ”‚ โ””โ”€โ”€ ModelBanner.tsx # Download progress UI -โ””โ”€โ”€ styles/ - โ””โ”€โ”€ index.css # Dark theme CSS -``` +- Kine-Sight is built on top of the **RunAnywhere SDK**. It uses a combination of powerful local models. +- **VLM** (Vision-Language Model): Captures frames from your webcam and analyzes your body positioning. +- **LLM** (Large Language Model): Processes the vision data to formulate encouraging text or corrective instructions. +- **Voice Pipeline** (VAD, STT, TTS): Allows you to ask the trainer questions hands-free while working out. -## Adding Your Own Models +## ๐Ÿค Contributing +Contributions, issues, and feature requests are welcome! Feel free to check the issues page if you want to contribute. +1. Fork the Project +2. Create your Feature Branch `git checkout -b feature/AmazingFeature` +3. Commit your Changes `git commit -m 'Add some AmazingFeature'` +4. Push to the Branch `git push origin feature/AmazingFeature` +5. Open a Pull Request -Edit the `MODELS` array in `src/runanywhere.ts`: - -```typescript -{ - id: 'my-custom-model', - name: 'My Model', - repo: 'username/repo-name', // HuggingFace repo - files: ['model.Q4_K_M.gguf'], // Files to download - framework: LLMFramework.LlamaCpp, - modality: ModelCategory.Language, // or Multimodal, SpeechRecognition, etc. - memoryRequirement: 500_000_000, // Bytes -} -``` - -Any GGUF model compatible with llama.cpp works for LLM/VLM. STT/TTS/VAD use sherpa-onnx models. - -## Deployment - -### Vercel - -```bash -npm run build -npx vercel --prod -``` - -The included `vercel.json` sets the required Cross-Origin-Isolation headers. - -### Netlify - -Add a `_headers` file: - -``` -/* - Cross-Origin-Opener-Policy: same-origin - Cross-Origin-Embedder-Policy: credentialless -``` - -### Any static host - -Serve the `dist/` folder with these HTTP headers on all responses: - -``` -Cross-Origin-Opener-Policy: same-origin -Cross-Origin-Embedder-Policy: credentialless -``` - -## Browser Requirements - -- Chrome 96+ or Edge 96+ (recommended: 120+) -- WebAssembly (required) -- SharedArrayBuffer (requires Cross-Origin Isolation headers) -- OPFS (for persistent model cache) - -## Documentation - -- [SDK API Reference](https://docs.runanywhere.ai) -- [npm package](https://www.npmjs.com/package/@runanywhere/web) -- [GitHub](https://github.com/RunanywhereAI/runanywhere-sdks) - -## License - -MIT +## ๐Ÿ“„ License +This project is licensed under the MIT License. +--- +Built with โค๏ธ to make fitness smarter, safer, and more accessible. From fef85d86b44e9a4c16966ed341a47ff9ed09e152 Mon Sep 17 00:00:00 2001 From: Aaryak <82655626+AaryakCreator@users.noreply.github.com> Date: Fri, 3 Apr 2026 00:08:39 +0530 Subject: [PATCH 09/14] Add video link to README Added a video link to the README for demonstration. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3689688a..d86597a5 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ Everything runs **100% locally in your browser** via WebAssemblyโ€”meaning zero - **โšก Blazing Fast On-Device AI:** Utilizes optimized WASM engines to run Vision (LFM2-VL) and Text (LFM2) models directly in the web browser. - **๐ŸŽ™๏ธ Voice Integration:** Speak to your AI trainer naturally, and it will respond via text-to-speech. +[![Watch the video](https://img.youtube.com/vi/egs9YfF6UwM/maxresdefault.jpg)](https://youtu.be/egs9YfF6UwM) + ## ๐Ÿ› ๏ธ Tech Stack - **Frontend:** React, TypeScript, Vite From 6c99b45daeebbb7ff542f84a3cfdd2df77d20971 Mon Sep 17 00:00:00 2001 From: Aaryak <82655626+AaryakCreator@users.noreply.github.com> Date: Fri, 3 Apr 2026 00:09:45 +0530 Subject: [PATCH 10/14] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index d86597a5..ccba6d0f 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ Everything runs **100% locally in your browser** via WebAssemblyโ€”meaning zero - **โšก Blazing Fast On-Device AI:** Utilizes optimized WASM engines to run Vision (LFM2-VL) and Text (LFM2) models directly in the web browser. - **๐ŸŽ™๏ธ Voice Integration:** Speak to your AI trainer naturally, and it will respond via text-to-speech. -[![Watch the video](https://img.youtube.com/vi/egs9YfF6UwM/maxresdefault.jpg)](https://youtu.be/egs9YfF6UwM) ## ๐Ÿ› ๏ธ Tech Stack From 8f3ef16afc51d5d58ad0479034d977af120917c3 Mon Sep 17 00:00:00 2001 From: Aaryak <82655626+AaryakCreator@users.noreply.github.com> Date: Fri, 3 Apr 2026 00:12:57 +0530 Subject: [PATCH 11/14] Add YouTube demo link to README Added a YouTube demo link to the README. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ccba6d0f..a1deae4d 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Everything runs **100% locally in your browser** via WebAssemblyโ€”meaning zero - **โšก Blazing Fast On-Device AI:** Utilizes optimized WASM engines to run Vision (LFM2-VL) and Text (LFM2) models directly in the web browser. - **๐ŸŽ™๏ธ Voice Integration:** Speak to your AI trainer naturally, and it will respond via text-to-speech. +[![YouTube Demo](https://img.shields.io/badge/YouTube-Watch_Demo-FF0000?style=for-the-badge&logo=youtube&logoColor=white)](https://www.youtube.com/watch?v=egs9YfF6UwM) ## ๐Ÿ› ๏ธ Tech Stack From 899ce710314f0ec368796c1481426fff0f9a62db Mon Sep 17 00:00:00 2001 From: Aaryak <82655626+AaryakCreator@users.noreply.github.com> Date: Fri, 3 Apr 2026 00:16:53 +0530 Subject: [PATCH 12/14] Updated README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a1deae4d..26a0ca05 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # ๐Ÿ‹๏ธโ€โ™‚๏ธ Kine-Sight: Your AI-Powered Digital Fitness Trainer [![Live Demo](https://img.shields.io/badge/Live_Demo-kine--sight.vercel.app-blue?style=for-the-badge)](https://kine-sight.vercel.app) +[![YouTube Demo](https://img.shields.io/badge/YouTube-Watch_Demo-FF0000?style=for-the-badge&logo=youtube&logoColor=white)](https://www.youtube.com/watch?v=egs9YfF6UwM) + **Kine-Sight** is a cutting-edge digital fitness trainer with an "eye." By leveraging on-device Vision-Language Models (VLMs), it watches your form, tracks your progress, catches your mistakes, and provides real-time, actionable feedback to help you perfect your fitness journey. @@ -14,7 +16,6 @@ Everything runs **100% locally in your browser** via WebAssemblyโ€”meaning zero - **โšก Blazing Fast On-Device AI:** Utilizes optimized WASM engines to run Vision (LFM2-VL) and Text (LFM2) models directly in the web browser. - **๐ŸŽ™๏ธ Voice Integration:** Speak to your AI trainer naturally, and it will respond via text-to-speech. -[![YouTube Demo](https://img.shields.io/badge/YouTube-Watch_Demo-FF0000?style=for-the-badge&logo=youtube&logoColor=white)](https://www.youtube.com/watch?v=egs9YfF6UwM) ## ๐Ÿ› ๏ธ Tech Stack From fb8b97ed57e9c3d186a3f5f2cd89422f130a6f2d Mon Sep 17 00:00:00 2001 From: proCreator_01 <82655626+AaryakCreator@users.noreply.github.com> Date: Sun, 5 Apr 2026 19:09:57 +0530 Subject: [PATCH 13/14] Add on-device LLM; Exercises and UI updates Introduce on-device LLM support and related services, add new exercise components and demo assets, and clean up build logs. Key additions: src/llm (llm-worker.ts, llmEngine.ts), src/tts/ttsService.ts, src/storage/storageService.ts, new exercises (bicep-curl, push-up), DemoVideo and ReportsTab components, sounds, styles for exercise demos, multi_agent.py, and DOCUMENTATION.md. Updated app UI and detection flow in src/App.tsx and src/components/FitnessTab.tsx; updated pose logic in src/fitness/poseEngine.ts; small CSS/theme tweaks in index.html and exercise styles. Removed stale build/error log files and minor dependency updates in package.json (added on-device LLM client and UI libs). --- .vscode/settings.json | 3 + DOCUMENTATION.md | 76 + build_error.log | Bin 3124 -> 0 bytes build_error_cmd.log | 16 - errors.txt | Bin 734 -> 0 bytes errors2.txt | 3 - errors3.txt | 3 - index.html | 19 + multi_agent.py | 99 + package-lock.json | 3988 ++++++++++++++++- package.json | 7 +- sounds/freesound_community-ding-36029.mp3 | Bin 0 -> 51827 bytes ...nity-training-program-incorrect2-88735.mp3 | Bin 0 -> 21120 bytes src/App.tsx | 31 +- src/components/DemoVideo.tsx | 78 + src/components/ExerciseDemo.tsx | 32 - src/components/FitnessTab.tsx | 777 +++- src/components/ReportsTab.tsx | 125 + src/exercises/bicep-curl/index.tsx | 13 + src/exercises/push-up/index.tsx | 13 + src/fitness/poseEngine.ts | 319 +- src/llm/llm-worker.ts | 6 + src/llm/llmEngine.ts | 88 + src/storage/storageService.ts | 91 + src/styles/exercise-demo-showcase.css | 145 + src/styles/exercise-demo.css | 366 +- src/tts/ttsService.ts | 86 + vite.config.ts | 3 + 28 files changed, 5713 insertions(+), 674 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 DOCUMENTATION.md delete mode 100644 build_error.log delete mode 100644 build_error_cmd.log delete mode 100644 errors.txt delete mode 100644 errors2.txt delete mode 100644 errors3.txt create mode 100644 multi_agent.py create mode 100644 sounds/freesound_community-ding-36029.mp3 create mode 100644 sounds/freesound_community-training-program-incorrect2-88735.mp3 create mode 100644 src/components/DemoVideo.tsx delete mode 100644 src/components/ExerciseDemo.tsx create mode 100644 src/components/ReportsTab.tsx create mode 100644 src/exercises/bicep-curl/index.tsx create mode 100644 src/exercises/push-up/index.tsx create mode 100644 src/llm/llm-worker.ts create mode 100644 src/llm/llmEngine.ts create mode 100644 src/storage/storageService.ts create mode 100644 src/styles/exercise-demo-showcase.css create mode 100644 src/tts/ttsService.ts diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..00ad71fb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules\\typescript\\lib" +} \ No newline at end of file diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md new file mode 100644 index 00000000..8e3a34f4 --- /dev/null +++ b/DOCUMENTATION.md @@ -0,0 +1,76 @@ +# Kine-Sight Documentation + +## Overview +**Kine-Sight** is an interactive, browser-based AI fitness coaching application. It leverages real-time local AI processing to track the user's fitness movements, count repetitions, verify correct posture using visual pose estimation, and provide real-time motivating and corrective feedback through a local Large Language Model (LLM). + +Because it relies on WebAssembly (WASM) and browser-based AI inference, it is highly private and fastโ€”processing happens entirely on the user's device without needing external API calls to remote servers. + +## Tech Stack & Dependencies +- **Frontend Framework**: React 19 + TypeScript +- **Bundler**: Vite +- **Computer Vision (Pose Detection)**: `@mediapipe/tasks-vision` runs MediaPipe's lightweight pose-landmarker model in the browser. +- **On-Device LLM (Voice/Text Coach)**: `@mlc-ai/web-llm` enables running local LLMs (e.g., Llama-3-8B-Instruct) natively in the browser using WebGPU and WebAssembly. +- **Audio Routing**: Web Audio API manages immediate correct/incorrect rep sound indications. + +--- + +## Core Architecture and Data Flow + +### 1. `src/App.tsx` & `src/main.tsx` +These act as the entry points of the application. `App.tsx` serves as a simple shell that mounts the primary view: `FitnessTab`. + +### 2. `src/components/FitnessTab.tsx` +This is the core view and primary state-machine controller for the application. + +**Key responsibilities:** +- **UI Architecture**: Displays the pre-workout "Dashboard/Bento" view with exercise choices. During a workout, it renders the video feed, the canvas overlay (for the skeleton), and the AI Coach's rolling text feedback. +- **Lifecycle Management**: + - Activates the camera (`getUserMedia`). + - Initializes the MediaPipe Pose model. + - Initializes the WebLLM coach in the background. + - Coordinates a pre-workout countdown. +- **Detection Loop (`startDetectionLoop`)**: + - Runs recursively via `requestAnimationFrame` to sample frames from the active `
{/* Main Content Canvas */} -
+
{/* Camera Viewport & AI Overlay */} -
+
{/* Hidden video element โ€” MediaPipe reads from this */}
- {/* Metrics Section (Asymmetric Bento Grid) */} -
- {/* Time Elapsed */} -
-
diff --git a/src/llm/llmEngine.ts b/src/llm/llmEngine.ts index b4b090de..9120ee77 100644 --- a/src/llm/llmEngine.ts +++ b/src/llm/llmEngine.ts @@ -1,7 +1,7 @@ import { CreateWebWorkerMLCEngine, WebWorkerMLCEngine, InitProgressReport } from '@mlc-ai/web-llm'; import type { FormQuality } from '../fitness/poseEngine'; -export const SYSTEM_PROMPT = "You are KineSight, a calm, motivating, and highly precise AI fitness coach. Based on the user's exercise data and form details, provide exactly ONE short, punchy sentence to guide or motivate them. No lists, no markdown."; +export const SYSTEM_PROMPT = "You are an AI fitness coach. When the user makes a mistake, explain EXACTLY what body part is misaligned and give ONE clear corrective cue. When motivating, be energetic and specific to their progress. Never use markdown, lists, or emojis. Keep it to ONE short sentence."; // Qwen2.5-1.5B โ€” verified to exist in @mlc-ai/web-llm v0.2.82 registry const MODEL_ID = "Qwen2.5-1.5B-Instruct-q4f16_1-MLC"; @@ -51,7 +51,7 @@ class LLMEngineWrapper { this.generating = true; - const prompt = `Action: ${action}\nExercise: ${exercise}\nData: ${JSON.stringify(context)}\nKeep your response to a single, short, punchy sentence. No markdown.`; + const prompt = `Exercise: ${exercise}\nAction: ${action}\nForm Mistakes: ${context.formDetails?.length ? context.formDetails.join(', ') : 'None'}\nRep Number: ${context.repNumber}\nProvide your ONE sentence response without any markdown.`; const messages = [ { role: "system" as const, content: SYSTEM_PROMPT },