@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.
Files changed (79) hide show
  1. package/lib/api/index.d.ts +2 -0
  2. package/lib/api/index.d.ts.map +1 -0
  3. package/lib/api/llm.d.ts +34 -0
  4. package/lib/api/llm.d.ts.map +1 -0
  5. package/lib/api/llm.js +200 -0
  6. package/lib/api/llm.js.map +1 -0
  7. package/lib/api/stt.d.ts +21 -0
  8. package/lib/api/stt.d.ts.map +1 -0
  9. package/lib/api/stt.js +111 -0
  10. package/lib/api/stt.js.map +1 -0
  11. package/lib/components/AttachmentPreview.d.ts +10 -0
  12. package/lib/components/AttachmentPreview.d.ts.map +1 -0
  13. package/lib/components/AttachmentPreview.js +50 -0
  14. package/lib/components/AttachmentPreview.js.map +1 -0
  15. package/lib/components/AudioRecorder.d.ts +8 -0
  16. package/lib/components/AudioRecorder.d.ts.map +1 -0
  17. package/lib/components/AudioRecorder.js +221 -0
  18. package/lib/components/AudioRecorder.js.map +1 -0
  19. package/lib/components/AudioVisualizer.d.ts +7 -0
  20. package/lib/components/AudioVisualizer.d.ts.map +1 -0
  21. package/lib/components/AudioVisualizer.js +116 -0
  22. package/lib/components/AudioVisualizer.js.map +1 -0
  23. package/lib/components/ChatFiles.d.ts +8 -0
  24. package/lib/components/ChatFiles.d.ts.map +1 -0
  25. package/lib/components/ChatFiles.js +76 -0
  26. package/lib/components/ChatFiles.js.map +1 -0
  27. package/lib/components/ChatScreenshot.d.ts +7 -0
  28. package/lib/components/ChatScreenshot.d.ts.map +1 -0
  29. package/lib/components/ChatScreenshot.js +113 -0
  30. package/lib/components/ChatScreenshot.js.map +1 -0
  31. package/lib/components/SearchBar.d.ts +9 -1
  32. package/lib/components/SearchBar.d.ts.map +1 -1
  33. package/lib/components/SearchBar.js +176 -240
  34. package/lib/components/SearchBar.js.map +1 -1
  35. package/lib/config/constants.d.ts +108 -0
  36. package/lib/config/constants.d.ts.map +1 -0
  37. package/lib/config/constants.js +115 -0
  38. package/lib/config/constants.js.map +1 -0
  39. package/lib/config/env.d.ts +20 -0
  40. package/lib/config/env.d.ts.map +1 -0
  41. package/lib/config/env.js +22 -0
  42. package/lib/config/env.js.map +1 -0
  43. package/lib/config/index.d.ts +4 -0
  44. package/lib/config/index.d.ts.map +1 -0
  45. package/lib/config/providers.d.ts +54 -0
  46. package/lib/config/providers.d.ts.map +1 -0
  47. package/lib/config/providers.js +65 -0
  48. package/lib/config/providers.js.map +1 -0
  49. package/lib/pages/home/HomePage.d.ts +1 -3
  50. package/lib/pages/home/HomePage.d.ts.map +1 -1
  51. package/lib/pages/home/HomePage.js +232 -4
  52. package/lib/pages/home/HomePage.js.map +1 -1
  53. package/lib/platform/browser.d.ts +18 -0
  54. package/lib/platform/browser.d.ts.map +1 -0
  55. package/lib/platform/context.d.ts +76 -0
  56. package/lib/platform/context.d.ts.map +1 -0
  57. package/lib/platform/index.d.ts +25 -0
  58. package/lib/platform/index.d.ts.map +1 -0
  59. package/lib/platform/tauri.d.ts +35 -0
  60. package/lib/platform/tauri.d.ts.map +1 -0
  61. package/lib/platform/types.d.ts +164 -0
  62. package/lib/platform/types.d.ts.map +1 -0
  63. package/lib/state/chatMachine.d.ts +148 -0
  64. package/lib/state/chatMachine.d.ts.map +1 -0
  65. package/lib/state/chatMachine.js +458 -0
  66. package/lib/state/chatMachine.js.map +1 -0
  67. package/lib/state/index.d.ts +3 -0
  68. package/lib/state/index.d.ts.map +1 -0
  69. package/lib/state/useChatWithPlatform.d.ts +43 -0
  70. package/lib/state/useChatWithPlatform.d.ts.map +1 -0
  71. package/lib/types/chat.d.ts +56 -0
  72. package/lib/types/chat.d.ts.map +1 -0
  73. package/lib/types/index.d.ts +2 -0
  74. package/lib/types/index.d.ts.map +1 -0
  75. package/lib/utils/chatStorage.d.ts +25 -0
  76. package/lib/utils/chatStorage.d.ts.map +1 -0
  77. package/lib/utils/chatStorage.js +17 -0
  78. package/lib/utils/chatStorage.js.map +1 -0
  79. 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":"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"}
@@ -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
- export declare function PerplexitySearch(): import("react/jsx-runtime").JSX.Element;
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":"AA0JA,wBAAgB,gBAAgB,4CAmN/B"}
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"}