@admin-layout/demo-perplexity-browser 12.2.4-alpha.0 → 12.2.4-alpha.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/index.d.ts +2 -0
- package/lib/api/index.d.ts.map +1 -0
- package/lib/api/llm.d.ts +34 -0
- package/lib/api/llm.d.ts.map +1 -0
- package/lib/api/llm.js +200 -0
- package/lib/api/llm.js.map +1 -0
- package/lib/api/stt.d.ts +21 -0
- package/lib/api/stt.d.ts.map +1 -0
- package/lib/api/stt.js +111 -0
- package/lib/api/stt.js.map +1 -0
- package/lib/components/AttachmentPreview.d.ts +10 -0
- package/lib/components/AttachmentPreview.d.ts.map +1 -0
- package/lib/components/AttachmentPreview.js +50 -0
- package/lib/components/AttachmentPreview.js.map +1 -0
- package/lib/components/AudioRecorder.d.ts +8 -0
- package/lib/components/AudioRecorder.d.ts.map +1 -0
- package/lib/components/AudioRecorder.js +221 -0
- package/lib/components/AudioRecorder.js.map +1 -0
- package/lib/components/AudioVisualizer.d.ts +7 -0
- package/lib/components/AudioVisualizer.d.ts.map +1 -0
- package/lib/components/AudioVisualizer.js +116 -0
- package/lib/components/AudioVisualizer.js.map +1 -0
- package/lib/components/ChatFiles.d.ts +8 -0
- package/lib/components/ChatFiles.d.ts.map +1 -0
- package/lib/components/ChatFiles.js +76 -0
- package/lib/components/ChatFiles.js.map +1 -0
- package/lib/components/ChatScreenshot.d.ts +7 -0
- package/lib/components/ChatScreenshot.d.ts.map +1 -0
- package/lib/components/ChatScreenshot.js +113 -0
- package/lib/components/ChatScreenshot.js.map +1 -0
- package/lib/components/SearchBar.d.ts +9 -1
- package/lib/components/SearchBar.d.ts.map +1 -1
- package/lib/components/SearchBar.js +176 -240
- package/lib/components/SearchBar.js.map +1 -1
- package/lib/config/constants.d.ts +108 -0
- package/lib/config/constants.d.ts.map +1 -0
- package/lib/config/constants.js +115 -0
- package/lib/config/constants.js.map +1 -0
- package/lib/config/env.d.ts +20 -0
- package/lib/config/env.d.ts.map +1 -0
- package/lib/config/env.js +22 -0
- package/lib/config/env.js.map +1 -0
- package/lib/config/index.d.ts +4 -0
- package/lib/config/index.d.ts.map +1 -0
- package/lib/config/providers.d.ts +54 -0
- package/lib/config/providers.d.ts.map +1 -0
- package/lib/config/providers.js +65 -0
- package/lib/config/providers.js.map +1 -0
- package/lib/pages/home/HomePage.d.ts +1 -3
- package/lib/pages/home/HomePage.d.ts.map +1 -1
- package/lib/pages/home/HomePage.js +232 -4
- package/lib/pages/home/HomePage.js.map +1 -1
- package/lib/platform/browser.d.ts +18 -0
- package/lib/platform/browser.d.ts.map +1 -0
- package/lib/platform/context.d.ts +76 -0
- package/lib/platform/context.d.ts.map +1 -0
- package/lib/platform/index.d.ts +25 -0
- package/lib/platform/index.d.ts.map +1 -0
- package/lib/platform/tauri.d.ts +35 -0
- package/lib/platform/tauri.d.ts.map +1 -0
- package/lib/platform/types.d.ts +164 -0
- package/lib/platform/types.d.ts.map +1 -0
- package/lib/state/chatMachine.d.ts +148 -0
- package/lib/state/chatMachine.d.ts.map +1 -0
- package/lib/state/chatMachine.js +458 -0
- package/lib/state/chatMachine.js.map +1 -0
- package/lib/state/index.d.ts +3 -0
- package/lib/state/index.d.ts.map +1 -0
- package/lib/state/useChatWithPlatform.d.ts +43 -0
- package/lib/state/useChatWithPlatform.d.ts.map +1 -0
- package/lib/types/chat.d.ts +56 -0
- package/lib/types/chat.d.ts.map +1 -0
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/utils/chatStorage.d.ts +25 -0
- package/lib/utils/chatStorage.d.ts.map +1 -0
- package/lib/utils/chatStorage.js +17 -0
- package/lib/utils/chatStorage.js.map +1 -0
- package/package.json +10 -6
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import {jsxs,jsx}from'react/jsx-runtime';import {useState,useRef,useCallback,useEffect}from'react';import {StopCircle,Loader2,Send}from'lucide-react';import {AudioVisualizer}from'./AudioVisualizer.js';import {transcribeAudio}from'../api/stt.js';const MAX_DURATION = 3 * 60 * 1000; // 3 minutes
|
|
2
|
+
function AudioRecorder({
|
|
3
|
+
onTranscriptionComplete,
|
|
4
|
+
onCancel,
|
|
5
|
+
onError
|
|
6
|
+
}) {
|
|
7
|
+
const [audioStream, setAudioStream] = useState(null);
|
|
8
|
+
const [isTranscribing, setIsTranscribing] = useState(false);
|
|
9
|
+
const [duration, setDuration] = useState(0);
|
|
10
|
+
const mediaRecorderRef = useRef(null);
|
|
11
|
+
const audioChunksRef = useRef([]);
|
|
12
|
+
const startTimeRef = useRef(0);
|
|
13
|
+
const durationIntervalRef = useRef(null);
|
|
14
|
+
const maxDurationTimeoutRef = useRef(null);
|
|
15
|
+
const cleanup = useCallback(() => {
|
|
16
|
+
if (durationIntervalRef.current) {
|
|
17
|
+
clearInterval(durationIntervalRef.current);
|
|
18
|
+
durationIntervalRef.current = null;
|
|
19
|
+
}
|
|
20
|
+
if (maxDurationTimeoutRef.current) {
|
|
21
|
+
clearTimeout(maxDurationTimeoutRef.current);
|
|
22
|
+
maxDurationTimeoutRef.current = null;
|
|
23
|
+
}
|
|
24
|
+
if (audioStream) {
|
|
25
|
+
audioStream.getTracks().forEach(track => track.stop());
|
|
26
|
+
setAudioStream(null);
|
|
27
|
+
}
|
|
28
|
+
if (mediaRecorderRef.current?.state === 'recording') {
|
|
29
|
+
mediaRecorderRef.current.stop();
|
|
30
|
+
mediaRecorderRef.current = null;
|
|
31
|
+
}
|
|
32
|
+
}, [audioStream]);
|
|
33
|
+
const startRecording = useCallback(async () => {
|
|
34
|
+
console.log('▶️ [AUDIO RECORDER] startRecording called');
|
|
35
|
+
try {
|
|
36
|
+
console.log('🎤 [AUDIO RECORDER] Requesting microphone access...');
|
|
37
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
38
|
+
audio: true
|
|
39
|
+
});
|
|
40
|
+
console.log('✅ [AUDIO RECORDER] Microphone access granted', stream);
|
|
41
|
+
setAudioStream(stream);
|
|
42
|
+
// Try multiple mimeTypes to find one that's supported
|
|
43
|
+
const supportedTypes = ['audio/webm;codecs=opus', 'audio/webm', 'audio/ogg;codecs=opus', 'audio/ogg', 'audio/mp4', 'audio/wav'];
|
|
44
|
+
let mimeType = '';
|
|
45
|
+
for (const type of supportedTypes) {
|
|
46
|
+
if (MediaRecorder.isTypeSupported(type)) {
|
|
47
|
+
mimeType = type;
|
|
48
|
+
console.log('✅ [AUDIO RECORDER] Using supported mimeType:', mimeType);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!mimeType) {
|
|
53
|
+
console.warn('⚠️ [AUDIO RECORDER] No supported mimeType found, using default');
|
|
54
|
+
mimeType = 'audio/webm'; // Fallback
|
|
55
|
+
}
|
|
56
|
+
const recorder = new MediaRecorder(stream, mimeType ? {
|
|
57
|
+
mimeType
|
|
58
|
+
} : {});
|
|
59
|
+
mediaRecorderRef.current = recorder;
|
|
60
|
+
audioChunksRef.current = [];
|
|
61
|
+
startTimeRef.current = Date.now();
|
|
62
|
+
recorder.ondataavailable = e => {
|
|
63
|
+
if (e.data.size > 0) {
|
|
64
|
+
audioChunksRef.current.push(e.data);
|
|
65
|
+
console.log('🎵 [AUDIO RECORDER] Audio chunk received:', {
|
|
66
|
+
chunkSize: e.data.size,
|
|
67
|
+
totalChunks: audioChunksRef.current.length,
|
|
68
|
+
totalSize: audioChunksRef.current.reduce((sum, chunk) => sum + chunk.size, 0)
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
recorder.start(100);
|
|
73
|
+
console.log('🔴 [AUDIO RECORDER] Recording started');
|
|
74
|
+
durationIntervalRef.current = setInterval(() => {
|
|
75
|
+
setDuration(Date.now() - startTimeRef.current);
|
|
76
|
+
}, 100);
|
|
77
|
+
maxDurationTimeoutRef.current = setTimeout(() => {
|
|
78
|
+
if (mediaRecorderRef.current?.state === 'recording') {
|
|
79
|
+
handleSend();
|
|
80
|
+
}
|
|
81
|
+
}, MAX_DURATION);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error('❌ [AUDIO RECORDER] Failed to start recording:', error);
|
|
84
|
+
cleanup();
|
|
85
|
+
onCancel();
|
|
86
|
+
}
|
|
87
|
+
}, [cleanup, onCancel]);
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
console.log('🎬 [AUDIO RECORDER] Component mounted - starting recording');
|
|
90
|
+
startRecording();
|
|
91
|
+
return () => {
|
|
92
|
+
console.log('🛑 [AUDIO RECORDER] Component unmounting - cleanup');
|
|
93
|
+
cleanup();
|
|
94
|
+
};
|
|
95
|
+
}, []);
|
|
96
|
+
const handleStop = useCallback(() => {
|
|
97
|
+
console.log('⏹️ [AUDIO RECORDER] Stop button clicked');
|
|
98
|
+
cleanup();
|
|
99
|
+
onCancel();
|
|
100
|
+
}, [cleanup, onCancel]);
|
|
101
|
+
const handleSend = useCallback(async () => {
|
|
102
|
+
console.log('📤 [AUDIO RECORDER] Send button clicked');
|
|
103
|
+
if (!mediaRecorderRef.current) {
|
|
104
|
+
console.error('❌ [AUDIO RECORDER] No media recorder found!');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (isTranscribing) {
|
|
108
|
+
console.warn('⚠️ [AUDIO RECORDER] Already transcribing, ignoring click');
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (audioChunksRef.current.length === 0) {
|
|
112
|
+
console.error('❌ [AUDIO RECORDER] No audio chunks recorded!');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
console.log('🔄 [AUDIO RECORDER] Starting transcription process');
|
|
116
|
+
setIsTranscribing(true);
|
|
117
|
+
const mimeType = mediaRecorderRef.current.mimeType;
|
|
118
|
+
const chunks = [...audioChunksRef.current];
|
|
119
|
+
cleanup();
|
|
120
|
+
try {
|
|
121
|
+
const audioBlob = new Blob(chunks, {
|
|
122
|
+
type: mimeType
|
|
123
|
+
});
|
|
124
|
+
console.log('📦 [AUDIO RECORDER] Audio blob created:', {
|
|
125
|
+
size: audioBlob.size,
|
|
126
|
+
type: audioBlob.type,
|
|
127
|
+
chunks: chunks.length
|
|
128
|
+
});
|
|
129
|
+
console.log('🚀 [AUDIO RECORDER] Calling transcribeAudio with whisper-large-v3-turbo...');
|
|
130
|
+
const text = await transcribeAudio({
|
|
131
|
+
audio: audioBlob
|
|
132
|
+
});
|
|
133
|
+
console.log('✅ [AUDIO RECORDER] Transcription result:', text);
|
|
134
|
+
onTranscriptionComplete(text);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
137
|
+
console.error('❌ [AUDIO RECORDER] Transcription failed:', errorMessage);
|
|
138
|
+
onError?.(errorMessage);
|
|
139
|
+
onCancel();
|
|
140
|
+
}
|
|
141
|
+
}, [isTranscribing, cleanup, onTranscriptionComplete, onError, onCancel]);
|
|
142
|
+
const formatTime = ms => {
|
|
143
|
+
const seconds = Math.floor(ms / 1000);
|
|
144
|
+
const mins = Math.floor(seconds / 60);
|
|
145
|
+
const secs = seconds % 60;
|
|
146
|
+
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
|
147
|
+
};
|
|
148
|
+
return jsxs("div", {
|
|
149
|
+
className: "border bg-background rounded-lg overflow-hidden",
|
|
150
|
+
children: [jsx("div", {
|
|
151
|
+
className: "h-10 relative bg-muted/20",
|
|
152
|
+
style: {
|
|
153
|
+
pointerEvents: 'none'
|
|
154
|
+
},
|
|
155
|
+
children: audioStream ? jsx("div", {
|
|
156
|
+
className: "h-full w-full pt-2",
|
|
157
|
+
children: jsx(AudioVisualizer, {
|
|
158
|
+
stream: audioStream,
|
|
159
|
+
isRecording: true
|
|
160
|
+
})
|
|
161
|
+
}) : jsx("div", {
|
|
162
|
+
className: "h-full flex items-center justify-center text-xs text-muted-foreground",
|
|
163
|
+
children: "Initializing..."
|
|
164
|
+
})
|
|
165
|
+
}), jsxs("div", {
|
|
166
|
+
className: "flex items-center justify-between px-3 py-2 border-t bg-muted/5",
|
|
167
|
+
style: {
|
|
168
|
+
position: 'relative',
|
|
169
|
+
zIndex: 10
|
|
170
|
+
},
|
|
171
|
+
children: [jsxs("div", {
|
|
172
|
+
className: "flex items-center gap-2",
|
|
173
|
+
children: [jsx("div", {
|
|
174
|
+
className: "h-1.5 w-1.5 bg-red-500 rounded-full animate-pulse"
|
|
175
|
+
}), jsx("span", {
|
|
176
|
+
className: "text-xs font-mono tabular-nums font-medium",
|
|
177
|
+
children: formatTime(duration)
|
|
178
|
+
}), jsx("span", {
|
|
179
|
+
className: "text-[10px] text-muted-foreground",
|
|
180
|
+
children: "/ 3:00"
|
|
181
|
+
})]
|
|
182
|
+
}), jsxs("div", {
|
|
183
|
+
className: "flex items-center gap-2",
|
|
184
|
+
style: {
|
|
185
|
+
position: 'relative',
|
|
186
|
+
zIndex: 20
|
|
187
|
+
},
|
|
188
|
+
children: [jsx("button", {
|
|
189
|
+
type: "button",
|
|
190
|
+
onClick: handleStop,
|
|
191
|
+
disabled: isTranscribing,
|
|
192
|
+
className: "inline-flex h-7 w-7 items-center justify-center rounded-lg border border-input bg-background hover:bg-accent hover:text-accent-foreground disabled:opacity-50 disabled:pointer-events-none",
|
|
193
|
+
title: "Stop recording",
|
|
194
|
+
style: {
|
|
195
|
+
pointerEvents: 'auto'
|
|
196
|
+
},
|
|
197
|
+
children: jsx(StopCircle, {
|
|
198
|
+
className: "h-3.5 w-3.5"
|
|
199
|
+
})
|
|
200
|
+
}), jsx("button", {
|
|
201
|
+
type: "button",
|
|
202
|
+
onClick: handleSend,
|
|
203
|
+
disabled: isTranscribing,
|
|
204
|
+
className: "inline-flex h-7 w-7 items-center justify-center rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:pointer-events-none",
|
|
205
|
+
title: isTranscribing ? 'Transcribing...' : 'Send to AI',
|
|
206
|
+
style: {
|
|
207
|
+
cursor: isTranscribing ? 'not-allowed' : 'pointer',
|
|
208
|
+
pointerEvents: 'auto',
|
|
209
|
+
position: 'relative',
|
|
210
|
+
zIndex: 30
|
|
211
|
+
},
|
|
212
|
+
children: isTranscribing ? jsx(Loader2, {
|
|
213
|
+
className: "h-3.5 w-3.5 animate-spin"
|
|
214
|
+
}) : jsx(Send, {
|
|
215
|
+
className: "h-3.5 w-3.5"
|
|
216
|
+
})
|
|
217
|
+
})]
|
|
218
|
+
})]
|
|
219
|
+
})]
|
|
220
|
+
});
|
|
221
|
+
}export{AudioRecorder};//# sourceMappingURL=AudioRecorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AudioRecorder.js","sources":["../../src/components/AudioRecorder.tsx"],"sourcesContent":[null],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface AudioVisualizerProps {
|
|
2
|
+
isRecording: boolean;
|
|
3
|
+
stream: MediaStream | null;
|
|
4
|
+
}
|
|
5
|
+
export declare function AudioVisualizer({ stream, isRecording }: AudioVisualizerProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=AudioVisualizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AudioVisualizer.d.ts","sourceRoot":"","sources":["../../src/components/AudioVisualizer.tsx"],"names":[],"mappings":"AAgBA,UAAU,oBAAoB;IAC1B,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;CAC9B;AAED,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,oBAAoB,2CAsI5E"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import {jsx}from'react/jsx-runtime';import {useRef,useEffect}from'react';// Configuration constants for the audio analyzer
|
|
2
|
+
const AUDIO_CONFIG = {
|
|
3
|
+
FFT_SIZE: 512,
|
|
4
|
+
SMOOTHING: 0.8,
|
|
5
|
+
MIN_BAR_HEIGHT: 2,
|
|
6
|
+
MIN_BAR_WIDTH: 2,
|
|
7
|
+
BAR_SPACING: 4,
|
|
8
|
+
COLOR: {
|
|
9
|
+
MIN_INTENSITY: 100,
|
|
10
|
+
MAX_INTENSITY: 255,
|
|
11
|
+
INTENSITY_RANGE: 155
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
function AudioVisualizer({
|
|
15
|
+
stream,
|
|
16
|
+
isRecording
|
|
17
|
+
}) {
|
|
18
|
+
const canvasRef = useRef(null);
|
|
19
|
+
const audioContextRef = useRef(null);
|
|
20
|
+
const analyserRef = useRef(null);
|
|
21
|
+
const animationFrameRef = useRef(0);
|
|
22
|
+
const containerRef = useRef(null);
|
|
23
|
+
const cleanup = () => {
|
|
24
|
+
if (animationFrameRef.current) {
|
|
25
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
26
|
+
}
|
|
27
|
+
if (audioContextRef.current) {
|
|
28
|
+
audioContextRef.current.close();
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
return cleanup;
|
|
33
|
+
}, []);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (stream && isRecording) {
|
|
36
|
+
startVisualization();
|
|
37
|
+
} else {
|
|
38
|
+
cleanup();
|
|
39
|
+
}
|
|
40
|
+
}, [stream, isRecording]);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const handleResize = () => {
|
|
43
|
+
if (canvasRef.current && containerRef.current) {
|
|
44
|
+
const container = containerRef.current;
|
|
45
|
+
const canvas = canvasRef.current;
|
|
46
|
+
const dpr = window.devicePixelRatio || 1;
|
|
47
|
+
const rect = container.getBoundingClientRect();
|
|
48
|
+
canvas.width = (rect.width - 2) * dpr;
|
|
49
|
+
canvas.height = (rect.height - 2) * dpr;
|
|
50
|
+
canvas.style.width = `${rect.width - 2}px`;
|
|
51
|
+
canvas.style.height = `${rect.height - 2}px`;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
window.addEventListener('resize', handleResize);
|
|
55
|
+
handleResize();
|
|
56
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
57
|
+
}, []);
|
|
58
|
+
const startVisualization = async () => {
|
|
59
|
+
try {
|
|
60
|
+
const audioContext = new AudioContext();
|
|
61
|
+
audioContextRef.current = audioContext;
|
|
62
|
+
const analyser = audioContext.createAnalyser();
|
|
63
|
+
analyser.fftSize = AUDIO_CONFIG.FFT_SIZE;
|
|
64
|
+
analyser.smoothingTimeConstant = AUDIO_CONFIG.SMOOTHING;
|
|
65
|
+
analyserRef.current = analyser;
|
|
66
|
+
const source = audioContext.createMediaStreamSource(stream);
|
|
67
|
+
source.connect(analyser);
|
|
68
|
+
draw();
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error('Error starting visualization:', error);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const getBarColor = normalizedHeight => {
|
|
74
|
+
const intensity = Math.floor(normalizedHeight * AUDIO_CONFIG.COLOR.INTENSITY_RANGE) + AUDIO_CONFIG.COLOR.MIN_INTENSITY;
|
|
75
|
+
return `rgb(${intensity}, ${intensity}, ${intensity})`;
|
|
76
|
+
};
|
|
77
|
+
const drawBar = (ctx, x, centerY, width, height, color) => {
|
|
78
|
+
ctx.fillStyle = color;
|
|
79
|
+
ctx.fillRect(x, centerY - height, width, height);
|
|
80
|
+
ctx.fillRect(x, centerY, width, height);
|
|
81
|
+
};
|
|
82
|
+
const draw = () => {
|
|
83
|
+
if (!isRecording) return;
|
|
84
|
+
const canvas = canvasRef.current;
|
|
85
|
+
const ctx = canvas?.getContext('2d');
|
|
86
|
+
if (!canvas || !ctx || !analyserRef.current) return;
|
|
87
|
+
const dpr = window.devicePixelRatio || 1;
|
|
88
|
+
ctx.scale(dpr, dpr);
|
|
89
|
+
const analyser = analyserRef.current;
|
|
90
|
+
const bufferLength = analyser.frequencyBinCount;
|
|
91
|
+
const frequencyData = new Uint8Array(bufferLength);
|
|
92
|
+
const drawFrame = () => {
|
|
93
|
+
animationFrameRef.current = requestAnimationFrame(drawFrame);
|
|
94
|
+
analyser.getByteFrequencyData(frequencyData);
|
|
95
|
+
ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr);
|
|
96
|
+
const barWidth = Math.max(AUDIO_CONFIG.MIN_BAR_WIDTH, canvas.width / dpr / bufferLength - AUDIO_CONFIG.BAR_SPACING);
|
|
97
|
+
const centerY = canvas.height / dpr / 2;
|
|
98
|
+
let x = 0;
|
|
99
|
+
for (let i = 0; i < bufferLength; i++) {
|
|
100
|
+
const normalizedHeight = frequencyData[i] / 255;
|
|
101
|
+
const barHeight = Math.max(AUDIO_CONFIG.MIN_BAR_HEIGHT, normalizedHeight * centerY);
|
|
102
|
+
drawBar(ctx, x, centerY, barWidth, barHeight, getBarColor(normalizedHeight));
|
|
103
|
+
x += barWidth + AUDIO_CONFIG.BAR_SPACING;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
drawFrame();
|
|
107
|
+
};
|
|
108
|
+
return jsx("div", {
|
|
109
|
+
ref: containerRef,
|
|
110
|
+
className: "!h-[32px] !w-full",
|
|
111
|
+
children: jsx("canvas", {
|
|
112
|
+
ref: canvasRef,
|
|
113
|
+
className: "h-full !w-full pl-8"
|
|
114
|
+
})
|
|
115
|
+
});
|
|
116
|
+
}export{AudioVisualizer};//# sourceMappingURL=AudioVisualizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AudioVisualizer.js","sources":["../../src/components/AudioVisualizer.tsx"],"sourcesContent":[null],"names":["_jsx"],"mappings":"yEAEA;AACA,MAAM,YAAY,GAAG;AACjB,EAAA,QAAA,EAAQ,GAAE;AACV,EAAA,SAAA,EAAS,GAAE;AACX,EAAA,cAAA,EAAc;AACd,EAAA,aAAA,EAAa;AACb,EAAA,WAAA,EAAW;AACX,EAAA,KAAA,EAAK;AACD,IAAA,aAAA,EAAA;AACA,IAAA,aAAA,EAAA;AACA,IAAA,eAAA,EAAA;AACH;CACK;SAOM,eAAe,CAAC;AAC5B,EAAA,MAAA;AACA,EAAA;AACA,CAAA,EAAA;AACA,EAAA,MAAA,mBAAuB,IAAG,CAAA;AAC1B,EAAA,MAAA,eAAkB,GAAG,MAAuB,CAAA,IAAK,CAAC;QAElD,WAAa,GAAG,MAAK,CAAA,IAAA,CAAA;AACjB,EAAA,MAAA,iBAAI,GAAA,MAAkB,CAAA,CAAA,CAAO;AACzB,EAAA,MAAA,YAAA,GAAA,MAAA,CAAA,IAAqB,CAAA;QACzB,OAAC,GAAA,MAAA;AACD,IAAA,IAAA,iBAAI,CAAA,OAAgB,EAAO;AACvB,MAAA,oBAAA,CAAe,iBAAiB,CAAA,OAAA,CAAA;;AAExC,IAAA,IAAE,eAAA,CAAA,OAAA,EAAA;MAEF,eAAe,CAAA,OAAA,CAAA,KAAA,EAAA;AACX,IAAA;;WAGJ,CAAA,MAAa;AACT,IAAA,OAAA,OAAU;AACN,EAAA,CAAA,EAAA,EAAA,CAAA;WACH,CAAA,MAAA;cAAO,IAAA,WAAA,EAAA;AACJ,MAAA,kBAAU,EAAA;WACb;AACL,aAAI;IAEJ;YACI,EAAM,WAAA,CAAY,CAAA;YACd,MAAI;AACA,IAAA,MAAA,YAAM,GAAA,MAAS;AACf,MAAA,IAAA,SAAA,CAAA,OAAM,IAAS,oBAAkB,EAAA;AACjC,QAAA,MAAA,YAAS,YAAU,CAAA,OAAA;AAEnB,QAAA,MAAA,MAAA,YAAa,CAAA,OAAU;AACvB,QAAA,MAAA,GAAA,GAAA,MAAO,CAAA,gBAAmB,IAAK,CAAA;AAC/B,QAAA,MAAA,IAAA,GAAA,SAAa,CAAA,qBAAuB,EAAA;AAEpC,QAAA,MAAA,CAAA,KAAA,GAAO,CAAA,IAAK,CAAC,KAAK,GAAG,CAAA,IAAG,GAAK;AAC7B,QAAA,MAAA,CAAA,MAAA,GAAO,CAAA,IAAM,CAAA,MAAM,GAAG,CAAA,IAAG,GAAK;cACjC,CAAA,KAAA,CAAA,KAAA,GAAA,CAAA,EAAA,IAAA,CAAA,KAAA,GAAA,CAAA,CAAA,EAAA,CAAA;AACL,QAAA,MAAE,CAAA,KAAA,CAAA,MAAA,GAAA,CAAA,EAAA,IAAA,CAAA,MAAA,GAAA,CAAA,CAAA,EAAA,CAAA;AAEF,MAAA;AACA,IAAA,CAAA;UAEA,CAAA,gBAAmB,CAAC,QAAA,EAAA,YAAoB,CAAA;IAC5C,YAAO,EAAA;AAEP,IAAA,OAAM,MAAA,MAAA,CAAA,mBAAgC,CAAA,QAAA,EAAA,YAAA,CAAA;AAClC,EAAA,CAAA,EAAA,EAAA,CAAA;AACI,EAAA,MAAA,kBAAM,GAAA,YAAmB;AACzB,IAAA,IAAA;AAEA,MAAA,MAAA,eAAc,IAAG,YAAa,EAAA;AAC9B,MAAA,eAAS,CAAA,OAAO,GAAG,YAAa;AAChC,MAAA,MAAA,QAAQ,GAAC,YAAA,CAAA,cAAwB,EAAA;AACjC,MAAA,QAAA,CAAA,OAAA,GAAY,YAAU,CAAA,QAAS;cAE/B,CAAA,qBAAe,GAAY;AAC3B,MAAA,WAAA,CAAM,OAAC,GAAQ,QAAS;AAExB,MAAA,MAAA,MAAM,GAAC,YAAA,CAAA,uBAAA,CAAA,MAAA,CAAA;YACV,CAAA,OAAA,CAAA,QAAA,CAAA;UAAC,EAAA;AACE,IAAA,CAAA,CAAA,OAAA,KAAA,EAAQ;aACX,CAAA,KAAA,CAAA,+BAAA,EAAA,KAAA,CAAA;AACL,IAAA;AAEA,EAAA,CAAA;QACI,WAAM,GAAA,gBACS,IAAA;AACf,IAAA,MAAA,YAAc,IAAA,CAAA,KAAS,CAAA,gBAAc,GAAK,YAAa,CAAA,KAAA,CAAA,eAAA,CAAA,GAAA,YAAA,CAAA,KAAA,CAAA,aAAA;AAC3D,IAAA,OAAE,CAAA,IAAA,EAAA,SAAA,CAAA,EAAA,EAAA,SAAA,CAAA,EAAA,EAAA,SAAA,CAAA,CAAA,CAAA;AAEF,EAAA,CAAA;AAQI,EAAA,MAAA,OAAI,GAAA,CAAA,GAAS,EAAG,CAAA,EAAA,OAAM,EAAA,KAAA,EAAA,MAAA,EAAA,KAAA,KAAA;AACtB,IAAA,GAAA,CAAA,SAAI,GAAQ,KAAI;QAChB,QAAI,CAAA,CAAA,EAAQ,OAAI,GAAA,MAAS,EAAK,KAAE,EAAA,MAAQ,CAAA;AAC5C,IAAA,GAAE,CAAA,QAAA,CAAA,CAAA,EAAA,OAAA,EAAA,KAAA,EAAA,MAAA,CAAA;;AAGE,EAAA,MAAA,IAAI,GAAC,MAAA;oBAAoB,EAAA;AAEzB,IAAA,MAAA,MAAM,GAAA,SAAS,CAAA;UACf,GAAA,GAAM,MAAM,EAAA,eAAmB,CAAA;QAC/B,CAAA,UAAW,CAAA,GAAI,IAAI,CAAA,WAAK,CAAA,OAAY,EAAA;aAAS,GAAA,MAAO,CAAA,gBAAA,IAAA,CAAA;AAEpD,IAAA,GAAA,CAAA,KAAA,CAAM,GAAG,EAAA,GAAG,CAAA;AACZ,IAAA,MAAA,QAAU,GAAG,WAAO,CAAA,OAAA;AAEpB,IAAA,MAAA,YAAc,GAAG,QAAA,CAAA,iBAAoB;AACrC,IAAA,MAAA,aAAM,GAAY,IAAG;AACrB,IAAA,MAAA,kBAAmB;uBAEJ,CAAA,OAAQ,GAAA,qBAAA,CAAA,SAAA,CAAA;AACnB,MAAA,QAAA,CAAA,oBAAkB,CAAA;AAElB,MAAA,GAAA,CAAA,SAAA,CAAQ,CAAC,EAAA,CAAA,EAAA,MAAA,CAAA,KAAA,GAAoB,GAAC,EAAA,MAAA,CAAA,MAAe,GAAA,GAAA,CAAA;AAE7C,MAAA,MAAA,QAAI,GAAA,IAAW,CAAA,IAAK,YAAY,CAAA,aAAc,QAAO,CAAA,KAAO,GAAC,GAAA,GAAA,YAAA,GAAA,YAAA,CAAA,WAAA,CAAA;YAE7D,OAAM,GAAA,OAAW,SACb,GAAA,GAAA,CAAA;cAGJ,CAAA;eACA,CAAI,GAAC,CAAG,EAAE,CAAA,GAAA,YAAA,EAAA,CAAA,EAAA,EAAA;AAEV,QAAA,MAAA,gBAAiB,GAAG,aAAc,CAAC,CAAA,CAAE,GAAG,GAAA;uBAC9B,GAAA,IAAA,CAAA,GAAA,CAAA,YAAmB,CAAA,gBAAuB,gBAAA,GAAA,OAAA,CAAA;AAChD,QAAA,OAAA,CAAA,GAAA,EAAA,CAAM,EAAA,OAAS,EAAA,UAAY,SAAA,EAAA,WAAa,CAAA,gBAAgB,CAAA,CAAA;AAExD,QAAA,CAAA,IAAA,QAAA,GAAQ,wBAAiB;AAEzB,MAAA;;AAER,IAAA,SAAE,EAAA;AAEF,EAAA,CAAA;AACJ,EAAA,OAAEA,GAAA,CAAA,KAAA,EAAA;IAEF,GAAA,EAAA;AAKJ,IAAC,SAAA,EAAA,mBAAA;;;;;;"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface ChatFilesProps {
|
|
2
|
+
onFilesSelected?: (files: File[]) => void;
|
|
3
|
+
currentFileCount?: number;
|
|
4
|
+
className?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function ChatFiles({ onFilesSelected, currentFileCount, className }: ChatFilesProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=ChatFiles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatFiles.d.ts","sourceRoot":"","sources":["../../src/components/ChatFiles.tsx"],"names":[],"mappings":"AAKA,UAAU,cAAc;IACpB,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IAC1C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAgBD,wBAAgB,SAAS,CAAC,EAAE,eAAe,EAAE,gBAAoB,EAAE,SAAS,EAAE,EAAE,cAAc,2CA4E7F"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {jsxs,Fragment,jsx}from'react/jsx-runtime';import {useRef,useCallback}from'react';import {PaperclipIcon}from'lucide-react';import {MAX_FILES,ACCEPTED_FILE_TYPES_STRING,MAX_FILE_SIZE_BYTES}from'../config/constants.js';import {cn}from'../utils/index.js';/**
|
|
2
|
+
* Validate file size
|
|
3
|
+
* @param file - File to validate
|
|
4
|
+
* @returns Error message if invalid, null if valid
|
|
5
|
+
*/
|
|
6
|
+
function validateFileSize(file) {
|
|
7
|
+
if (file.size > MAX_FILE_SIZE_BYTES) {
|
|
8
|
+
const fileSizeMB = (file.size / (1024 * 1024)).toFixed(2);
|
|
9
|
+
const maxSizeMB = (MAX_FILE_SIZE_BYTES / (1024 * 1024)).toFixed(0);
|
|
10
|
+
return `File "${file.name}" is too large (${fileSizeMB}MB). Maximum size is ${maxSizeMB}MB.`;
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
function ChatFiles({
|
|
15
|
+
onFilesSelected,
|
|
16
|
+
currentFileCount = 0,
|
|
17
|
+
className
|
|
18
|
+
}) {
|
|
19
|
+
const fileInputRef = useRef(null);
|
|
20
|
+
const handleFileSelect = useCallback(() => {
|
|
21
|
+
console.log('📎 [CHAT FILES] Button clicked!');
|
|
22
|
+
fileInputRef.current?.click();
|
|
23
|
+
}, []);
|
|
24
|
+
const handleFileChange = useCallback(e => {
|
|
25
|
+
const selectedFiles = Array.from(e.target.files || []);
|
|
26
|
+
if (selectedFiles.length === 0) return;
|
|
27
|
+
// Check total count
|
|
28
|
+
const remainingSlots = MAX_FILES - currentFileCount;
|
|
29
|
+
if (selectedFiles.length > remainingSlots) {
|
|
30
|
+
alert(`You can only attach ${remainingSlots} more file(s). Maximum is ${MAX_FILES} files.`);
|
|
31
|
+
e.target.value = '';
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// Validate each file
|
|
35
|
+
const validFiles = [];
|
|
36
|
+
const errors = [];
|
|
37
|
+
for (const file of selectedFiles) {
|
|
38
|
+
const error = validateFileSize(file);
|
|
39
|
+
if (error) {
|
|
40
|
+
errors.push(error);
|
|
41
|
+
} else {
|
|
42
|
+
validFiles.push(file);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Show errors if any
|
|
46
|
+
if (errors.length > 0) {
|
|
47
|
+
alert(errors.join('\n\n'));
|
|
48
|
+
}
|
|
49
|
+
// Add valid files
|
|
50
|
+
if (validFiles.length > 0 && onFilesSelected) {
|
|
51
|
+
console.log('📎 [CHAT FILES] Selected:', validFiles.length);
|
|
52
|
+
onFilesSelected(validFiles);
|
|
53
|
+
}
|
|
54
|
+
// Reset input so same file can be selected again
|
|
55
|
+
e.target.value = '';
|
|
56
|
+
}, [currentFileCount, onFilesSelected]);
|
|
57
|
+
return jsxs(Fragment, {
|
|
58
|
+
children: [jsx("button", {
|
|
59
|
+
type: "button",
|
|
60
|
+
onClick: handleFileSelect,
|
|
61
|
+
className: cn('inline-flex h-7 w-7 items-center justify-center rounded-2xl transition-colors hover:bg-secondary/70 active:bg-secondary text-muted-foreground', className),
|
|
62
|
+
title: "Attach files",
|
|
63
|
+
"aria-label": "Attach files",
|
|
64
|
+
children: jsx(PaperclipIcon, {
|
|
65
|
+
className: "h-4 w-4"
|
|
66
|
+
})
|
|
67
|
+
}), jsx("input", {
|
|
68
|
+
ref: fileInputRef,
|
|
69
|
+
type: "file",
|
|
70
|
+
multiple: true,
|
|
71
|
+
accept: ACCEPTED_FILE_TYPES_STRING,
|
|
72
|
+
className: "hidden",
|
|
73
|
+
onChange: handleFileChange
|
|
74
|
+
})]
|
|
75
|
+
});
|
|
76
|
+
}export{ChatFiles};//# sourceMappingURL=ChatFiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatFiles.js","sources":["../../src/components/ChatFiles.tsx"],"sourcesContent":[null],"names":[],"mappings":"mQAWA;;;;AAIG;AACH,SAAS,gBAAgB,CAAC,IAAU,EAAA;AAChC,EAAA,IAAA,SAAS,GAAI,mBAAG,EAAmB;AAC/B,IAAA,MAAA,cAAgB,IAAI,KAAK,IAAI,IAAI,GAAA,IAAO,CAAA,EAAA;AACxC,IAAA,MAAA,aAAe,mBAAI,IAAmB,IAAI,GAAA,IAAO,CAAA,EAAA,OAAO,CAAA,CAAA,CAAA;WACxD,CAAA,MAAO,EAAA,IAAS,KAAK,CAAA,4BAAuB,CAAA,qBAAU,EAAA,SAAwB,CAAA,GAAS,CAAA;;AAE3F,EAAA,OAAA;AACJ;AAEM,SAAU,SAAS,CAAC;AACtB,EAAA;AAEA,EAAA,gBAAM,GAAA,CAAA;AACF,EAAA;AACA,CAAA,EAAA;QACD,YAAI,GAAA,MAAA,CAAA,IAAA,CAAA;AAEP,EAAA,MAAA,mBAAsB,WAAG,CAAA,MACoB;AACrC,IAAA,OAAA,CAAA,GAAM,CAAA,iCAAmC,CAAC;AAE1C,IAAA,YAAI,CAAA,OAAA,EAAc,KAAA,EAAM;;QAExB,gBAAA,GAAA,WAAoB,CAAA,CAAA,IAAA;AACpB,IAAA,MAAA,aAAM,GAAA,KAAiB,CAAA,IAAA,CAAA,CAAA,CAAA;AACvB,IAAA,IAAA,oBAAkB,KAAM,CAAA,EAAG;AACvB;AACA,IAAA,MAAA,cAAS,GAAK,SAAM,GAAA,gBAAA;qBACb,CAAA,MAAA,GAAA,cAAA,EAAA;WACV,CAAA,CAAA,oBAAA,EAAA,cAAA,CAAA,0BAAA,EAAA,SAAA,CAAA,OAAA,CAAA,CAAA;QAED,MAAA,CAAA,KAAA,GAAA,EAAA;;;AAIA;AACI,IAAA,MAAA,aAAW,EAAA;gBACP,GAAA,EAAK;AACL,IAAA,KAAA,MAAA,IAAA,IAAO,aAAY,EAAA;YACvB,KAAC,GAAA,gBAAA,CAAA,IAAA,CAAA;iBAAM;AACH,QAAA,MAAA,CAAA,IAAA,CAAA,KAAA,CAAU;aACb;QACL,UAAC,CAAA,IAAA,CAAA,IAAA,CAAA;;AAGD,IAAA;;QAEA,MAAC,CAAA,MAAA,GAAA,CAAA,EAAA;WAED,CAAA,MAAA,CAAA,IAAA,CAAA,MAAkB,CAAA,CAAA;;;kBAGd,CAAA,MAAA,GAAgB,CAAA,IAAA,eAAY,EAAA;aAC/B,CAAA,GAAA,CAAA,2BAAA,EAAA,UAAA,CAAA,MAAA,CAAA;qBAED,CAAA,UAAA,CAAA;AACA,IAAA;AACJ;AAIJ,IAAA,CAAA,CAAA,MACI,CAAA,KAAA,GAAA,EAAA;AAuBR,EAAC,CAAA,EAAA,CAAA,gBAAA,EAAA,eAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface ChatScreenshotProps {
|
|
2
|
+
onScreenshot?: (base64Image: string) => void;
|
|
3
|
+
className?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function ChatScreenshot({ onScreenshot, className }: ChatScreenshotProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=ChatScreenshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatScreenshot.d.ts","sourceRoot":"","sources":["../../src/components/ChatScreenshot.tsx"],"names":[],"mappings":"AAIA,UAAU,mBAAmB;IACzB,YAAY,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AA6ED,wBAAgB,cAAc,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,mBAAmB,2CA+D9E"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {jsx}from'react/jsx-runtime';import {useState,useRef,useCallback}from'react';import {Camera}from'lucide-react';import {cn}from'../utils/index.js';/**
|
|
2
|
+
* Check if running in Tauri environment
|
|
3
|
+
*/
|
|
4
|
+
function isTauriEnvironment() {
|
|
5
|
+
return typeof window !== 'undefined' && '__TAURI__' in window;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Capture screenshot using browser API (fallback for non-Tauri environments)
|
|
9
|
+
*/
|
|
10
|
+
async function captureScreenshotBrowser() {
|
|
11
|
+
try {
|
|
12
|
+
// @ts-ignore - getDisplayMedia is not in all TypeScript definitions
|
|
13
|
+
const stream = await navigator.mediaDevices.getDisplayMedia({
|
|
14
|
+
video: true
|
|
15
|
+
});
|
|
16
|
+
const video = document.createElement('video');
|
|
17
|
+
video.srcObject = stream;
|
|
18
|
+
video.play();
|
|
19
|
+
// Wait for video to load
|
|
20
|
+
await new Promise(resolve => {
|
|
21
|
+
video.onloadedmetadata = resolve;
|
|
22
|
+
});
|
|
23
|
+
const canvas = document.createElement('canvas');
|
|
24
|
+
canvas.width = video.videoWidth;
|
|
25
|
+
canvas.height = video.videoHeight;
|
|
26
|
+
const ctx = canvas.getContext('2d');
|
|
27
|
+
if (!ctx) throw new Error('Failed to get canvas context');
|
|
28
|
+
ctx.drawImage(video, 0, 0);
|
|
29
|
+
// Stop the stream
|
|
30
|
+
stream.getTracks().forEach(track => track.stop());
|
|
31
|
+
// Convert to base64
|
|
32
|
+
const base64 = canvas.toDataURL('image/png');
|
|
33
|
+
return base64;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error('Browser screenshot error:', error);
|
|
36
|
+
throw new Error('Screenshot cancelled or failed');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Tauri types are defined in src/platform/types.ts
|
|
40
|
+
/**
|
|
41
|
+
* Capture screenshot using Tauri API via window.__TAURI__ global
|
|
42
|
+
*/
|
|
43
|
+
async function captureScreenshotTauri() {
|
|
44
|
+
try {
|
|
45
|
+
const tauri = window.__TAURI__;
|
|
46
|
+
if (!tauri) {
|
|
47
|
+
throw new Error('Tauri API not available');
|
|
48
|
+
}
|
|
49
|
+
console.log('📷 [SCREENSHOT] Invoking capture_to_base64...');
|
|
50
|
+
const base64 = await tauri.core.invoke('capture_to_base64');
|
|
51
|
+
console.log('📷 [SCREENSHOT] Received response, length:', base64?.length || 0);
|
|
52
|
+
// Handle both formats: already a data URL or raw base64
|
|
53
|
+
// Rust returns data:image/jpeg;base64,... format
|
|
54
|
+
const dataUrl = base64.startsWith('data:') ? base64 : `data:image/jpeg;base64,${base64}`;
|
|
55
|
+
console.log('📷 [SCREENSHOT] Data URL created, starts with:', dataUrl.substring(0, 30));
|
|
56
|
+
return dataUrl;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error('📷 [SCREENSHOT] Tauri screenshot error:', error);
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function ChatScreenshot({
|
|
63
|
+
onScreenshot,
|
|
64
|
+
className
|
|
65
|
+
}) {
|
|
66
|
+
const [isCapturing, setIsCapturing] = useState(false);
|
|
67
|
+
const screenshotInitiatedRef = useRef(false);
|
|
68
|
+
const handleScreenshot = useCallback(async () => {
|
|
69
|
+
console.log('📷 [SCREENSHOT] Button clicked!');
|
|
70
|
+
if (isCapturing) {
|
|
71
|
+
console.log('📷 [SCREENSHOT] Already capturing, ignoring click');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
screenshotInitiatedRef.current = true;
|
|
75
|
+
setIsCapturing(true);
|
|
76
|
+
try {
|
|
77
|
+
console.log('📷 [SCREENSHOT] Capturing screen...');
|
|
78
|
+
let dataUrl;
|
|
79
|
+
if (isTauriEnvironment()) {
|
|
80
|
+
console.log('📷 [SCREENSHOT] Using Tauri API');
|
|
81
|
+
dataUrl = await captureScreenshotTauri();
|
|
82
|
+
} else {
|
|
83
|
+
console.log('📷 [SCREENSHOT] Using browser API');
|
|
84
|
+
dataUrl = await captureScreenshotBrowser();
|
|
85
|
+
}
|
|
86
|
+
console.log('📷 [SCREENSHOT] Screenshot captured successfully');
|
|
87
|
+
if (onScreenshot && dataUrl) {
|
|
88
|
+
onScreenshot(dataUrl);
|
|
89
|
+
}
|
|
90
|
+
setIsCapturing(false);
|
|
91
|
+
screenshotInitiatedRef.current = false;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('📷 [SCREENSHOT] Error capturing screenshot:', error);
|
|
94
|
+
setIsCapturing(false);
|
|
95
|
+
screenshotInitiatedRef.current = false;
|
|
96
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to capture screenshot';
|
|
97
|
+
if (!errorMessage.includes('cancelled')) {
|
|
98
|
+
alert(`Failed to capture screenshot: ${errorMessage}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}, [isCapturing, onScreenshot]);
|
|
102
|
+
return jsx("button", {
|
|
103
|
+
type: "button",
|
|
104
|
+
onClick: handleScreenshot,
|
|
105
|
+
disabled: isCapturing,
|
|
106
|
+
className: cn('inline-flex h-7 w-7 items-center justify-center rounded-2xl transition-colors hover:bg-secondary/70 active:bg-secondary text-muted-foreground disabled:opacity-50', className),
|
|
107
|
+
title: "Take screenshot",
|
|
108
|
+
"aria-label": "Take screenshot",
|
|
109
|
+
children: jsx(Camera, {
|
|
110
|
+
className: "h-4 w-4"
|
|
111
|
+
})
|
|
112
|
+
});
|
|
113
|
+
}export{ChatScreenshot};//# sourceMappingURL=ChatScreenshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatScreenshot.js","sources":["../../src/components/ChatScreenshot.tsx"],"sourcesContent":[null],"names":["_jsx"],"mappings":"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"}
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import type { AttachedFile } from '../types/chat';
|
|
2
|
+
type SearchMode = 'search' | 'creative' | 'insights';
|
|
3
|
+
type PerplexitySearchProps = {
|
|
4
|
+
onSubmit: (query: string, mode: SearchMode, attachments?: AttachedFile[]) => void;
|
|
5
|
+
isLoading?: boolean;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare function PerplexitySearch({ onSubmit, isLoading, disabled }: PerplexitySearchProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export type { AttachedFile, PerplexitySearchProps, SearchMode };
|
|
2
10
|
//# sourceMappingURL=SearchBar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchBar.d.ts","sourceRoot":"","sources":["../../src/components/SearchBar.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SearchBar.d.ts","sourceRoot":"","sources":["../../src/components/SearchBar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAkBlD,KAAK,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAC;AACrD,KAAK,qBAAqB,GAAG;IACzB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IAClF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAgGF,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,SAAiB,EAAE,QAAgB,EAAE,EAAE,qBAAqB,2CA6QxG;AAED,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,UAAU,EAAE,CAAC"}
|