danielrosehill commited on
Commit
0138286
·
1 Parent(s): 8b52932
.gitignore ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+
15
+ # Editor directories and files
16
+ .vscode/*
17
+ !.vscode/extensions.json
18
+ .idea
19
+ .DS_Store
20
+ *.suo
21
+ *.ntvs*
22
+ *.njsproj
23
+ *.sln
24
+ *.sw?
App.tsx ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useCallback } from 'react';
2
+ import { AudioInput } from './components/AudioInput';
3
+ import { ResultsView } from './components/ResultsView';
4
+ import { ApiKeySettings } from './components/ApiKeySettings';
5
+ import { analyzeAudio } from './services/geminiService';
6
+ import type { VocalProfile, EQSetting } from './types';
7
+
8
+ type Status = 'idle' | 'processing' | 'success' | 'error';
9
+
10
+ const blobToBase64 = (blob: Blob): Promise<string> => {
11
+ return new Promise((resolve, reject) => {
12
+ const reader = new FileReader();
13
+ reader.onloadend = () => {
14
+ if (typeof reader.result === 'string') {
15
+ resolve(reader.result.split(',')[1]);
16
+ } else {
17
+ reject(new Error('Failed to convert blob to base64'));
18
+ }
19
+ };
20
+ reader.onerror = reject;
21
+ reader.readAsDataURL(blob);
22
+ });
23
+ };
24
+
25
+ const Header: React.FC = () => (
26
+ <header className="py-4 px-6 text-center">
27
+ <h1 className="text-4xl md:text-5xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-brand-blue to-brand-purple">
28
+ EQ Template Creator
29
+ </h1>
30
+ <p className="mt-2 text-lg text-gray-400">Your Personal AI Audio Engineer</p>
31
+ </header>
32
+ );
33
+
34
+ const Loader: React.FC<{ message: string }> = ({ message }) => (
35
+ <div className="flex flex-col items-center justify-center space-y-4 p-8">
36
+ <div className="w-16 h-16 border-4 border-dashed rounded-full animate-spin border-brand-blue"></div>
37
+ <p className="text-lg text-gray-300">{message}</p>
38
+ </div>
39
+ );
40
+
41
+ export default function App() {
42
+ const [status, setStatus] = useState<Status>('idle');
43
+ const [error, setError] = useState<string | null>(null);
44
+ const [vocalProfile, setVocalProfile] = useState<VocalProfile | null>(null);
45
+ const [eqSettings, setEqSettings] = useState<EQSetting[] | null>(null);
46
+ const [audioBlob, setAudioBlob] = useState<Blob | null>(null);
47
+ const [audacityXml, setAudacityXml] = useState<string | null>(null);
48
+ const [hasApiKey, setHasApiKey] = useState<boolean>(false);
49
+
50
+ const handleAudioSubmit = useCallback(async (blob: Blob, mimeType: string) => {
51
+ setStatus('processing');
52
+ setError(null);
53
+ setAudioBlob(blob);
54
+
55
+ try {
56
+ const base64Audio = await blobToBase64(blob);
57
+ const result = await analyzeAudio(base64Audio, mimeType);
58
+
59
+ setVocalProfile(result.vocalProfile);
60
+ setEqSettings(result.eqPreset);
61
+ setAudacityXml(result.audacityXml);
62
+ setStatus('success');
63
+ } catch (err) {
64
+ console.error(err);
65
+ setError(err instanceof Error ? err.message : 'An unknown error occurred during analysis.');
66
+ setStatus('error');
67
+ }
68
+ }, []);
69
+
70
+ const handleReset = () => {
71
+ setStatus('idle');
72
+ setError(null);
73
+ setVocalProfile(null);
74
+ setEqSettings(null);
75
+ setAudioBlob(null);
76
+ setAudacityXml(null);
77
+ };
78
+
79
+ const renderContent = () => {
80
+ switch (status) {
81
+ case 'processing':
82
+ return <Loader message="Gemini is analyzing your voice... this may take a moment." />;
83
+ case 'success':
84
+ return vocalProfile && eqSettings && audioBlob && audacityXml && (
85
+ <ResultsView
86
+ vocalProfile={vocalProfile}
87
+ eqSettings={eqSettings}
88
+ audioBlob={audioBlob}
89
+ audacityXml={audacityXml}
90
+ onReset={handleReset}
91
+ />
92
+ );
93
+ case 'error':
94
+ return (
95
+ <div className="text-center p-8 bg-red-900/20 rounded-lg border border-red-500">
96
+ <h3 className="text-2xl font-bold text-red-400">Analysis Failed</h3>
97
+ <p className="mt-2 text-red-300">{error}</p>
98
+ <button
99
+ onClick={handleReset}
100
+ className="mt-6 px-6 py-2 bg-brand-blue text-white font-semibold rounded-lg hover:bg-opacity-80 transition-all"
101
+ >
102
+ Try Again
103
+ </button>
104
+ </div>
105
+ );
106
+ case 'idle':
107
+ default:
108
+ return <AudioInput onAudioSubmit={handleAudioSubmit} isProcessing={status === 'processing'}/>;
109
+ }
110
+ };
111
+
112
+ return (
113
+ <div className="min-h-screen flex flex-col items-center justify-center p-4">
114
+ <main className="w-full max-w-4xl mx-auto">
115
+ <Header />
116
+ <div className="mt-8 mb-6">
117
+ <ApiKeySettings onKeyConfigured={setHasApiKey} />
118
+ </div>
119
+ {hasApiKey && (
120
+ <div className="bg-gray-800/50 rounded-2xl shadow-2xl backdrop-blur-sm border border-gray-700">
121
+ {renderContent()}
122
+ </div>
123
+ )}
124
+ <footer className="text-center mt-8 text-gray-500 text-sm">
125
+ <p>Powered by Google Gemini. For demonstration purposes only.</p>
126
+ </footer>
127
+ </main>
128
+ </div>
129
+ );
130
+ }
README.md CHANGED
@@ -1,11 +1,73 @@
1
- ---
2
- title: EQ Template Generator
3
- emoji: ⚡
4
- colorFrom: purple
5
- colorTo: pink
6
- sdk: static
7
- pinned: false
8
- short_description: Analyses voice characteristics and generates an XML for EQ
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div align="center">
2
+ <img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
3
+ </div>
4
+
5
+ # EQ Template Creator - BYOK Edition
6
+
7
+ AI-powered vocal analysis and EQ preset generator using Google Gemini API.
8
+
9
+ This app analyzes your voice and generates custom EQ presets for audio processing software like Audacity.
10
+
11
+ ## Features
12
+
13
+ - **Bring Your Own Key (BYOK)**: Use your own Gemini API key
14
+ - **Browser-based API Key Storage**: Securely store your API key in localStorage
15
+ - **Voice Analysis**: AI-powered vocal characteristics analysis
16
+ - **Custom EQ Presets**: Generate 10-band EQ settings optimized for your voice
17
+ - **Audacity Export**: Download presets in Audacity XML format
18
+
19
+ ## Run Locally
20
+
21
+ **Prerequisites:** Node.js (v18 or higher)
22
+
23
+ 1. **Install dependencies:**
24
+ ```bash
25
+ npm install
26
+ ```
27
+
28
+ 2. **Configure API Key (Choose one method):**
29
+
30
+ **Option A - Browser UI (Recommended):**
31
+ - Run the app and enter your Gemini API key in the UI
32
+ - Your key will be stored in browser localStorage
33
+
34
+ **Option B - Environment Variable:**
35
+ - Set `VITE_GEMINI_API_KEY` in [.env.local](.env.local)
36
+ - Useful for development
37
+
38
+ 3. **Run the app:**
39
+ ```bash
40
+ npm run dev
41
+ ```
42
+
43
+ 4. **Open your browser:**
44
+ - Navigate to http://localhost:3000
45
+
46
+ ## Get a Gemini API Key
47
+
48
+ 1. Visit [Google AI Studio](https://aistudio.google.com/apikey)
49
+ 2. Sign in with your Google account
50
+ 3. Create a new API key
51
+ 4. Copy and paste it into the app's API key settings
52
+
53
+ ## How It Works
54
+
55
+ 1. Enter your Gemini API key (stored locally in your browser)
56
+ 2. Record or upload an audio sample of your voice
57
+ 3. Gemini analyzes your vocal characteristics
58
+ 4. Receive a custom EQ preset tailored to your voice
59
+ 5. Download the preset for use in Audacity or other audio software
60
+
61
+ ## Technology Stack
62
+
63
+ - **Frontend:** React 19 + TypeScript
64
+ - **Build Tool:** Vite
65
+ - **AI:** Google Gemini 2.5 Pro (via @google/genai SDK)
66
+ - **Styling:** Tailwind CSS (inline utilities)
67
+
68
+ ## Privacy & Security
69
+
70
+ - Your API key is stored only in your browser's localStorage
71
+ - No keys are sent to any server except Google's Gemini API
72
+ - Audio analysis happens via direct API calls to Google Gemini
73
+ - No data is stored on external servers
components/ApiKeySettings.tsx ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect } from 'react';
2
+
3
+ const STORAGE_KEY = 'gemini_api_key';
4
+
5
+ interface ApiKeySettingsProps {
6
+ onKeyConfigured: (hasKey: boolean) => void;
7
+ }
8
+
9
+ export const ApiKeySettings: React.FC<ApiKeySettingsProps> = ({ onKeyConfigured }) => {
10
+ const [apiKey, setApiKey] = useState('');
11
+ const [showKey, setShowKey] = useState(false);
12
+ const [hasStoredKey, setHasStoredKey] = useState(false);
13
+
14
+ useEffect(() => {
15
+ const storedKey = localStorage.getItem(STORAGE_KEY);
16
+ if (storedKey) {
17
+ setHasStoredKey(true);
18
+ onKeyConfigured(true);
19
+ } else {
20
+ onKeyConfigured(false);
21
+ }
22
+ }, [onKeyConfigured]);
23
+
24
+ const handleSaveKey = () => {
25
+ if (apiKey.trim()) {
26
+ localStorage.setItem(STORAGE_KEY, apiKey.trim());
27
+ setHasStoredKey(true);
28
+ setApiKey('');
29
+ onKeyConfigured(true);
30
+ }
31
+ };
32
+
33
+ const handleClearKey = () => {
34
+ localStorage.removeItem(STORAGE_KEY);
35
+ setHasStoredKey(false);
36
+ setApiKey('');
37
+ onKeyConfigured(false);
38
+ };
39
+
40
+ const handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
41
+ if (e.key === 'Enter') {
42
+ handleSaveKey();
43
+ }
44
+ };
45
+
46
+ if (hasStoredKey) {
47
+ return (
48
+ <div className="p-6 bg-green-900/20 rounded-lg border border-green-500/50">
49
+ <div className="flex items-center justify-between">
50
+ <div className="flex items-center space-x-2">
51
+ <svg className="w-5 h-5 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
52
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
53
+ </svg>
54
+ <span className="text-green-400 font-semibold">API Key Configured</span>
55
+ </div>
56
+ <button
57
+ onClick={handleClearKey}
58
+ className="px-4 py-2 bg-red-600 text-white text-sm font-semibold rounded-lg hover:bg-red-700 transition-all"
59
+ >
60
+ Clear Key
61
+ </button>
62
+ </div>
63
+ </div>
64
+ );
65
+ }
66
+
67
+ return (
68
+ <div className="p-6 bg-blue-900/20 rounded-lg border border-blue-500/50">
69
+ <h3 className="text-xl font-bold text-blue-300 mb-2">Configure Gemini API Key</h3>
70
+ <p className="text-gray-400 text-sm mb-4">
71
+ Enter your Gemini API key to use this app. Your key will be stored locally in your browser.
72
+ Get your API key from{' '}
73
+ <a
74
+ href="https://aistudio.google.com/apikey"
75
+ target="_blank"
76
+ rel="noopener noreferrer"
77
+ className="text-blue-400 hover:text-blue-300 underline"
78
+ >
79
+ Google AI Studio
80
+ </a>
81
+ </p>
82
+ <div className="flex flex-col sm:flex-row gap-3">
83
+ <div className="flex-1 relative">
84
+ <input
85
+ type={showKey ? 'text' : 'password'}
86
+ value={apiKey}
87
+ onChange={(e) => setApiKey(e.target.value)}
88
+ onKeyPress={handleKeyPress}
89
+ placeholder="Enter your Gemini API key..."
90
+ className="w-full px-4 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:border-blue-500 pr-10"
91
+ />
92
+ <button
93
+ type="button"
94
+ onClick={() => setShowKey(!showKey)}
95
+ className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-300"
96
+ >
97
+ {showKey ? (
98
+ <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
99
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
100
+ </svg>
101
+ ) : (
102
+ <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
103
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
104
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
105
+ </svg>
106
+ )}
107
+ </button>
108
+ </div>
109
+ <button
110
+ onClick={handleSaveKey}
111
+ disabled={!apiKey.trim()}
112
+ className="px-6 py-2 bg-brand-blue text-white font-semibold rounded-lg hover:bg-opacity-80 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
113
+ >
114
+ Save Key
115
+ </button>
116
+ </div>
117
+ </div>
118
+ );
119
+ };
120
+
121
+ export const getApiKey = (): string | null => {
122
+ return localStorage.getItem(STORAGE_KEY);
123
+ };
components/AudioInput.tsx ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState, useRef, useCallback, useEffect } from 'react';
3
+
4
+ interface AudioInputProps {
5
+ onAudioSubmit: (blob: Blob, mimeType: string) => void;
6
+ isProcessing: boolean;
7
+ }
8
+
9
+ const MicrophoneIcon: React.FC<{className?: string}> = ({ className }) => (
10
+ <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
11
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" />
12
+ </svg>
13
+ );
14
+
15
+ const UploadIcon: React.FC<{className?: string}> = ({ className }) => (
16
+ <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
17
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
18
+ </svg>
19
+ );
20
+
21
+
22
+ export const AudioInput: React.FC<AudioInputProps> = ({ onAudioSubmit, isProcessing }) => {
23
+ const [isRecording, setIsRecording] = useState(false);
24
+ const [audioURL, setAudioURL] = useState<string | null>(null);
25
+ const [audioBlob, setAudioBlob] = useState<Blob | null>(null);
26
+ const [mimeType, setMimeType] = useState<string>('audio/webm');
27
+ const mediaRecorderRef = useRef<MediaRecorder | null>(null);
28
+ const audioChunksRef = useRef<Blob[]>([]);
29
+ const [recordTime, setRecordTime] = useState(0);
30
+ const timerIntervalRef = useRef<number | null>(null);
31
+
32
+ const formatTime = (seconds: number) => {
33
+ const minutes = Math.floor(seconds / 60);
34
+ const secs = seconds % 60;
35
+ return `${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
36
+ };
37
+
38
+ useEffect(() => {
39
+ return () => {
40
+ if(timerIntervalRef.current) clearInterval(timerIntervalRef.current);
41
+ }
42
+ }, []);
43
+
44
+ const handleStartRecording = async () => {
45
+ try {
46
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
47
+ setIsRecording(true);
48
+ setAudioURL(null);
49
+ setAudioBlob(null);
50
+
51
+ let options = { mimeType: 'audio/webm' };
52
+ if (!MediaRecorder.isTypeSupported('audio/webm')) {
53
+ options = { mimeType: 'audio/mp4' };
54
+ }
55
+ setMimeType(options.mimeType);
56
+
57
+ mediaRecorderRef.current = new MediaRecorder(stream, options);
58
+ mediaRecorderRef.current.ondataavailable = (event) => {
59
+ audioChunksRef.current.push(event.data);
60
+ };
61
+ mediaRecorderRef.current.onstop = () => {
62
+ const blob = new Blob(audioChunksRef.current, { type: options.mimeType });
63
+ const url = URL.createObjectURL(blob);
64
+ setAudioBlob(blob);
65
+ setAudioURL(url);
66
+ audioChunksRef.current = [];
67
+ stream.getTracks().forEach(track => track.stop()); // Stop microphone access
68
+ };
69
+ mediaRecorderRef.current.start();
70
+ setRecordTime(0);
71
+ timerIntervalRef.current = window.setInterval(() => {
72
+ setRecordTime(prev => prev + 1);
73
+ }, 1000);
74
+
75
+ } catch (err) {
76
+ console.error("Error accessing microphone:", err);
77
+ alert("Could not access microphone. Please check your browser permissions.");
78
+ }
79
+ };
80
+
81
+ const handleStopRecording = () => {
82
+ if (mediaRecorderRef.current) {
83
+ mediaRecorderRef.current.stop();
84
+ setIsRecording(false);
85
+ if (timerIntervalRef.current) clearInterval(timerIntervalRef.current);
86
+ }
87
+ };
88
+
89
+ const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
90
+ const file = event.target.files?.[0];
91
+ if (file && file.type.startsWith('audio/')) {
92
+ const url = URL.createObjectURL(file);
93
+ setAudioBlob(file);
94
+ setMimeType(file.type);
95
+ setAudioURL(url);
96
+ } else {
97
+ alert("Please upload a valid audio file.");
98
+ }
99
+ };
100
+
101
+ const handleSubmit = () => {
102
+ if(audioBlob) {
103
+ onAudioSubmit(audioBlob, mimeType);
104
+ }
105
+ };
106
+
107
+ const handleReset = () => {
108
+ setAudioBlob(null);
109
+ setAudioURL(null);
110
+ setIsRecording(false);
111
+ setRecordTime(0);
112
+ if(mediaRecorderRef.current && mediaRecorderRef.current.state === 'recording') {
113
+ mediaRecorderRef.current.stop();
114
+ }
115
+ if (timerIntervalRef.current) clearInterval(timerIntervalRef.current);
116
+ };
117
+
118
+
119
+ return (
120
+ <div className="p-6 md:p-8 space-y-6">
121
+ <div>
122
+ <h2 className="text-2xl font-semibold text-center text-gray-100">Submit Your Voice Sample</h2>
123
+ <p className="text-center text-gray-400 mt-2">Record up to 3 minutes of audio or upload a file.</p>
124
+ </div>
125
+ <div className="flex flex-col md:flex-row items-center justify-center gap-4">
126
+ {isRecording ? (
127
+ <button
128
+ onClick={handleStopRecording}
129
+ className="w-full md:w-auto flex-1 flex items-center justify-center gap-3 px-6 py-3 bg-red-600 text-white font-semibold rounded-lg hover:bg-red-700 transition-colors"
130
+ >
131
+ <span className="relative flex h-3 w-3">
132
+ <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-white opacity-75"></span>
133
+ <span className="relative inline-flex rounded-full h-3 w-3 bg-white"></span>
134
+ </span>
135
+ Stop Recording ({formatTime(recordTime)})
136
+ </button>
137
+ ) : (
138
+ <button
139
+ onClick={handleStartRecording}
140
+ disabled={!!audioBlob}
141
+ className="w-full md:w-auto flex-1 flex items-center justify-center gap-3 px-6 py-3 bg-brand-blue text-white font-semibold rounded-lg hover:bg-opacity-80 transition-all disabled:bg-gray-600 disabled:cursor-not-allowed"
142
+ >
143
+ <MicrophoneIcon className="w-6 h-6"/>
144
+ Record Voice
145
+ </button>
146
+ )}
147
+ <span className="text-gray-500">OR</span>
148
+ <label className={`w-full md:w-auto flex-1 flex items-center justify-center gap-3 px-6 py-3 bg-gray-700 text-white font-semibold rounded-lg transition-colors ${!!audioBlob ? 'cursor-not-allowed bg-gray-600' : 'cursor-pointer hover:bg-gray-600'}`}>
149
+ <UploadIcon className="w-6 h-6"/>
150
+ Upload File
151
+ <input type="file" accept="audio/*" className="hidden" onChange={handleFileUpload} disabled={isRecording || !!audioBlob} />
152
+ </label>
153
+ </div>
154
+
155
+ {audioURL && (
156
+ <div className="mt-6 p-4 bg-gray-900/50 rounded-lg flex flex-col items-center gap-4">
157
+ <p className="font-semibold">Your Audio Sample:</p>
158
+ <audio controls src={audioURL} className="w-full max-w-md"></audio>
159
+ <div className="flex gap-4 mt-2">
160
+ <button
161
+ onClick={handleSubmit}
162
+ disabled={isProcessing}
163
+ className="px-8 py-2 bg-gradient-to-r from-brand-blue to-brand-purple text-white font-bold rounded-lg hover:opacity-90 transition-opacity disabled:opacity-50 disabled:cursor-wait"
164
+ >
165
+ {isProcessing ? 'Processing...' : 'Generate EQ'}
166
+ </button>
167
+ <button
168
+ onClick={handleReset}
169
+ disabled={isProcessing}
170
+ className="px-6 py-2 bg-gray-600 text-white font-semibold rounded-lg hover:bg-gray-500 transition-colors disabled:opacity-50"
171
+ >
172
+ Reset
173
+ </button>
174
+ </div>
175
+ </div>
176
+ )}
177
+ </div>
178
+ );
179
+ };
components/ResultsView.tsx ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useEffect, useRef } from 'react';
2
+ import type { VocalProfile, EQSetting } from '../types';
3
+
4
+ // Let TypeScript know D3 is available on the global scope
5
+ declare const d3: any;
6
+
7
+ interface ResultsViewProps {
8
+ vocalProfile: VocalProfile;
9
+ eqSettings: EQSetting[];
10
+ audioBlob: Blob;
11
+ audacityXml: string;
12
+ onReset: () => void;
13
+ }
14
+
15
+ const FrequencyVisualizer: React.FC<{ audioBlob: Blob; eqSettings: EQSetting[] }> = ({ audioBlob, eqSettings }) => {
16
+ const d3Container = useRef<SVGSVGElement | null>(null);
17
+
18
+ useEffect(() => {
19
+ if (!audioBlob || !d3Container.current) return;
20
+ let audioContext: AudioContext | null = null;
21
+
22
+ const processAudio = async () => {
23
+ audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
24
+ const arrayBuffer = await audioBlob.arrayBuffer();
25
+ let audioBuffer;
26
+ try {
27
+ audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
28
+ } catch (e) {
29
+ console.error("Error decoding audio data:", e);
30
+ drawChart(new Uint8Array(0), 0, "Error decoding audio file.");
31
+ if (audioContext) audioContext.close();
32
+ return;
33
+ }
34
+
35
+ const source = audioContext.createBufferSource();
36
+ source.buffer = audioBuffer;
37
+
38
+ const analyser = audioContext.createAnalyser();
39
+ analyser.fftSize = 2048;
40
+
41
+ source.connect(analyser);
42
+ // Not connecting to destination to play silently
43
+
44
+ source.start(0, Math.min(0.1, audioBuffer.duration), 5); // Start at 0.1s, play for 5s max
45
+
46
+ setTimeout(() => {
47
+ if(!audioContext || audioContext.state === 'closed') return;
48
+
49
+ const bufferLength = analyser.frequencyBinCount;
50
+ const dataArray = new Uint8Array(bufferLength);
51
+ analyser.getByteFrequencyData(dataArray);
52
+
53
+ source.stop();
54
+ if (dataArray.some(d => d > 0)) {
55
+ drawChart(dataArray, audioBuffer.sampleRate);
56
+ } else {
57
+ console.warn("Analyser returned no frequency data. The audio might be silent at the snapshot point.");
58
+ drawChart(new Uint8Array(0), 0, "Could not visualize audio: sample may be silent.");
59
+ }
60
+ audioContext.close();
61
+ }, 100); // Take snapshot after 100ms
62
+ };
63
+
64
+ const drawChart = (dataArray: Uint8Array, sampleRate: number, message?: string) => {
65
+ const svg = d3.select(d3Container.current);
66
+ svg.selectAll("*").remove();
67
+
68
+ const svgNode = d3Container.current;
69
+ if (!svgNode) return;
70
+
71
+ const { width: containerWidth, height: containerHeight } = svgNode.getBoundingClientRect();
72
+
73
+ const margin = { top: 20, right: 20, bottom: 40, left: 50 };
74
+ const width = containerWidth - margin.left - margin.right;
75
+ const height = containerHeight - margin.top - margin.bottom;
76
+
77
+ if (width <= 0 || height <= 0) return;
78
+
79
+ const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
80
+
81
+ const x = d3.scaleLog().domain([20, 20000]).range([0, width]);
82
+ const y = d3.scaleLinear().domain([0, 255]).range([height, 0]);
83
+
84
+ g.append("g")
85
+ .attr("transform", `translate(0,${height})`)
86
+ .call(d3.axisBottom(x).ticks(5, ".0s").tickSizeOuter(0))
87
+ .selectAll("text")
88
+ .style("fill", "#9ca3af");
89
+
90
+ g.append("g")
91
+ .call(d3.axisLeft(y).ticks(5).tickSizeOuter(0))
92
+ .selectAll("text")
93
+ .style("fill", "#9ca3af");
94
+
95
+ g.append("text")
96
+ .attr("text-anchor", "middle")
97
+ .attr("x", width/2)
98
+ .attr("y", height + margin.bottom - 5)
99
+ .style("fill", "#9ca3af")
100
+ .text("Frequency (Hz)");
101
+
102
+ g.append("text")
103
+ .attr("text-anchor", "middle")
104
+ .attr("transform", "rotate(-90)")
105
+ .attr("y", -margin.left + 15)
106
+ .attr("x", -height/2)
107
+ .style("fill", "#9ca3af")
108
+ .text("Amplitude");
109
+
110
+ if (dataArray.length === 0 || !sampleRate) {
111
+ g.append("text")
112
+ .attr("x", width / 2)
113
+ .attr("y", height / 2)
114
+ .attr("text-anchor", "middle")
115
+ .style("fill", "#e5e7eb")
116
+ .text(message || "No audio data to display.");
117
+ return;
118
+ }
119
+
120
+ const data = Array.from(dataArray).map((d, i) => ({
121
+ frequency: (i * sampleRate) / (2 * dataArray.length),
122
+ amplitude: d,
123
+ })).filter(d => d.frequency >= 20 && d.frequency <= 20000);
124
+
125
+ const barWidth = width / data.length;
126
+
127
+ g.selectAll(".bar")
128
+ .data(data)
129
+ .enter()
130
+ .append("rect")
131
+ .attr("class", "bar")
132
+ .attr("x", d => x(d.frequency))
133
+ .attr("y", d => y(d.amplitude))
134
+ .attr("width", barWidth > 0 ? barWidth : 1)
135
+ .attr("height", d => height - y(d.amplitude))
136
+ .attr("fill", d => {
137
+ const closestEq = eqSettings.find(eq => Math.abs(Math.log10(eq.frequency) - Math.log10(d.frequency)) < 0.1);
138
+ if(closestEq) {
139
+ return closestEq.gain > 0 ? '#22c55e' : '#ef4444';
140
+ }
141
+ return '#00BFFF';
142
+ });
143
+ };
144
+
145
+ processAudio().catch(console.error);
146
+
147
+ return () => {
148
+ if (audioContext && audioContext.state !== 'closed') {
149
+ audioContext.close();
150
+ }
151
+ };
152
+ }, [audioBlob, eqSettings]);
153
+
154
+ return (
155
+ <div className="bg-gray-900/50 p-4 rounded-lg">
156
+ <h3 className="text-xl font-semibold mb-2 text-gray-200">Vocal Frequency Snapshot</h3>
157
+ <svg ref={d3Container} className="w-full h-64 md:h-80"></svg>
158
+ </div>
159
+ );
160
+ };
161
+
162
+
163
+ export const ResultsView: React.FC<ResultsViewProps> = ({ vocalProfile, eqSettings, audioBlob, audacityXml, onReset }) => {
164
+ const handleCopyJson = () => {
165
+ navigator.clipboard.writeText(JSON.stringify({ vocalProfile, eqSettings }, null, 2));
166
+ alert("EQ settings copied to clipboard as JSON!");
167
+ };
168
+
169
+ const handleDownloadXml = () => {
170
+ const blob = new Blob([audacityXml], { type: 'application/xml' });
171
+ const url = URL.createObjectURL(blob);
172
+ const a = document.createElement('a');
173
+ a.href = url;
174
+ a.download = 'gemini-eq-preset.xml';
175
+ document.body.appendChild(a);
176
+ a.click();
177
+ document.body.removeChild(a);
178
+ URL.revokeObjectURL(url);
179
+ };
180
+
181
+ return (
182
+ <div className="p-6 md:p-8 space-y-8 animate-fade-in">
183
+ <div>
184
+ <h2 className="text-3xl font-bold text-center bg-clip-text text-transparent bg-gradient-to-r from-brand-blue to-brand-purple">Analysis Complete</h2>
185
+ </div>
186
+ <div className="grid md:grid-cols-2 gap-8">
187
+ <div className="space-y-6">
188
+ <div>
189
+ <h3 className="text-xl font-semibold mb-2 text-gray-200">Vocal Profile</h3>
190
+ <p className="text-gray-300 bg-gray-900/50 p-4 rounded-lg">{vocalProfile.description}</p>
191
+ </div>
192
+ <div>
193
+ <h3 className="text-xl font-semibold mb-2 text-gray-200">Key Characteristics</h3>
194
+ <ul className="list-disc list-inside bg-gray-900/50 p-4 rounded-lg text-gray-300 space-y-1">
195
+ <li><strong>Fundamental Range:</strong> {vocalProfile.fundamentalRange}</li>
196
+ {vocalProfile.keyCharacteristics.map((char, i) => (
197
+ <li key={i}>{char}</li>
198
+ ))}
199
+ </ul>
200
+ </div>
201
+ </div>
202
+
203
+ <div className="space-y-6">
204
+ <div>
205
+ <h3 className="text-xl font-semibold mb-2 text-gray-200">Generated EQ Preset</h3>
206
+ <div className="bg-gray-900/50 p-4 rounded-lg">
207
+ <table className="w-full text-left">
208
+ <thead className="border-b border-gray-600">
209
+ <tr>
210
+ <th className="py-2 px-2">Frequency</th>
211
+ <th className="py-2 px-2">Gain (dB)</th>
212
+ <th className="py-2 px-2">Action</th>
213
+ </tr>
214
+ </thead>
215
+ <tbody>
216
+ {eqSettings.map(({ frequency, gain }, i) => (
217
+ <tr key={i} className="border-b border-gray-700 last:border-0">
218
+ <td className="py-2 px-2">{frequency} Hz</td>
219
+ <td className={`py-2 px-2 font-mono ${gain > 0 ? 'text-green-400' : gain < 0 ? 'text-red-400' : 'text-gray-300'}`}>
220
+ {gain > 0 ? '+' : ''}{gain.toFixed(1)}
221
+ </td>
222
+ <td className="py-2 px-2">
223
+ <span className={`px-2 py-1 text-xs rounded-full ${gain > 0.1 ? 'bg-green-800/50 text-green-300' : gain < -0.1 ? 'bg-red-800/50 text-red-300' : 'bg-gray-600 text-gray-300'}`}>
224
+ {gain > 0.1 ? 'Boost' : gain < -0.1 ? 'Cut' : 'Neutral'}
225
+ </span>
226
+ </td>
227
+ </tr>
228
+ ))}
229
+ </tbody>
230
+ </table>
231
+ </div>
232
+ </div>
233
+ </div>
234
+ </div>
235
+
236
+ <FrequencyVisualizer audioBlob={audioBlob} eqSettings={eqSettings} />
237
+
238
+ <div className="flex flex-wrap justify-center gap-4 pt-6">
239
+ <button
240
+ onClick={onReset}
241
+ className="px-6 py-2 bg-gray-600 text-white font-semibold rounded-lg hover:bg-gray-500 transition-colors"
242
+ >
243
+ Analyze Another
244
+ </button>
245
+ <button
246
+ onClick={handleDownloadXml}
247
+ className="px-6 py-2 bg-green-600 text-white font-semibold rounded-lg hover:bg-green-700 transition-colors"
248
+ >
249
+ Download Audacity XML
250
+ </button>
251
+ <button
252
+ onClick={handleCopyJson}
253
+ className="px-6 py-2 bg-brand-blue text-white font-semibold rounded-lg hover:bg-opacity-80 transition-opacity"
254
+ >
255
+ Copy as JSON
256
+ </button>
257
+ </div>
258
+ </div>
259
+ );
260
+ };
index.html CHANGED
@@ -1,19 +1,38 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>EQ Template Creator - AI Vocal EQ</title>
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://d3js.org/d3.v7.min.js"></script>
10
+ <script>
11
+ tailwind.config = {
12
+ theme: {
13
+ extend: {
14
+ colors: {
15
+ 'brand-blue': '#00BFFF',
16
+ 'brand-purple': '#8A2BE2',
17
+ },
18
+ },
19
+ },
20
+ }
21
+ </script>
22
+ <script type="importmap">
23
+ {
24
+ "imports": {
25
+ "@google/genai": "https://aistudiocdn.com/@google/genai@^1.27.0",
26
+ "react/": "https://aistudiocdn.com/react@^19.2.0/",
27
+ "react": "https://aistudiocdn.com/react@^19.2.0",
28
+ "react-dom/": "https://aistudiocdn.com/react-dom@^19.2.0/"
29
+ }
30
+ }
31
+ </script>
32
+ <link rel="stylesheet" href="/index.css">
33
+ </head>
34
+ <body class="bg-gray-900 text-gray-200 font-sans antialiased">
35
+ <div id="root"></div>
36
+ <script type="module" src="/index.tsx"></script>
37
+ </body>
38
+ </html>
index.tsx ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import ReactDOM from 'react-dom/client';
4
+ import App from './App';
5
+
6
+ const rootElement = document.getElementById('root');
7
+ if (!rootElement) {
8
+ throw new Error("Could not find root element to mount to");
9
+ }
10
+
11
+ const root = ReactDOM.createRoot(rootElement);
12
+ root.render(
13
+ <React.StrictMode>
14
+ <App />
15
+ </React.StrictMode>
16
+ );
metadata.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "EQ Template Creator",
3
+ "description": "An intelligent audio engineering assistant that analyzes your voice using Gemini to generate custom EQ presets. Upload or record an audio sample, and receive a professional EQ configuration and a visual analysis of your vocal frequencies.",
4
+ "requestFramePermissions": [
5
+ "microphone"
6
+ ]
7
+ }
package-lock.json ADDED
@@ -0,0 +1,2083 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "eq-template-creator",
3
+ "version": "0.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "eq-template-creator",
9
+ "version": "0.0.0",
10
+ "dependencies": {
11
+ "@google/genai": "^1.27.0",
12
+ "react": "^19.2.0",
13
+ "react-dom": "^19.2.0"
14
+ },
15
+ "devDependencies": {
16
+ "@types/node": "^22.14.0",
17
+ "@vitejs/plugin-react": "^5.0.0",
18
+ "typescript": "~5.8.2",
19
+ "vite": "^6.2.0"
20
+ }
21
+ },
22
+ "node_modules/@babel/code-frame": {
23
+ "version": "7.27.1",
24
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
25
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
26
+ "dev": true,
27
+ "license": "MIT",
28
+ "dependencies": {
29
+ "@babel/helper-validator-identifier": "^7.27.1",
30
+ "js-tokens": "^4.0.0",
31
+ "picocolors": "^1.1.1"
32
+ },
33
+ "engines": {
34
+ "node": ">=6.9.0"
35
+ }
36
+ },
37
+ "node_modules/@babel/compat-data": {
38
+ "version": "7.28.5",
39
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
40
+ "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
41
+ "dev": true,
42
+ "license": "MIT",
43
+ "engines": {
44
+ "node": ">=6.9.0"
45
+ }
46
+ },
47
+ "node_modules/@babel/core": {
48
+ "version": "7.28.5",
49
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
50
+ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
51
+ "dev": true,
52
+ "license": "MIT",
53
+ "dependencies": {
54
+ "@babel/code-frame": "^7.27.1",
55
+ "@babel/generator": "^7.28.5",
56
+ "@babel/helper-compilation-targets": "^7.27.2",
57
+ "@babel/helper-module-transforms": "^7.28.3",
58
+ "@babel/helpers": "^7.28.4",
59
+ "@babel/parser": "^7.28.5",
60
+ "@babel/template": "^7.27.2",
61
+ "@babel/traverse": "^7.28.5",
62
+ "@babel/types": "^7.28.5",
63
+ "@jridgewell/remapping": "^2.3.5",
64
+ "convert-source-map": "^2.0.0",
65
+ "debug": "^4.1.0",
66
+ "gensync": "^1.0.0-beta.2",
67
+ "json5": "^2.2.3",
68
+ "semver": "^6.3.1"
69
+ },
70
+ "engines": {
71
+ "node": ">=6.9.0"
72
+ },
73
+ "funding": {
74
+ "type": "opencollective",
75
+ "url": "https://opencollective.com/babel"
76
+ }
77
+ },
78
+ "node_modules/@babel/generator": {
79
+ "version": "7.28.5",
80
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
81
+ "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
82
+ "dev": true,
83
+ "license": "MIT",
84
+ "dependencies": {
85
+ "@babel/parser": "^7.28.5",
86
+ "@babel/types": "^7.28.5",
87
+ "@jridgewell/gen-mapping": "^0.3.12",
88
+ "@jridgewell/trace-mapping": "^0.3.28",
89
+ "jsesc": "^3.0.2"
90
+ },
91
+ "engines": {
92
+ "node": ">=6.9.0"
93
+ }
94
+ },
95
+ "node_modules/@babel/helper-compilation-targets": {
96
+ "version": "7.27.2",
97
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
98
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
99
+ "dev": true,
100
+ "license": "MIT",
101
+ "dependencies": {
102
+ "@babel/compat-data": "^7.27.2",
103
+ "@babel/helper-validator-option": "^7.27.1",
104
+ "browserslist": "^4.24.0",
105
+ "lru-cache": "^5.1.1",
106
+ "semver": "^6.3.1"
107
+ },
108
+ "engines": {
109
+ "node": ">=6.9.0"
110
+ }
111
+ },
112
+ "node_modules/@babel/helper-globals": {
113
+ "version": "7.28.0",
114
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
115
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
116
+ "dev": true,
117
+ "license": "MIT",
118
+ "engines": {
119
+ "node": ">=6.9.0"
120
+ }
121
+ },
122
+ "node_modules/@babel/helper-module-imports": {
123
+ "version": "7.27.1",
124
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
125
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
126
+ "dev": true,
127
+ "license": "MIT",
128
+ "dependencies": {
129
+ "@babel/traverse": "^7.27.1",
130
+ "@babel/types": "^7.27.1"
131
+ },
132
+ "engines": {
133
+ "node": ">=6.9.0"
134
+ }
135
+ },
136
+ "node_modules/@babel/helper-module-transforms": {
137
+ "version": "7.28.3",
138
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
139
+ "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
140
+ "dev": true,
141
+ "license": "MIT",
142
+ "dependencies": {
143
+ "@babel/helper-module-imports": "^7.27.1",
144
+ "@babel/helper-validator-identifier": "^7.27.1",
145
+ "@babel/traverse": "^7.28.3"
146
+ },
147
+ "engines": {
148
+ "node": ">=6.9.0"
149
+ },
150
+ "peerDependencies": {
151
+ "@babel/core": "^7.0.0"
152
+ }
153
+ },
154
+ "node_modules/@babel/helper-plugin-utils": {
155
+ "version": "7.27.1",
156
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
157
+ "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
158
+ "dev": true,
159
+ "license": "MIT",
160
+ "engines": {
161
+ "node": ">=6.9.0"
162
+ }
163
+ },
164
+ "node_modules/@babel/helper-string-parser": {
165
+ "version": "7.27.1",
166
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
167
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
168
+ "dev": true,
169
+ "license": "MIT",
170
+ "engines": {
171
+ "node": ">=6.9.0"
172
+ }
173
+ },
174
+ "node_modules/@babel/helper-validator-identifier": {
175
+ "version": "7.28.5",
176
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
177
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
178
+ "dev": true,
179
+ "license": "MIT",
180
+ "engines": {
181
+ "node": ">=6.9.0"
182
+ }
183
+ },
184
+ "node_modules/@babel/helper-validator-option": {
185
+ "version": "7.27.1",
186
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
187
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
188
+ "dev": true,
189
+ "license": "MIT",
190
+ "engines": {
191
+ "node": ">=6.9.0"
192
+ }
193
+ },
194
+ "node_modules/@babel/helpers": {
195
+ "version": "7.28.4",
196
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
197
+ "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
198
+ "dev": true,
199
+ "license": "MIT",
200
+ "dependencies": {
201
+ "@babel/template": "^7.27.2",
202
+ "@babel/types": "^7.28.4"
203
+ },
204
+ "engines": {
205
+ "node": ">=6.9.0"
206
+ }
207
+ },
208
+ "node_modules/@babel/parser": {
209
+ "version": "7.28.5",
210
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
211
+ "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
212
+ "dev": true,
213
+ "license": "MIT",
214
+ "dependencies": {
215
+ "@babel/types": "^7.28.5"
216
+ },
217
+ "bin": {
218
+ "parser": "bin/babel-parser.js"
219
+ },
220
+ "engines": {
221
+ "node": ">=6.0.0"
222
+ }
223
+ },
224
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
225
+ "version": "7.27.1",
226
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
227
+ "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
228
+ "dev": true,
229
+ "license": "MIT",
230
+ "dependencies": {
231
+ "@babel/helper-plugin-utils": "^7.27.1"
232
+ },
233
+ "engines": {
234
+ "node": ">=6.9.0"
235
+ },
236
+ "peerDependencies": {
237
+ "@babel/core": "^7.0.0-0"
238
+ }
239
+ },
240
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
241
+ "version": "7.27.1",
242
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
243
+ "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
244
+ "dev": true,
245
+ "license": "MIT",
246
+ "dependencies": {
247
+ "@babel/helper-plugin-utils": "^7.27.1"
248
+ },
249
+ "engines": {
250
+ "node": ">=6.9.0"
251
+ },
252
+ "peerDependencies": {
253
+ "@babel/core": "^7.0.0-0"
254
+ }
255
+ },
256
+ "node_modules/@babel/template": {
257
+ "version": "7.27.2",
258
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
259
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
260
+ "dev": true,
261
+ "license": "MIT",
262
+ "dependencies": {
263
+ "@babel/code-frame": "^7.27.1",
264
+ "@babel/parser": "^7.27.2",
265
+ "@babel/types": "^7.27.1"
266
+ },
267
+ "engines": {
268
+ "node": ">=6.9.0"
269
+ }
270
+ },
271
+ "node_modules/@babel/traverse": {
272
+ "version": "7.28.5",
273
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
274
+ "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
275
+ "dev": true,
276
+ "license": "MIT",
277
+ "dependencies": {
278
+ "@babel/code-frame": "^7.27.1",
279
+ "@babel/generator": "^7.28.5",
280
+ "@babel/helper-globals": "^7.28.0",
281
+ "@babel/parser": "^7.28.5",
282
+ "@babel/template": "^7.27.2",
283
+ "@babel/types": "^7.28.5",
284
+ "debug": "^4.3.1"
285
+ },
286
+ "engines": {
287
+ "node": ">=6.9.0"
288
+ }
289
+ },
290
+ "node_modules/@babel/types": {
291
+ "version": "7.28.5",
292
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
293
+ "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
294
+ "dev": true,
295
+ "license": "MIT",
296
+ "dependencies": {
297
+ "@babel/helper-string-parser": "^7.27.1",
298
+ "@babel/helper-validator-identifier": "^7.28.5"
299
+ },
300
+ "engines": {
301
+ "node": ">=6.9.0"
302
+ }
303
+ },
304
+ "node_modules/@esbuild/aix-ppc64": {
305
+ "version": "0.25.11",
306
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz",
307
+ "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==",
308
+ "cpu": [
309
+ "ppc64"
310
+ ],
311
+ "dev": true,
312
+ "license": "MIT",
313
+ "optional": true,
314
+ "os": [
315
+ "aix"
316
+ ],
317
+ "engines": {
318
+ "node": ">=18"
319
+ }
320
+ },
321
+ "node_modules/@esbuild/android-arm": {
322
+ "version": "0.25.11",
323
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz",
324
+ "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==",
325
+ "cpu": [
326
+ "arm"
327
+ ],
328
+ "dev": true,
329
+ "license": "MIT",
330
+ "optional": true,
331
+ "os": [
332
+ "android"
333
+ ],
334
+ "engines": {
335
+ "node": ">=18"
336
+ }
337
+ },
338
+ "node_modules/@esbuild/android-arm64": {
339
+ "version": "0.25.11",
340
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz",
341
+ "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==",
342
+ "cpu": [
343
+ "arm64"
344
+ ],
345
+ "dev": true,
346
+ "license": "MIT",
347
+ "optional": true,
348
+ "os": [
349
+ "android"
350
+ ],
351
+ "engines": {
352
+ "node": ">=18"
353
+ }
354
+ },
355
+ "node_modules/@esbuild/android-x64": {
356
+ "version": "0.25.11",
357
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz",
358
+ "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==",
359
+ "cpu": [
360
+ "x64"
361
+ ],
362
+ "dev": true,
363
+ "license": "MIT",
364
+ "optional": true,
365
+ "os": [
366
+ "android"
367
+ ],
368
+ "engines": {
369
+ "node": ">=18"
370
+ }
371
+ },
372
+ "node_modules/@esbuild/darwin-arm64": {
373
+ "version": "0.25.11",
374
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz",
375
+ "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==",
376
+ "cpu": [
377
+ "arm64"
378
+ ],
379
+ "dev": true,
380
+ "license": "MIT",
381
+ "optional": true,
382
+ "os": [
383
+ "darwin"
384
+ ],
385
+ "engines": {
386
+ "node": ">=18"
387
+ }
388
+ },
389
+ "node_modules/@esbuild/darwin-x64": {
390
+ "version": "0.25.11",
391
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz",
392
+ "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==",
393
+ "cpu": [
394
+ "x64"
395
+ ],
396
+ "dev": true,
397
+ "license": "MIT",
398
+ "optional": true,
399
+ "os": [
400
+ "darwin"
401
+ ],
402
+ "engines": {
403
+ "node": ">=18"
404
+ }
405
+ },
406
+ "node_modules/@esbuild/freebsd-arm64": {
407
+ "version": "0.25.11",
408
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz",
409
+ "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==",
410
+ "cpu": [
411
+ "arm64"
412
+ ],
413
+ "dev": true,
414
+ "license": "MIT",
415
+ "optional": true,
416
+ "os": [
417
+ "freebsd"
418
+ ],
419
+ "engines": {
420
+ "node": ">=18"
421
+ }
422
+ },
423
+ "node_modules/@esbuild/freebsd-x64": {
424
+ "version": "0.25.11",
425
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz",
426
+ "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==",
427
+ "cpu": [
428
+ "x64"
429
+ ],
430
+ "dev": true,
431
+ "license": "MIT",
432
+ "optional": true,
433
+ "os": [
434
+ "freebsd"
435
+ ],
436
+ "engines": {
437
+ "node": ">=18"
438
+ }
439
+ },
440
+ "node_modules/@esbuild/linux-arm": {
441
+ "version": "0.25.11",
442
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz",
443
+ "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==",
444
+ "cpu": [
445
+ "arm"
446
+ ],
447
+ "dev": true,
448
+ "license": "MIT",
449
+ "optional": true,
450
+ "os": [
451
+ "linux"
452
+ ],
453
+ "engines": {
454
+ "node": ">=18"
455
+ }
456
+ },
457
+ "node_modules/@esbuild/linux-arm64": {
458
+ "version": "0.25.11",
459
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz",
460
+ "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==",
461
+ "cpu": [
462
+ "arm64"
463
+ ],
464
+ "dev": true,
465
+ "license": "MIT",
466
+ "optional": true,
467
+ "os": [
468
+ "linux"
469
+ ],
470
+ "engines": {
471
+ "node": ">=18"
472
+ }
473
+ },
474
+ "node_modules/@esbuild/linux-ia32": {
475
+ "version": "0.25.11",
476
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz",
477
+ "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==",
478
+ "cpu": [
479
+ "ia32"
480
+ ],
481
+ "dev": true,
482
+ "license": "MIT",
483
+ "optional": true,
484
+ "os": [
485
+ "linux"
486
+ ],
487
+ "engines": {
488
+ "node": ">=18"
489
+ }
490
+ },
491
+ "node_modules/@esbuild/linux-loong64": {
492
+ "version": "0.25.11",
493
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz",
494
+ "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==",
495
+ "cpu": [
496
+ "loong64"
497
+ ],
498
+ "dev": true,
499
+ "license": "MIT",
500
+ "optional": true,
501
+ "os": [
502
+ "linux"
503
+ ],
504
+ "engines": {
505
+ "node": ">=18"
506
+ }
507
+ },
508
+ "node_modules/@esbuild/linux-mips64el": {
509
+ "version": "0.25.11",
510
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz",
511
+ "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==",
512
+ "cpu": [
513
+ "mips64el"
514
+ ],
515
+ "dev": true,
516
+ "license": "MIT",
517
+ "optional": true,
518
+ "os": [
519
+ "linux"
520
+ ],
521
+ "engines": {
522
+ "node": ">=18"
523
+ }
524
+ },
525
+ "node_modules/@esbuild/linux-ppc64": {
526
+ "version": "0.25.11",
527
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz",
528
+ "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==",
529
+ "cpu": [
530
+ "ppc64"
531
+ ],
532
+ "dev": true,
533
+ "license": "MIT",
534
+ "optional": true,
535
+ "os": [
536
+ "linux"
537
+ ],
538
+ "engines": {
539
+ "node": ">=18"
540
+ }
541
+ },
542
+ "node_modules/@esbuild/linux-riscv64": {
543
+ "version": "0.25.11",
544
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz",
545
+ "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==",
546
+ "cpu": [
547
+ "riscv64"
548
+ ],
549
+ "dev": true,
550
+ "license": "MIT",
551
+ "optional": true,
552
+ "os": [
553
+ "linux"
554
+ ],
555
+ "engines": {
556
+ "node": ">=18"
557
+ }
558
+ },
559
+ "node_modules/@esbuild/linux-s390x": {
560
+ "version": "0.25.11",
561
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz",
562
+ "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==",
563
+ "cpu": [
564
+ "s390x"
565
+ ],
566
+ "dev": true,
567
+ "license": "MIT",
568
+ "optional": true,
569
+ "os": [
570
+ "linux"
571
+ ],
572
+ "engines": {
573
+ "node": ">=18"
574
+ }
575
+ },
576
+ "node_modules/@esbuild/linux-x64": {
577
+ "version": "0.25.11",
578
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz",
579
+ "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==",
580
+ "cpu": [
581
+ "x64"
582
+ ],
583
+ "dev": true,
584
+ "license": "MIT",
585
+ "optional": true,
586
+ "os": [
587
+ "linux"
588
+ ],
589
+ "engines": {
590
+ "node": ">=18"
591
+ }
592
+ },
593
+ "node_modules/@esbuild/netbsd-arm64": {
594
+ "version": "0.25.11",
595
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz",
596
+ "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==",
597
+ "cpu": [
598
+ "arm64"
599
+ ],
600
+ "dev": true,
601
+ "license": "MIT",
602
+ "optional": true,
603
+ "os": [
604
+ "netbsd"
605
+ ],
606
+ "engines": {
607
+ "node": ">=18"
608
+ }
609
+ },
610
+ "node_modules/@esbuild/netbsd-x64": {
611
+ "version": "0.25.11",
612
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz",
613
+ "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==",
614
+ "cpu": [
615
+ "x64"
616
+ ],
617
+ "dev": true,
618
+ "license": "MIT",
619
+ "optional": true,
620
+ "os": [
621
+ "netbsd"
622
+ ],
623
+ "engines": {
624
+ "node": ">=18"
625
+ }
626
+ },
627
+ "node_modules/@esbuild/openbsd-arm64": {
628
+ "version": "0.25.11",
629
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz",
630
+ "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==",
631
+ "cpu": [
632
+ "arm64"
633
+ ],
634
+ "dev": true,
635
+ "license": "MIT",
636
+ "optional": true,
637
+ "os": [
638
+ "openbsd"
639
+ ],
640
+ "engines": {
641
+ "node": ">=18"
642
+ }
643
+ },
644
+ "node_modules/@esbuild/openbsd-x64": {
645
+ "version": "0.25.11",
646
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz",
647
+ "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==",
648
+ "cpu": [
649
+ "x64"
650
+ ],
651
+ "dev": true,
652
+ "license": "MIT",
653
+ "optional": true,
654
+ "os": [
655
+ "openbsd"
656
+ ],
657
+ "engines": {
658
+ "node": ">=18"
659
+ }
660
+ },
661
+ "node_modules/@esbuild/openharmony-arm64": {
662
+ "version": "0.25.11",
663
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz",
664
+ "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==",
665
+ "cpu": [
666
+ "arm64"
667
+ ],
668
+ "dev": true,
669
+ "license": "MIT",
670
+ "optional": true,
671
+ "os": [
672
+ "openharmony"
673
+ ],
674
+ "engines": {
675
+ "node": ">=18"
676
+ }
677
+ },
678
+ "node_modules/@esbuild/sunos-x64": {
679
+ "version": "0.25.11",
680
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz",
681
+ "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==",
682
+ "cpu": [
683
+ "x64"
684
+ ],
685
+ "dev": true,
686
+ "license": "MIT",
687
+ "optional": true,
688
+ "os": [
689
+ "sunos"
690
+ ],
691
+ "engines": {
692
+ "node": ">=18"
693
+ }
694
+ },
695
+ "node_modules/@esbuild/win32-arm64": {
696
+ "version": "0.25.11",
697
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz",
698
+ "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==",
699
+ "cpu": [
700
+ "arm64"
701
+ ],
702
+ "dev": true,
703
+ "license": "MIT",
704
+ "optional": true,
705
+ "os": [
706
+ "win32"
707
+ ],
708
+ "engines": {
709
+ "node": ">=18"
710
+ }
711
+ },
712
+ "node_modules/@esbuild/win32-ia32": {
713
+ "version": "0.25.11",
714
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz",
715
+ "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==",
716
+ "cpu": [
717
+ "ia32"
718
+ ],
719
+ "dev": true,
720
+ "license": "MIT",
721
+ "optional": true,
722
+ "os": [
723
+ "win32"
724
+ ],
725
+ "engines": {
726
+ "node": ">=18"
727
+ }
728
+ },
729
+ "node_modules/@esbuild/win32-x64": {
730
+ "version": "0.25.11",
731
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz",
732
+ "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==",
733
+ "cpu": [
734
+ "x64"
735
+ ],
736
+ "dev": true,
737
+ "license": "MIT",
738
+ "optional": true,
739
+ "os": [
740
+ "win32"
741
+ ],
742
+ "engines": {
743
+ "node": ">=18"
744
+ }
745
+ },
746
+ "node_modules/@google/genai": {
747
+ "version": "1.27.0",
748
+ "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.27.0.tgz",
749
+ "integrity": "sha512-sveeQqwyzO/U5kOjo3EflF1rf7v0ZGprrjPGmeT6V5u22IUTcA4wBFxW+q1n7hOX0M1iWR3944MImoNPOM+zsA==",
750
+ "license": "Apache-2.0",
751
+ "dependencies": {
752
+ "google-auth-library": "^10.3.0",
753
+ "ws": "^8.18.0"
754
+ },
755
+ "engines": {
756
+ "node": ">=20.0.0"
757
+ },
758
+ "peerDependencies": {
759
+ "@modelcontextprotocol/sdk": "^1.20.1"
760
+ },
761
+ "peerDependenciesMeta": {
762
+ "@modelcontextprotocol/sdk": {
763
+ "optional": true
764
+ }
765
+ }
766
+ },
767
+ "node_modules/@jridgewell/gen-mapping": {
768
+ "version": "0.3.13",
769
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
770
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
771
+ "dev": true,
772
+ "license": "MIT",
773
+ "dependencies": {
774
+ "@jridgewell/sourcemap-codec": "^1.5.0",
775
+ "@jridgewell/trace-mapping": "^0.3.24"
776
+ }
777
+ },
778
+ "node_modules/@jridgewell/remapping": {
779
+ "version": "2.3.5",
780
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
781
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
782
+ "dev": true,
783
+ "license": "MIT",
784
+ "dependencies": {
785
+ "@jridgewell/gen-mapping": "^0.3.5",
786
+ "@jridgewell/trace-mapping": "^0.3.24"
787
+ }
788
+ },
789
+ "node_modules/@jridgewell/resolve-uri": {
790
+ "version": "3.1.2",
791
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
792
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
793
+ "dev": true,
794
+ "license": "MIT",
795
+ "engines": {
796
+ "node": ">=6.0.0"
797
+ }
798
+ },
799
+ "node_modules/@jridgewell/sourcemap-codec": {
800
+ "version": "1.5.5",
801
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
802
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
803
+ "dev": true,
804
+ "license": "MIT"
805
+ },
806
+ "node_modules/@jridgewell/trace-mapping": {
807
+ "version": "0.3.31",
808
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
809
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
810
+ "dev": true,
811
+ "license": "MIT",
812
+ "dependencies": {
813
+ "@jridgewell/resolve-uri": "^3.1.0",
814
+ "@jridgewell/sourcemap-codec": "^1.4.14"
815
+ }
816
+ },
817
+ "node_modules/@rolldown/pluginutils": {
818
+ "version": "1.0.0-beta.43",
819
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.43.tgz",
820
+ "integrity": "sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ==",
821
+ "dev": true,
822
+ "license": "MIT"
823
+ },
824
+ "node_modules/@rollup/rollup-android-arm-eabi": {
825
+ "version": "4.52.5",
826
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz",
827
+ "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==",
828
+ "cpu": [
829
+ "arm"
830
+ ],
831
+ "dev": true,
832
+ "license": "MIT",
833
+ "optional": true,
834
+ "os": [
835
+ "android"
836
+ ]
837
+ },
838
+ "node_modules/@rollup/rollup-android-arm64": {
839
+ "version": "4.52.5",
840
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz",
841
+ "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==",
842
+ "cpu": [
843
+ "arm64"
844
+ ],
845
+ "dev": true,
846
+ "license": "MIT",
847
+ "optional": true,
848
+ "os": [
849
+ "android"
850
+ ]
851
+ },
852
+ "node_modules/@rollup/rollup-darwin-arm64": {
853
+ "version": "4.52.5",
854
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz",
855
+ "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==",
856
+ "cpu": [
857
+ "arm64"
858
+ ],
859
+ "dev": true,
860
+ "license": "MIT",
861
+ "optional": true,
862
+ "os": [
863
+ "darwin"
864
+ ]
865
+ },
866
+ "node_modules/@rollup/rollup-darwin-x64": {
867
+ "version": "4.52.5",
868
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz",
869
+ "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==",
870
+ "cpu": [
871
+ "x64"
872
+ ],
873
+ "dev": true,
874
+ "license": "MIT",
875
+ "optional": true,
876
+ "os": [
877
+ "darwin"
878
+ ]
879
+ },
880
+ "node_modules/@rollup/rollup-freebsd-arm64": {
881
+ "version": "4.52.5",
882
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz",
883
+ "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==",
884
+ "cpu": [
885
+ "arm64"
886
+ ],
887
+ "dev": true,
888
+ "license": "MIT",
889
+ "optional": true,
890
+ "os": [
891
+ "freebsd"
892
+ ]
893
+ },
894
+ "node_modules/@rollup/rollup-freebsd-x64": {
895
+ "version": "4.52.5",
896
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz",
897
+ "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==",
898
+ "cpu": [
899
+ "x64"
900
+ ],
901
+ "dev": true,
902
+ "license": "MIT",
903
+ "optional": true,
904
+ "os": [
905
+ "freebsd"
906
+ ]
907
+ },
908
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
909
+ "version": "4.52.5",
910
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz",
911
+ "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==",
912
+ "cpu": [
913
+ "arm"
914
+ ],
915
+ "dev": true,
916
+ "license": "MIT",
917
+ "optional": true,
918
+ "os": [
919
+ "linux"
920
+ ]
921
+ },
922
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
923
+ "version": "4.52.5",
924
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz",
925
+ "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==",
926
+ "cpu": [
927
+ "arm"
928
+ ],
929
+ "dev": true,
930
+ "license": "MIT",
931
+ "optional": true,
932
+ "os": [
933
+ "linux"
934
+ ]
935
+ },
936
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
937
+ "version": "4.52.5",
938
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz",
939
+ "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==",
940
+ "cpu": [
941
+ "arm64"
942
+ ],
943
+ "dev": true,
944
+ "license": "MIT",
945
+ "optional": true,
946
+ "os": [
947
+ "linux"
948
+ ]
949
+ },
950
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
951
+ "version": "4.52.5",
952
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz",
953
+ "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==",
954
+ "cpu": [
955
+ "arm64"
956
+ ],
957
+ "dev": true,
958
+ "license": "MIT",
959
+ "optional": true,
960
+ "os": [
961
+ "linux"
962
+ ]
963
+ },
964
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
965
+ "version": "4.52.5",
966
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz",
967
+ "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==",
968
+ "cpu": [
969
+ "loong64"
970
+ ],
971
+ "dev": true,
972
+ "license": "MIT",
973
+ "optional": true,
974
+ "os": [
975
+ "linux"
976
+ ]
977
+ },
978
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
979
+ "version": "4.52.5",
980
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz",
981
+ "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==",
982
+ "cpu": [
983
+ "ppc64"
984
+ ],
985
+ "dev": true,
986
+ "license": "MIT",
987
+ "optional": true,
988
+ "os": [
989
+ "linux"
990
+ ]
991
+ },
992
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
993
+ "version": "4.52.5",
994
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz",
995
+ "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==",
996
+ "cpu": [
997
+ "riscv64"
998
+ ],
999
+ "dev": true,
1000
+ "license": "MIT",
1001
+ "optional": true,
1002
+ "os": [
1003
+ "linux"
1004
+ ]
1005
+ },
1006
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
1007
+ "version": "4.52.5",
1008
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz",
1009
+ "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==",
1010
+ "cpu": [
1011
+ "riscv64"
1012
+ ],
1013
+ "dev": true,
1014
+ "license": "MIT",
1015
+ "optional": true,
1016
+ "os": [
1017
+ "linux"
1018
+ ]
1019
+ },
1020
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
1021
+ "version": "4.52.5",
1022
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz",
1023
+ "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==",
1024
+ "cpu": [
1025
+ "s390x"
1026
+ ],
1027
+ "dev": true,
1028
+ "license": "MIT",
1029
+ "optional": true,
1030
+ "os": [
1031
+ "linux"
1032
+ ]
1033
+ },
1034
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
1035
+ "version": "4.52.5",
1036
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz",
1037
+ "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==",
1038
+ "cpu": [
1039
+ "x64"
1040
+ ],
1041
+ "dev": true,
1042
+ "license": "MIT",
1043
+ "optional": true,
1044
+ "os": [
1045
+ "linux"
1046
+ ]
1047
+ },
1048
+ "node_modules/@rollup/rollup-linux-x64-musl": {
1049
+ "version": "4.52.5",
1050
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz",
1051
+ "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==",
1052
+ "cpu": [
1053
+ "x64"
1054
+ ],
1055
+ "dev": true,
1056
+ "license": "MIT",
1057
+ "optional": true,
1058
+ "os": [
1059
+ "linux"
1060
+ ]
1061
+ },
1062
+ "node_modules/@rollup/rollup-openharmony-arm64": {
1063
+ "version": "4.52.5",
1064
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz",
1065
+ "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==",
1066
+ "cpu": [
1067
+ "arm64"
1068
+ ],
1069
+ "dev": true,
1070
+ "license": "MIT",
1071
+ "optional": true,
1072
+ "os": [
1073
+ "openharmony"
1074
+ ]
1075
+ },
1076
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
1077
+ "version": "4.52.5",
1078
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz",
1079
+ "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==",
1080
+ "cpu": [
1081
+ "arm64"
1082
+ ],
1083
+ "dev": true,
1084
+ "license": "MIT",
1085
+ "optional": true,
1086
+ "os": [
1087
+ "win32"
1088
+ ]
1089
+ },
1090
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
1091
+ "version": "4.52.5",
1092
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz",
1093
+ "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==",
1094
+ "cpu": [
1095
+ "ia32"
1096
+ ],
1097
+ "dev": true,
1098
+ "license": "MIT",
1099
+ "optional": true,
1100
+ "os": [
1101
+ "win32"
1102
+ ]
1103
+ },
1104
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
1105
+ "version": "4.52.5",
1106
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz",
1107
+ "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==",
1108
+ "cpu": [
1109
+ "x64"
1110
+ ],
1111
+ "dev": true,
1112
+ "license": "MIT",
1113
+ "optional": true,
1114
+ "os": [
1115
+ "win32"
1116
+ ]
1117
+ },
1118
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
1119
+ "version": "4.52.5",
1120
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz",
1121
+ "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==",
1122
+ "cpu": [
1123
+ "x64"
1124
+ ],
1125
+ "dev": true,
1126
+ "license": "MIT",
1127
+ "optional": true,
1128
+ "os": [
1129
+ "win32"
1130
+ ]
1131
+ },
1132
+ "node_modules/@types/babel__core": {
1133
+ "version": "7.20.5",
1134
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
1135
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
1136
+ "dev": true,
1137
+ "license": "MIT",
1138
+ "dependencies": {
1139
+ "@babel/parser": "^7.20.7",
1140
+ "@babel/types": "^7.20.7",
1141
+ "@types/babel__generator": "*",
1142
+ "@types/babel__template": "*",
1143
+ "@types/babel__traverse": "*"
1144
+ }
1145
+ },
1146
+ "node_modules/@types/babel__generator": {
1147
+ "version": "7.27.0",
1148
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
1149
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
1150
+ "dev": true,
1151
+ "license": "MIT",
1152
+ "dependencies": {
1153
+ "@babel/types": "^7.0.0"
1154
+ }
1155
+ },
1156
+ "node_modules/@types/babel__template": {
1157
+ "version": "7.4.4",
1158
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
1159
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
1160
+ "dev": true,
1161
+ "license": "MIT",
1162
+ "dependencies": {
1163
+ "@babel/parser": "^7.1.0",
1164
+ "@babel/types": "^7.0.0"
1165
+ }
1166
+ },
1167
+ "node_modules/@types/babel__traverse": {
1168
+ "version": "7.28.0",
1169
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
1170
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
1171
+ "dev": true,
1172
+ "license": "MIT",
1173
+ "dependencies": {
1174
+ "@babel/types": "^7.28.2"
1175
+ }
1176
+ },
1177
+ "node_modules/@types/estree": {
1178
+ "version": "1.0.8",
1179
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
1180
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
1181
+ "dev": true,
1182
+ "license": "MIT"
1183
+ },
1184
+ "node_modules/@types/node": {
1185
+ "version": "22.18.12",
1186
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.12.tgz",
1187
+ "integrity": "sha512-BICHQ67iqxQGFSzfCFTT7MRQ5XcBjG5aeKh5Ok38UBbPe5fxTyE+aHFxwVrGyr8GNlqFMLKD1D3P2K/1ks8tog==",
1188
+ "dev": true,
1189
+ "license": "MIT",
1190
+ "dependencies": {
1191
+ "undici-types": "~6.21.0"
1192
+ }
1193
+ },
1194
+ "node_modules/@vitejs/plugin-react": {
1195
+ "version": "5.1.0",
1196
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.0.tgz",
1197
+ "integrity": "sha512-4LuWrg7EKWgQaMJfnN+wcmbAW+VSsCmqGohftWjuct47bv8uE4n/nPpq4XjJPsxgq00GGG5J8dvBczp8uxScew==",
1198
+ "dev": true,
1199
+ "license": "MIT",
1200
+ "dependencies": {
1201
+ "@babel/core": "^7.28.4",
1202
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
1203
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
1204
+ "@rolldown/pluginutils": "1.0.0-beta.43",
1205
+ "@types/babel__core": "^7.20.5",
1206
+ "react-refresh": "^0.18.0"
1207
+ },
1208
+ "engines": {
1209
+ "node": "^20.19.0 || >=22.12.0"
1210
+ },
1211
+ "peerDependencies": {
1212
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
1213
+ }
1214
+ },
1215
+ "node_modules/agent-base": {
1216
+ "version": "7.1.4",
1217
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
1218
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
1219
+ "license": "MIT",
1220
+ "engines": {
1221
+ "node": ">= 14"
1222
+ }
1223
+ },
1224
+ "node_modules/base64-js": {
1225
+ "version": "1.5.1",
1226
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
1227
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
1228
+ "funding": [
1229
+ {
1230
+ "type": "github",
1231
+ "url": "https://github.com/sponsors/feross"
1232
+ },
1233
+ {
1234
+ "type": "patreon",
1235
+ "url": "https://www.patreon.com/feross"
1236
+ },
1237
+ {
1238
+ "type": "consulting",
1239
+ "url": "https://feross.org/support"
1240
+ }
1241
+ ],
1242
+ "license": "MIT"
1243
+ },
1244
+ "node_modules/baseline-browser-mapping": {
1245
+ "version": "2.8.20",
1246
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.20.tgz",
1247
+ "integrity": "sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ==",
1248
+ "dev": true,
1249
+ "license": "Apache-2.0",
1250
+ "bin": {
1251
+ "baseline-browser-mapping": "dist/cli.js"
1252
+ }
1253
+ },
1254
+ "node_modules/bignumber.js": {
1255
+ "version": "9.3.1",
1256
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
1257
+ "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
1258
+ "license": "MIT",
1259
+ "engines": {
1260
+ "node": "*"
1261
+ }
1262
+ },
1263
+ "node_modules/browserslist": {
1264
+ "version": "4.27.0",
1265
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz",
1266
+ "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==",
1267
+ "dev": true,
1268
+ "funding": [
1269
+ {
1270
+ "type": "opencollective",
1271
+ "url": "https://opencollective.com/browserslist"
1272
+ },
1273
+ {
1274
+ "type": "tidelift",
1275
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
1276
+ },
1277
+ {
1278
+ "type": "github",
1279
+ "url": "https://github.com/sponsors/ai"
1280
+ }
1281
+ ],
1282
+ "license": "MIT",
1283
+ "dependencies": {
1284
+ "baseline-browser-mapping": "^2.8.19",
1285
+ "caniuse-lite": "^1.0.30001751",
1286
+ "electron-to-chromium": "^1.5.238",
1287
+ "node-releases": "^2.0.26",
1288
+ "update-browserslist-db": "^1.1.4"
1289
+ },
1290
+ "bin": {
1291
+ "browserslist": "cli.js"
1292
+ },
1293
+ "engines": {
1294
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
1295
+ }
1296
+ },
1297
+ "node_modules/buffer-equal-constant-time": {
1298
+ "version": "1.0.1",
1299
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
1300
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
1301
+ "license": "BSD-3-Clause"
1302
+ },
1303
+ "node_modules/caniuse-lite": {
1304
+ "version": "1.0.30001751",
1305
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz",
1306
+ "integrity": "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==",
1307
+ "dev": true,
1308
+ "funding": [
1309
+ {
1310
+ "type": "opencollective",
1311
+ "url": "https://opencollective.com/browserslist"
1312
+ },
1313
+ {
1314
+ "type": "tidelift",
1315
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
1316
+ },
1317
+ {
1318
+ "type": "github",
1319
+ "url": "https://github.com/sponsors/ai"
1320
+ }
1321
+ ],
1322
+ "license": "CC-BY-4.0"
1323
+ },
1324
+ "node_modules/convert-source-map": {
1325
+ "version": "2.0.0",
1326
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
1327
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
1328
+ "dev": true,
1329
+ "license": "MIT"
1330
+ },
1331
+ "node_modules/data-uri-to-buffer": {
1332
+ "version": "4.0.1",
1333
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
1334
+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
1335
+ "license": "MIT",
1336
+ "engines": {
1337
+ "node": ">= 12"
1338
+ }
1339
+ },
1340
+ "node_modules/debug": {
1341
+ "version": "4.4.3",
1342
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
1343
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
1344
+ "license": "MIT",
1345
+ "dependencies": {
1346
+ "ms": "^2.1.3"
1347
+ },
1348
+ "engines": {
1349
+ "node": ">=6.0"
1350
+ },
1351
+ "peerDependenciesMeta": {
1352
+ "supports-color": {
1353
+ "optional": true
1354
+ }
1355
+ }
1356
+ },
1357
+ "node_modules/ecdsa-sig-formatter": {
1358
+ "version": "1.0.11",
1359
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
1360
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
1361
+ "license": "Apache-2.0",
1362
+ "dependencies": {
1363
+ "safe-buffer": "^5.0.1"
1364
+ }
1365
+ },
1366
+ "node_modules/electron-to-chromium": {
1367
+ "version": "1.5.240",
1368
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.240.tgz",
1369
+ "integrity": "sha512-OBwbZjWgrCOH+g6uJsA2/7Twpas2OlepS9uvByJjR2datRDuKGYeD+nP8lBBks2qnB7bGJNHDUx7c/YLaT3QMQ==",
1370
+ "dev": true,
1371
+ "license": "ISC"
1372
+ },
1373
+ "node_modules/esbuild": {
1374
+ "version": "0.25.11",
1375
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz",
1376
+ "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==",
1377
+ "dev": true,
1378
+ "hasInstallScript": true,
1379
+ "license": "MIT",
1380
+ "bin": {
1381
+ "esbuild": "bin/esbuild"
1382
+ },
1383
+ "engines": {
1384
+ "node": ">=18"
1385
+ },
1386
+ "optionalDependencies": {
1387
+ "@esbuild/aix-ppc64": "0.25.11",
1388
+ "@esbuild/android-arm": "0.25.11",
1389
+ "@esbuild/android-arm64": "0.25.11",
1390
+ "@esbuild/android-x64": "0.25.11",
1391
+ "@esbuild/darwin-arm64": "0.25.11",
1392
+ "@esbuild/darwin-x64": "0.25.11",
1393
+ "@esbuild/freebsd-arm64": "0.25.11",
1394
+ "@esbuild/freebsd-x64": "0.25.11",
1395
+ "@esbuild/linux-arm": "0.25.11",
1396
+ "@esbuild/linux-arm64": "0.25.11",
1397
+ "@esbuild/linux-ia32": "0.25.11",
1398
+ "@esbuild/linux-loong64": "0.25.11",
1399
+ "@esbuild/linux-mips64el": "0.25.11",
1400
+ "@esbuild/linux-ppc64": "0.25.11",
1401
+ "@esbuild/linux-riscv64": "0.25.11",
1402
+ "@esbuild/linux-s390x": "0.25.11",
1403
+ "@esbuild/linux-x64": "0.25.11",
1404
+ "@esbuild/netbsd-arm64": "0.25.11",
1405
+ "@esbuild/netbsd-x64": "0.25.11",
1406
+ "@esbuild/openbsd-arm64": "0.25.11",
1407
+ "@esbuild/openbsd-x64": "0.25.11",
1408
+ "@esbuild/openharmony-arm64": "0.25.11",
1409
+ "@esbuild/sunos-x64": "0.25.11",
1410
+ "@esbuild/win32-arm64": "0.25.11",
1411
+ "@esbuild/win32-ia32": "0.25.11",
1412
+ "@esbuild/win32-x64": "0.25.11"
1413
+ }
1414
+ },
1415
+ "node_modules/escalade": {
1416
+ "version": "3.2.0",
1417
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
1418
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
1419
+ "dev": true,
1420
+ "license": "MIT",
1421
+ "engines": {
1422
+ "node": ">=6"
1423
+ }
1424
+ },
1425
+ "node_modules/extend": {
1426
+ "version": "3.0.2",
1427
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
1428
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
1429
+ "license": "MIT"
1430
+ },
1431
+ "node_modules/fdir": {
1432
+ "version": "6.5.0",
1433
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
1434
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
1435
+ "dev": true,
1436
+ "license": "MIT",
1437
+ "engines": {
1438
+ "node": ">=12.0.0"
1439
+ },
1440
+ "peerDependencies": {
1441
+ "picomatch": "^3 || ^4"
1442
+ },
1443
+ "peerDependenciesMeta": {
1444
+ "picomatch": {
1445
+ "optional": true
1446
+ }
1447
+ }
1448
+ },
1449
+ "node_modules/fetch-blob": {
1450
+ "version": "3.2.0",
1451
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
1452
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
1453
+ "funding": [
1454
+ {
1455
+ "type": "github",
1456
+ "url": "https://github.com/sponsors/jimmywarting"
1457
+ },
1458
+ {
1459
+ "type": "paypal",
1460
+ "url": "https://paypal.me/jimmywarting"
1461
+ }
1462
+ ],
1463
+ "license": "MIT",
1464
+ "dependencies": {
1465
+ "node-domexception": "^1.0.0",
1466
+ "web-streams-polyfill": "^3.0.3"
1467
+ },
1468
+ "engines": {
1469
+ "node": "^12.20 || >= 14.13"
1470
+ }
1471
+ },
1472
+ "node_modules/formdata-polyfill": {
1473
+ "version": "4.0.10",
1474
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
1475
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
1476
+ "license": "MIT",
1477
+ "dependencies": {
1478
+ "fetch-blob": "^3.1.2"
1479
+ },
1480
+ "engines": {
1481
+ "node": ">=12.20.0"
1482
+ }
1483
+ },
1484
+ "node_modules/fsevents": {
1485
+ "version": "2.3.3",
1486
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
1487
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
1488
+ "dev": true,
1489
+ "hasInstallScript": true,
1490
+ "license": "MIT",
1491
+ "optional": true,
1492
+ "os": [
1493
+ "darwin"
1494
+ ],
1495
+ "engines": {
1496
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1497
+ }
1498
+ },
1499
+ "node_modules/gaxios": {
1500
+ "version": "7.1.2",
1501
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.2.tgz",
1502
+ "integrity": "sha512-/Szrn8nr+2TsQT1Gp8iIe/BEytJmbyfrbFh419DfGQSkEgNEhbPi7JRJuughjkTzPWgU9gBQf5AVu3DbHt0OXA==",
1503
+ "license": "Apache-2.0",
1504
+ "dependencies": {
1505
+ "extend": "^3.0.2",
1506
+ "https-proxy-agent": "^7.0.1",
1507
+ "node-fetch": "^3.3.2"
1508
+ },
1509
+ "engines": {
1510
+ "node": ">=18"
1511
+ }
1512
+ },
1513
+ "node_modules/gcp-metadata": {
1514
+ "version": "8.1.1",
1515
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.1.tgz",
1516
+ "integrity": "sha512-dTCcAe9fRQf06ELwel6lWWFrEbstwjUBYEhr5VRGoC+iPDZQucHppCowaIp8b8v92tU1G4X4H3b/Y6zXZxkMsQ==",
1517
+ "license": "Apache-2.0",
1518
+ "dependencies": {
1519
+ "gaxios": "^7.0.0",
1520
+ "google-logging-utils": "^1.0.0",
1521
+ "json-bigint": "^1.0.0"
1522
+ },
1523
+ "engines": {
1524
+ "node": ">=18"
1525
+ }
1526
+ },
1527
+ "node_modules/gensync": {
1528
+ "version": "1.0.0-beta.2",
1529
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
1530
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
1531
+ "dev": true,
1532
+ "license": "MIT",
1533
+ "engines": {
1534
+ "node": ">=6.9.0"
1535
+ }
1536
+ },
1537
+ "node_modules/google-auth-library": {
1538
+ "version": "10.4.2",
1539
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.4.2.tgz",
1540
+ "integrity": "sha512-EKiQasw6aEdxSovPEf1oBxCEvxjFamZ6MPaVOSPXZMnqKFLo+rrYjHyjKlFfZcXiKi9qAH6cutr5WRqqa1jKhg==",
1541
+ "license": "Apache-2.0",
1542
+ "dependencies": {
1543
+ "base64-js": "^1.3.0",
1544
+ "ecdsa-sig-formatter": "^1.0.11",
1545
+ "gaxios": "^7.0.0",
1546
+ "gcp-metadata": "^8.0.0",
1547
+ "google-logging-utils": "^1.0.0",
1548
+ "gtoken": "^8.0.0",
1549
+ "jws": "^4.0.0"
1550
+ },
1551
+ "engines": {
1552
+ "node": ">=18"
1553
+ }
1554
+ },
1555
+ "node_modules/google-logging-utils": {
1556
+ "version": "1.1.1",
1557
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.1.tgz",
1558
+ "integrity": "sha512-rcX58I7nqpu4mbKztFeOAObbomBbHU2oIb/d3tJfF3dizGSApqtSwYJigGCooHdnMyQBIw8BrWyK96w3YXgr6A==",
1559
+ "license": "Apache-2.0",
1560
+ "engines": {
1561
+ "node": ">=14"
1562
+ }
1563
+ },
1564
+ "node_modules/gtoken": {
1565
+ "version": "8.0.0",
1566
+ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz",
1567
+ "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==",
1568
+ "license": "MIT",
1569
+ "dependencies": {
1570
+ "gaxios": "^7.0.0",
1571
+ "jws": "^4.0.0"
1572
+ },
1573
+ "engines": {
1574
+ "node": ">=18"
1575
+ }
1576
+ },
1577
+ "node_modules/https-proxy-agent": {
1578
+ "version": "7.0.6",
1579
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
1580
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
1581
+ "license": "MIT",
1582
+ "dependencies": {
1583
+ "agent-base": "^7.1.2",
1584
+ "debug": "4"
1585
+ },
1586
+ "engines": {
1587
+ "node": ">= 14"
1588
+ }
1589
+ },
1590
+ "node_modules/js-tokens": {
1591
+ "version": "4.0.0",
1592
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
1593
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
1594
+ "dev": true,
1595
+ "license": "MIT"
1596
+ },
1597
+ "node_modules/jsesc": {
1598
+ "version": "3.1.0",
1599
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
1600
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
1601
+ "dev": true,
1602
+ "license": "MIT",
1603
+ "bin": {
1604
+ "jsesc": "bin/jsesc"
1605
+ },
1606
+ "engines": {
1607
+ "node": ">=6"
1608
+ }
1609
+ },
1610
+ "node_modules/json-bigint": {
1611
+ "version": "1.0.0",
1612
+ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
1613
+ "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
1614
+ "license": "MIT",
1615
+ "dependencies": {
1616
+ "bignumber.js": "^9.0.0"
1617
+ }
1618
+ },
1619
+ "node_modules/json5": {
1620
+ "version": "2.2.3",
1621
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
1622
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
1623
+ "dev": true,
1624
+ "license": "MIT",
1625
+ "bin": {
1626
+ "json5": "lib/cli.js"
1627
+ },
1628
+ "engines": {
1629
+ "node": ">=6"
1630
+ }
1631
+ },
1632
+ "node_modules/jwa": {
1633
+ "version": "2.0.1",
1634
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
1635
+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
1636
+ "license": "MIT",
1637
+ "dependencies": {
1638
+ "buffer-equal-constant-time": "^1.0.1",
1639
+ "ecdsa-sig-formatter": "1.0.11",
1640
+ "safe-buffer": "^5.0.1"
1641
+ }
1642
+ },
1643
+ "node_modules/jws": {
1644
+ "version": "4.0.0",
1645
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
1646
+ "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
1647
+ "license": "MIT",
1648
+ "dependencies": {
1649
+ "jwa": "^2.0.0",
1650
+ "safe-buffer": "^5.0.1"
1651
+ }
1652
+ },
1653
+ "node_modules/lru-cache": {
1654
+ "version": "5.1.1",
1655
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
1656
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
1657
+ "dev": true,
1658
+ "license": "ISC",
1659
+ "dependencies": {
1660
+ "yallist": "^3.0.2"
1661
+ }
1662
+ },
1663
+ "node_modules/ms": {
1664
+ "version": "2.1.3",
1665
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1666
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1667
+ "license": "MIT"
1668
+ },
1669
+ "node_modules/nanoid": {
1670
+ "version": "3.3.11",
1671
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
1672
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
1673
+ "dev": true,
1674
+ "funding": [
1675
+ {
1676
+ "type": "github",
1677
+ "url": "https://github.com/sponsors/ai"
1678
+ }
1679
+ ],
1680
+ "license": "MIT",
1681
+ "bin": {
1682
+ "nanoid": "bin/nanoid.cjs"
1683
+ },
1684
+ "engines": {
1685
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1686
+ }
1687
+ },
1688
+ "node_modules/node-domexception": {
1689
+ "version": "1.0.0",
1690
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
1691
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
1692
+ "deprecated": "Use your platform's native DOMException instead",
1693
+ "funding": [
1694
+ {
1695
+ "type": "github",
1696
+ "url": "https://github.com/sponsors/jimmywarting"
1697
+ },
1698
+ {
1699
+ "type": "github",
1700
+ "url": "https://paypal.me/jimmywarting"
1701
+ }
1702
+ ],
1703
+ "license": "MIT",
1704
+ "engines": {
1705
+ "node": ">=10.5.0"
1706
+ }
1707
+ },
1708
+ "node_modules/node-fetch": {
1709
+ "version": "3.3.2",
1710
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
1711
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
1712
+ "license": "MIT",
1713
+ "dependencies": {
1714
+ "data-uri-to-buffer": "^4.0.0",
1715
+ "fetch-blob": "^3.1.4",
1716
+ "formdata-polyfill": "^4.0.10"
1717
+ },
1718
+ "engines": {
1719
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
1720
+ },
1721
+ "funding": {
1722
+ "type": "opencollective",
1723
+ "url": "https://opencollective.com/node-fetch"
1724
+ }
1725
+ },
1726
+ "node_modules/node-releases": {
1727
+ "version": "2.0.26",
1728
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.26.tgz",
1729
+ "integrity": "sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==",
1730
+ "dev": true,
1731
+ "license": "MIT"
1732
+ },
1733
+ "node_modules/picocolors": {
1734
+ "version": "1.1.1",
1735
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
1736
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
1737
+ "dev": true,
1738
+ "license": "ISC"
1739
+ },
1740
+ "node_modules/picomatch": {
1741
+ "version": "4.0.3",
1742
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
1743
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
1744
+ "dev": true,
1745
+ "license": "MIT",
1746
+ "engines": {
1747
+ "node": ">=12"
1748
+ },
1749
+ "funding": {
1750
+ "url": "https://github.com/sponsors/jonschlinkert"
1751
+ }
1752
+ },
1753
+ "node_modules/postcss": {
1754
+ "version": "8.5.6",
1755
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
1756
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
1757
+ "dev": true,
1758
+ "funding": [
1759
+ {
1760
+ "type": "opencollective",
1761
+ "url": "https://opencollective.com/postcss/"
1762
+ },
1763
+ {
1764
+ "type": "tidelift",
1765
+ "url": "https://tidelift.com/funding/github/npm/postcss"
1766
+ },
1767
+ {
1768
+ "type": "github",
1769
+ "url": "https://github.com/sponsors/ai"
1770
+ }
1771
+ ],
1772
+ "license": "MIT",
1773
+ "dependencies": {
1774
+ "nanoid": "^3.3.11",
1775
+ "picocolors": "^1.1.1",
1776
+ "source-map-js": "^1.2.1"
1777
+ },
1778
+ "engines": {
1779
+ "node": "^10 || ^12 || >=14"
1780
+ }
1781
+ },
1782
+ "node_modules/react": {
1783
+ "version": "19.2.0",
1784
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
1785
+ "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
1786
+ "license": "MIT",
1787
+ "engines": {
1788
+ "node": ">=0.10.0"
1789
+ }
1790
+ },
1791
+ "node_modules/react-dom": {
1792
+ "version": "19.2.0",
1793
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
1794
+ "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
1795
+ "license": "MIT",
1796
+ "dependencies": {
1797
+ "scheduler": "^0.27.0"
1798
+ },
1799
+ "peerDependencies": {
1800
+ "react": "^19.2.0"
1801
+ }
1802
+ },
1803
+ "node_modules/react-refresh": {
1804
+ "version": "0.18.0",
1805
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
1806
+ "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
1807
+ "dev": true,
1808
+ "license": "MIT",
1809
+ "engines": {
1810
+ "node": ">=0.10.0"
1811
+ }
1812
+ },
1813
+ "node_modules/rollup": {
1814
+ "version": "4.52.5",
1815
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz",
1816
+ "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==",
1817
+ "dev": true,
1818
+ "license": "MIT",
1819
+ "dependencies": {
1820
+ "@types/estree": "1.0.8"
1821
+ },
1822
+ "bin": {
1823
+ "rollup": "dist/bin/rollup"
1824
+ },
1825
+ "engines": {
1826
+ "node": ">=18.0.0",
1827
+ "npm": ">=8.0.0"
1828
+ },
1829
+ "optionalDependencies": {
1830
+ "@rollup/rollup-android-arm-eabi": "4.52.5",
1831
+ "@rollup/rollup-android-arm64": "4.52.5",
1832
+ "@rollup/rollup-darwin-arm64": "4.52.5",
1833
+ "@rollup/rollup-darwin-x64": "4.52.5",
1834
+ "@rollup/rollup-freebsd-arm64": "4.52.5",
1835
+ "@rollup/rollup-freebsd-x64": "4.52.5",
1836
+ "@rollup/rollup-linux-arm-gnueabihf": "4.52.5",
1837
+ "@rollup/rollup-linux-arm-musleabihf": "4.52.5",
1838
+ "@rollup/rollup-linux-arm64-gnu": "4.52.5",
1839
+ "@rollup/rollup-linux-arm64-musl": "4.52.5",
1840
+ "@rollup/rollup-linux-loong64-gnu": "4.52.5",
1841
+ "@rollup/rollup-linux-ppc64-gnu": "4.52.5",
1842
+ "@rollup/rollup-linux-riscv64-gnu": "4.52.5",
1843
+ "@rollup/rollup-linux-riscv64-musl": "4.52.5",
1844
+ "@rollup/rollup-linux-s390x-gnu": "4.52.5",
1845
+ "@rollup/rollup-linux-x64-gnu": "4.52.5",
1846
+ "@rollup/rollup-linux-x64-musl": "4.52.5",
1847
+ "@rollup/rollup-openharmony-arm64": "4.52.5",
1848
+ "@rollup/rollup-win32-arm64-msvc": "4.52.5",
1849
+ "@rollup/rollup-win32-ia32-msvc": "4.52.5",
1850
+ "@rollup/rollup-win32-x64-gnu": "4.52.5",
1851
+ "@rollup/rollup-win32-x64-msvc": "4.52.5",
1852
+ "fsevents": "~2.3.2"
1853
+ }
1854
+ },
1855
+ "node_modules/safe-buffer": {
1856
+ "version": "5.2.1",
1857
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1858
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1859
+ "funding": [
1860
+ {
1861
+ "type": "github",
1862
+ "url": "https://github.com/sponsors/feross"
1863
+ },
1864
+ {
1865
+ "type": "patreon",
1866
+ "url": "https://www.patreon.com/feross"
1867
+ },
1868
+ {
1869
+ "type": "consulting",
1870
+ "url": "https://feross.org/support"
1871
+ }
1872
+ ],
1873
+ "license": "MIT"
1874
+ },
1875
+ "node_modules/scheduler": {
1876
+ "version": "0.27.0",
1877
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
1878
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
1879
+ "license": "MIT"
1880
+ },
1881
+ "node_modules/semver": {
1882
+ "version": "6.3.1",
1883
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
1884
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
1885
+ "dev": true,
1886
+ "license": "ISC",
1887
+ "bin": {
1888
+ "semver": "bin/semver.js"
1889
+ }
1890
+ },
1891
+ "node_modules/source-map-js": {
1892
+ "version": "1.2.1",
1893
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
1894
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
1895
+ "dev": true,
1896
+ "license": "BSD-3-Clause",
1897
+ "engines": {
1898
+ "node": ">=0.10.0"
1899
+ }
1900
+ },
1901
+ "node_modules/tinyglobby": {
1902
+ "version": "0.2.15",
1903
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
1904
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
1905
+ "dev": true,
1906
+ "license": "MIT",
1907
+ "dependencies": {
1908
+ "fdir": "^6.5.0",
1909
+ "picomatch": "^4.0.3"
1910
+ },
1911
+ "engines": {
1912
+ "node": ">=12.0.0"
1913
+ },
1914
+ "funding": {
1915
+ "url": "https://github.com/sponsors/SuperchupuDev"
1916
+ }
1917
+ },
1918
+ "node_modules/typescript": {
1919
+ "version": "5.8.3",
1920
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
1921
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
1922
+ "dev": true,
1923
+ "license": "Apache-2.0",
1924
+ "bin": {
1925
+ "tsc": "bin/tsc",
1926
+ "tsserver": "bin/tsserver"
1927
+ },
1928
+ "engines": {
1929
+ "node": ">=14.17"
1930
+ }
1931
+ },
1932
+ "node_modules/undici-types": {
1933
+ "version": "6.21.0",
1934
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
1935
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
1936
+ "dev": true,
1937
+ "license": "MIT"
1938
+ },
1939
+ "node_modules/update-browserslist-db": {
1940
+ "version": "1.1.4",
1941
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz",
1942
+ "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==",
1943
+ "dev": true,
1944
+ "funding": [
1945
+ {
1946
+ "type": "opencollective",
1947
+ "url": "https://opencollective.com/browserslist"
1948
+ },
1949
+ {
1950
+ "type": "tidelift",
1951
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
1952
+ },
1953
+ {
1954
+ "type": "github",
1955
+ "url": "https://github.com/sponsors/ai"
1956
+ }
1957
+ ],
1958
+ "license": "MIT",
1959
+ "dependencies": {
1960
+ "escalade": "^3.2.0",
1961
+ "picocolors": "^1.1.1"
1962
+ },
1963
+ "bin": {
1964
+ "update-browserslist-db": "cli.js"
1965
+ },
1966
+ "peerDependencies": {
1967
+ "browserslist": ">= 4.21.0"
1968
+ }
1969
+ },
1970
+ "node_modules/vite": {
1971
+ "version": "6.4.1",
1972
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
1973
+ "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
1974
+ "dev": true,
1975
+ "license": "MIT",
1976
+ "dependencies": {
1977
+ "esbuild": "^0.25.0",
1978
+ "fdir": "^6.4.4",
1979
+ "picomatch": "^4.0.2",
1980
+ "postcss": "^8.5.3",
1981
+ "rollup": "^4.34.9",
1982
+ "tinyglobby": "^0.2.13"
1983
+ },
1984
+ "bin": {
1985
+ "vite": "bin/vite.js"
1986
+ },
1987
+ "engines": {
1988
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
1989
+ },
1990
+ "funding": {
1991
+ "url": "https://github.com/vitejs/vite?sponsor=1"
1992
+ },
1993
+ "optionalDependencies": {
1994
+ "fsevents": "~2.3.3"
1995
+ },
1996
+ "peerDependencies": {
1997
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
1998
+ "jiti": ">=1.21.0",
1999
+ "less": "*",
2000
+ "lightningcss": "^1.21.0",
2001
+ "sass": "*",
2002
+ "sass-embedded": "*",
2003
+ "stylus": "*",
2004
+ "sugarss": "*",
2005
+ "terser": "^5.16.0",
2006
+ "tsx": "^4.8.1",
2007
+ "yaml": "^2.4.2"
2008
+ },
2009
+ "peerDependenciesMeta": {
2010
+ "@types/node": {
2011
+ "optional": true
2012
+ },
2013
+ "jiti": {
2014
+ "optional": true
2015
+ },
2016
+ "less": {
2017
+ "optional": true
2018
+ },
2019
+ "lightningcss": {
2020
+ "optional": true
2021
+ },
2022
+ "sass": {
2023
+ "optional": true
2024
+ },
2025
+ "sass-embedded": {
2026
+ "optional": true
2027
+ },
2028
+ "stylus": {
2029
+ "optional": true
2030
+ },
2031
+ "sugarss": {
2032
+ "optional": true
2033
+ },
2034
+ "terser": {
2035
+ "optional": true
2036
+ },
2037
+ "tsx": {
2038
+ "optional": true
2039
+ },
2040
+ "yaml": {
2041
+ "optional": true
2042
+ }
2043
+ }
2044
+ },
2045
+ "node_modules/web-streams-polyfill": {
2046
+ "version": "3.3.3",
2047
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
2048
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
2049
+ "license": "MIT",
2050
+ "engines": {
2051
+ "node": ">= 8"
2052
+ }
2053
+ },
2054
+ "node_modules/ws": {
2055
+ "version": "8.18.3",
2056
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
2057
+ "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
2058
+ "license": "MIT",
2059
+ "engines": {
2060
+ "node": ">=10.0.0"
2061
+ },
2062
+ "peerDependencies": {
2063
+ "bufferutil": "^4.0.1",
2064
+ "utf-8-validate": ">=5.0.2"
2065
+ },
2066
+ "peerDependenciesMeta": {
2067
+ "bufferutil": {
2068
+ "optional": true
2069
+ },
2070
+ "utf-8-validate": {
2071
+ "optional": true
2072
+ }
2073
+ }
2074
+ },
2075
+ "node_modules/yallist": {
2076
+ "version": "3.1.1",
2077
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
2078
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
2079
+ "dev": true,
2080
+ "license": "ISC"
2081
+ }
2082
+ }
2083
+ }
package.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "eq-template-creator",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@google/genai": "^1.27.0",
13
+ "react": "^19.2.0",
14
+ "react-dom": "^19.2.0"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^22.14.0",
18
+ "@vitejs/plugin-react": "^5.0.0",
19
+ "typescript": "~5.8.2",
20
+ "vite": "^6.2.0"
21
+ }
22
+ }
services/geminiService.ts ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { GoogleGenAI, Type } from "@google/genai";
2
+ import type { GeminiAnalysisResult } from '../types';
3
+
4
+ const getApiKey = (): string => {
5
+ // Try localStorage first (user-provided key)
6
+ const storedKey = localStorage.getItem('gemini_api_key');
7
+ if (storedKey) {
8
+ return storedKey;
9
+ }
10
+
11
+ // Fall back to environment variable (for development)
12
+ const envKey = import.meta.env.VITE_GEMINI_API_KEY;
13
+ if (envKey) {
14
+ return envKey;
15
+ }
16
+
17
+ throw new Error("No API key found. Please configure your Gemini API key.");
18
+ };
19
+
20
+ const systemInstruction = `You are an expert audio engineer specializing in vocal processing.
21
+ Analyze the provided audio sample to determine the speaker's vocal characteristics.
22
+ Identify the fundamental frequency range, prominent harmonics, and any problematic frequencies (e.g., sibilance, plosives, muddiness).
23
+ Based on this analysis, generate a 10-band graphic EQ preset to enhance vocal clarity, presence, and warmth. The preset should be suitable for a standard podcast or voice-over.
24
+ Provide the output in a JSON format with three main keys: 'vocalProfile', 'eqPreset', and 'audacityXml'.
25
+ - 'vocalProfile' should be an object containing 'description' (a paragraph summarizing the voice), 'fundamentalRange' (e.g., '100Hz - 250Hz'), and 'keyCharacteristics' (an array of strings like 'Slightly sibilant', 'Warm low-mids').
26
+ - 'eqPreset' should be an array of objects, where each object has 'frequency' (in Hz) and 'gain' (in dB).
27
+ - 'audacityXml' should be a string containing a valid Audacity EQ preset in XML format. The curve should be named 'Gemini Vocal Preset' and contain <point> elements for each frequency and gain setting.`;
28
+
29
+ const responseSchema = {
30
+ type: Type.OBJECT,
31
+ properties: {
32
+ vocalProfile: {
33
+ type: Type.OBJECT,
34
+ properties: {
35
+ description: { type: Type.STRING },
36
+ fundamentalRange: { type: Type.STRING },
37
+ keyCharacteristics: {
38
+ type: Type.ARRAY,
39
+ items: { type: Type.STRING }
40
+ }
41
+ },
42
+ required: ["description", "fundamentalRange", "keyCharacteristics"]
43
+ },
44
+ eqPreset: {
45
+ type: Type.ARRAY,
46
+ items: {
47
+ type: Type.OBJECT,
48
+ properties: {
49
+ frequency: { type: Type.NUMBER },
50
+ gain: { type: Type.NUMBER }
51
+ },
52
+ required: ["frequency", "gain"]
53
+ }
54
+ },
55
+ audacityXml: { type: Type.STRING }
56
+ },
57
+ required: ["vocalProfile", "eqPreset", "audacityXml"]
58
+ };
59
+
60
+ export async function analyzeAudio(audioBase64: string, mimeType: string): Promise<GeminiAnalysisResult> {
61
+ try {
62
+ const apiKey = getApiKey();
63
+ const ai = new GoogleGenAI({ apiKey });
64
+
65
+ const audioPart = {
66
+ inlineData: {
67
+ data: audioBase64,
68
+ mimeType: mimeType,
69
+ },
70
+ };
71
+
72
+ const response = await ai.models.generateContent({
73
+ model: 'gemini-2.5-pro',
74
+ contents: { parts: [audioPart] },
75
+ config: {
76
+ systemInstruction,
77
+ responseMimeType: 'application/json',
78
+ responseSchema: responseSchema,
79
+ }
80
+ });
81
+
82
+ const text = response.text;
83
+
84
+ if (!text) {
85
+ throw new Error('Gemini returned an empty response.');
86
+ }
87
+
88
+ // The response should be valid JSON due to responseMimeType
89
+ return JSON.parse(text) as GeminiAnalysisResult;
90
+
91
+ } catch (error) {
92
+ console.error("Error calling Gemini API:", error);
93
+ if(error instanceof Error && error.message.includes('SAFETY')) {
94
+ throw new Error("The audio could not be processed due to safety settings. Please try a different audio sample.");
95
+ }
96
+ throw new Error("Failed to get analysis from Gemini. Please check the console for more details.");
97
+ }
98
+ }
tsconfig.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "experimentalDecorators": true,
5
+ "useDefineForClassFields": false,
6
+ "module": "ESNext",
7
+ "lib": [
8
+ "ES2022",
9
+ "DOM",
10
+ "DOM.Iterable"
11
+ ],
12
+ "skipLibCheck": true,
13
+ "types": [
14
+ "node"
15
+ ],
16
+ "moduleResolution": "bundler",
17
+ "isolatedModules": true,
18
+ "moduleDetection": "force",
19
+ "allowJs": true,
20
+ "jsx": "react-jsx",
21
+ "paths": {
22
+ "@/*": [
23
+ "./*"
24
+ ]
25
+ },
26
+ "allowImportingTsExtensions": true,
27
+ "noEmit": true
28
+ }
29
+ }
types.ts ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export interface VocalProfile {
2
+ description: string;
3
+ fundamentalRange: string;
4
+ keyCharacteristics: string[];
5
+ }
6
+
7
+ export interface EQSetting {
8
+ frequency: number;
9
+ gain: number;
10
+ }
11
+
12
+ export interface GeminiAnalysisResult {
13
+ vocalProfile: VocalProfile;
14
+ eqPreset: EQSetting[];
15
+ audacityXml: string;
16
+ }
vite.config.ts ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import path from 'path';
2
+ import { defineConfig, loadEnv } from 'vite';
3
+ import react from '@vitejs/plugin-react';
4
+
5
+ export default defineConfig(({ mode }) => {
6
+ const env = loadEnv(mode, '.', '');
7
+ return {
8
+ server: {
9
+ port: 3000,
10
+ host: '0.0.0.0',
11
+ },
12
+ plugins: [react()],
13
+ define: {
14
+ 'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY),
15
+ 'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY)
16
+ },
17
+ resolve: {
18
+ alias: {
19
+ '@': path.resolve(__dirname, '.'),
20
+ }
21
+ }
22
+ };
23
+ });