chagtptmm commited on
Commit
b8e2197
·
verified ·
1 Parent(s): b8c0830

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +1042 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Chatbox
3
- emoji: 💻
4
- colorFrom: green
5
- colorTo: green
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: chatbox
3
+ emoji: 🐳
4
+ colorFrom: purple
5
+ colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1042 @@
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
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Advanced AI Client</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ .chat-message.user {
11
+ background-color: #f3f4f6;
12
+ border-radius: 1rem 1rem 0 1rem;
13
+ }
14
+ .chat-message.ai {
15
+ background-color: #e0f2fe;
16
+ border-radius: 1rem 1rem 1rem 0;
17
+ }
18
+ .typing-indicator {
19
+ display: inline-block;
20
+ }
21
+ .typing-indicator span {
22
+ height: 8px;
23
+ width: 8px;
24
+ background: #9ca3af;
25
+ border-radius: 50%;
26
+ display: inline-block;
27
+ margin: 0 2px;
28
+ opacity: 0.4;
29
+ }
30
+ .typing-indicator span:nth-child(1) {
31
+ animation: typing 1s infinite;
32
+ }
33
+ .typing-indicator span:nth-child(2) {
34
+ animation: typing 1s infinite 0.2s;
35
+ }
36
+ .typing-indicator span:nth-child(3) {
37
+ animation: typing 1s infinite 0.4s;
38
+ }
39
+ @keyframes typing {
40
+ 0%, 100% {
41
+ opacity: 0.4;
42
+ transform: translateY(0);
43
+ }
44
+ 50% {
45
+ opacity: 1;
46
+ transform: translateY(-5px);
47
+ }
48
+ }
49
+ .slide-in {
50
+ animation: slideIn 0.3s ease-out forwards;
51
+ }
52
+ @keyframes slideIn {
53
+ from {
54
+ transform: translateY(20px);
55
+ opacity: 0;
56
+ }
57
+ to {
58
+ transform: translateY(0);
59
+ opacity: 1;
60
+ }
61
+ }
62
+ .file-preview {
63
+ max-width: 200px;
64
+ max-height: 200px;
65
+ border-radius: 0.5rem;
66
+ margin-top: 0.5rem;
67
+ }
68
+ .progress-bar {
69
+ height: 4px;
70
+ background-color: #e5e7eb;
71
+ border-radius: 2px;
72
+ margin-top: 4px;
73
+ }
74
+ .progress-bar-fill {
75
+ height: 100%;
76
+ background-color: #3b82f6;
77
+ border-radius: 2px;
78
+ transition: width 0.3s ease;
79
+ }
80
+ #dropzone {
81
+ border: 2px dashed #d1d5db;
82
+ border-radius: 0.5rem;
83
+ transition: all 0.2s ease;
84
+ }
85
+ #dropzone.active {
86
+ border-color: #3b82f6;
87
+ background-color: #eff6ff;
88
+ }
89
+ </style>
90
+ </head>
91
+ <body class="bg-gray-50 min-h-screen">
92
+ <div class="container mx-auto max-w-6xl p-4">
93
+ <!-- Header -->
94
+ <header class="flex justify-between items-center mb-6">
95
+ <div class="flex items-center space-x-2">
96
+ <i class="fas fa-robot text-3xl text-blue-500"></i>
97
+ <h1 class="text-2xl font-bold text-gray-800">Advanced AI Client</h1>
98
+ </div>
99
+ <div class="flex space-x-2">
100
+ <button id="web-search-btn" class="p-2 rounded-full hover:bg-gray-200 transition" title="Web Search">
101
+ <i class="fas fa-globe text-gray-600"></i>
102
+ </button>
103
+ <button id="settings-btn" class="p-2 rounded-full hover:bg-gray-200 transition" title="Settings">
104
+ <i class="fas fa-cog text-gray-600"></i>
105
+ </button>
106
+ </div>
107
+ </header>
108
+
109
+ <!-- Main Content -->
110
+ <div class="flex flex-col md:flex-row gap-6">
111
+ <!-- Sidebar -->
112
+ <aside class="hidden md:block w-64 bg-white rounded-lg shadow p-4 h-fit sticky top-4">
113
+ <div class="mb-6">
114
+ <h2 class="font-semibold text-gray-700 mb-2">Conversations</h2>
115
+ <ul id="conversation-list" class="space-y-1">
116
+ <li class="p-2 rounded hover:bg-gray-100 cursor-pointer flex items-center justify-between bg-blue-50">
117
+ <span>New Chat</span>
118
+ <i class="fas fa-plus text-blue-500"></i>
119
+ </li>
120
+ <li class="p-2 rounded hover:bg-gray-100 cursor-pointer">API Configuration Help</li>
121
+ <li class="p-2 rounded hover:bg-gray-100 cursor-pointer">Creative Writing</li>
122
+ <li class="p-2 rounded hover:bg-gray-100 cursor-pointer">Code Assistance</li>
123
+ <li class="p-2 rounded hover:bg-gray-100 cursor-pointer">Multimodal Analysis</li>
124
+ </ul>
125
+ </div>
126
+ <div>
127
+ <h2 class="font-semibold text-gray-700 mb-2">Features</h2>
128
+ <div class="space-y-2">
129
+ <div class="flex items-center p-2 rounded hover:bg-gray-100 cursor-pointer">
130
+ <i class="fas fa-globe text-blue-500 mr-2"></i>
131
+ <span>Web Search</span>
132
+ </div>
133
+ <div class="flex items-center p-2 rounded hover:bg-gray-100 cursor-pointer">
134
+ <i class="fas fa-file-upload text-blue-500 mr-2"></i>
135
+ <span>File Upload</span>
136
+ </div>
137
+ <div class="flex items-center p-2 rounded hover:bg-gray-100 cursor-pointer">
138
+ <i class="fas fa-images text-blue-500 mr-2"></i>
139
+ <span>Multimodal</span>
140
+ </div>
141
+ </div>
142
+ </div>
143
+ <div class="mt-4">
144
+ <h2 class="font-semibold text-gray-700 mb-2">API Status</h2>
145
+ <div class="flex items-center mb-2">
146
+ <div class="w-3 h-3 rounded-full bg-green-500 mr-2"></div>
147
+ <span class="text-sm">Free API: Active</span>
148
+ </div>
149
+ <div class="flex items-center">
150
+ <div class="w-3 h-3 rounded-full bg-gray-300 mr-2"></div>
151
+ <span class="text-sm">Custom API: Inactive</span>
152
+ </div>
153
+ </div>
154
+ </aside>
155
+
156
+ <!-- Chat Area -->
157
+ <main class="flex-1 bg-white rounded-lg shadow overflow-hidden flex flex-col">
158
+ <div id="chat-container" class="flex-1 p-4 overflow-y-auto space-y-4 h-96">
159
+ <div class="chat-message ai p-4 max-w-[80%] slide-in">
160
+ <div class="flex items-start space-x-2">
161
+ <div class="bg-blue-500 text-white p-2 rounded-full">
162
+ <i class="fas fa-robot"></i>
163
+ </div>
164
+ <div>
165
+ <p class="font-medium text-gray-700">AI Assistant</p>
166
+ <p class="text-gray-600 mt-1">Hello! I'm your advanced AI assistant. You can use the free API or configure your own API key in the settings. I support web search, file uploads, and multimodal capabilities. How can I help you today?</p>
167
+ </div>
168
+ </div>
169
+ </div>
170
+ </div>
171
+
172
+ <!-- Input Area -->
173
+ <div class="border-t p-4 bg-gray-50">
174
+ <!-- File Upload Dropzone -->
175
+ <div id="dropzone" class="p-4 mb-3 text-center hidden">
176
+ <i class="fas fa-cloud-upload-alt text-3xl text-blue-500 mb-2"></i>
177
+ <p class="font-medium">Drop files here or click to upload</p>
178
+ <p class="text-sm text-gray-500 mt-1">Supports images, PDFs, and text files</p>
179
+ <input type="file" id="file-input" class="hidden" multiple>
180
+ </div>
181
+
182
+ <!-- Uploaded Files Preview -->
183
+ <div id="file-previews" class="mb-3 hidden"></div>
184
+
185
+ <!-- Web Search Toggle -->
186
+ <div id="web-search-toggle" class="flex items-center mb-3 hidden">
187
+ <label class="relative inline-flex items-center cursor-pointer">
188
+ <input type="checkbox" id="enable-web-search" class="sr-only peer">
189
+ <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-500"></div>
190
+ <span class="ml-3 text-sm font-medium text-gray-700">Enable Web Search</span>
191
+ </label>
192
+ </div>
193
+
194
+ <!-- Chat Input Form -->
195
+ <form id="chat-form" class="flex space-x-2">
196
+ <div class="flex-1 relative">
197
+ <input
198
+ type="text"
199
+ id="user-input"
200
+ placeholder="Type your message here..."
201
+ class="w-full border border-gray-300 rounded-full px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 pr-10"
202
+ autocomplete="off"
203
+ >
204
+ <button
205
+ type="button"
206
+ id="file-upload-btn"
207
+ class="absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-blue-500"
208
+ title="Upload files"
209
+ >
210
+ <i class="fas fa-paperclip"></i>
211
+ </button>
212
+ </div>
213
+ <button
214
+ type="submit"
215
+ class="bg-blue-500 hover:bg-blue-600 text-white rounded-full p-2 w-10 h-10 flex items-center justify-center transition"
216
+ >
217
+ <i class="fas fa-paper-plane"></i>
218
+ </button>
219
+ </form>
220
+ <div class="mt-2 text-xs text-gray-500 flex justify-between">
221
+ <span id="api-status">Using: Free API</span>
222
+ <span id="token-counter">Tokens: 0/1000 (Free limit)</span>
223
+ </div>
224
+ </div>
225
+ </main>
226
+ </div>
227
+ </div>
228
+
229
+ <!-- Settings Modal -->
230
+ <div id="settings-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 hidden z-50">
231
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-2xl max-h-[90vh] overflow-y-auto">
232
+ <div class="p-6">
233
+ <div class="flex justify-between items-center mb-4">
234
+ <h2 class="text-xl font-bold text-gray-800">Advanced API Configuration</h2>
235
+ <button id="close-settings" class="text-gray-500 hover:text-gray-700">
236
+ <i class="fas fa-times"></i>
237
+ </button>
238
+ </div>
239
+
240
+ <div class="space-y-6">
241
+ <!-- API Provider Section -->
242
+ <div>
243
+ <h3 class="font-medium text-gray-700 mb-2">API Provider</h3>
244
+ <div class="grid grid-cols-2 gap-3">
245
+ <label class="flex items-center space-x-2 cursor-pointer p-3 border rounded hover:bg-gray-50">
246
+ <input type="radio" name="api-provider" value="free" class="text-blue-500" checked>
247
+ <div>
248
+ <span class="font-medium">Free API</span>
249
+ <p class="text-xs text-gray-500">Limited capabilities, no setup required</p>
250
+ </div>
251
+ </label>
252
+ <label class="flex items-center space-x-2 cursor-pointer p-3 border rounded hover:bg-gray-50">
253
+ <input type="radio" name="api-provider" value="openai" class="text-blue-500">
254
+ <div>
255
+ <span class="font-medium">OpenAI</span>
256
+ <p class="text-xs text-gray-500">GPT-4, GPT-3.5, DALL·E</p>
257
+ </div>
258
+ </label>
259
+ <label class="flex items-center space-x-2 cursor-pointer p-3 border rounded hover:bg-gray-50">
260
+ <input type="radio" name="api-provider" value="anthropic" class="text-blue-500">
261
+ <div>
262
+ <span class="font-medium">Anthropic</span>
263
+ <p class="text-xs text-gray-500">Claude models</p>
264
+ </div>
265
+ </label>
266
+ <label class="flex items-center space-x-2 cursor-pointer p-3 border rounded hover:bg-gray-50">
267
+ <input type="radio" name="api-provider" value="google" class="text-blue-500">
268
+ <div>
269
+ <span class="font-medium">Google</span>
270
+ <p class="text-xs text-gray-500">Gemini, Vertex AI</p>
271
+ </div>
272
+ </label>
273
+ <label class="flex items-center space-x-2 cursor-pointer p-3 border rounded hover:bg-gray-50">
274
+ <input type="radio" name="api-provider" value="custom" class="text-blue-500">
275
+ <div>
276
+ <span class="font-medium">Custom API</span>
277
+ <p class="text-xs text-gray-500">Fully customizable endpoint</p>
278
+ </div>
279
+ </label>
280
+ </div>
281
+ </div>
282
+
283
+ <!-- API Key Section -->
284
+ <div id="api-key-section" class="hidden">
285
+ <h3 class="font-medium text-gray-700 mb-2">API Key</h3>
286
+ <div class="flex space-x-2">
287
+ <input
288
+ type="password"
289
+ id="api-key-input"
290
+ placeholder="Enter your API key"
291
+ class="flex-1 border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
292
+ >
293
+ <button id="toggle-api-key" class="bg-gray-100 hover:bg-gray-200 px-3 rounded">
294
+ <i class="fas fa-eye"></i>
295
+ </button>
296
+ </div>
297
+ <p class="text-xs text-gray-500 mt-1">Your API key is stored locally in your browser and never sent to our servers.</p>
298
+ </div>
299
+
300
+ <!-- Custom API Section -->
301
+ <div id="custom-api-section" class="hidden">
302
+ <h3 class="font-medium text-gray-700 mb-2">Custom API Configuration</h3>
303
+ <div class="space-y-3">
304
+ <div>
305
+ <label class="block text-sm font-medium text-gray-700 mb-1">API Endpoint</label>
306
+ <input
307
+ type="text"
308
+ id="api-endpoint-input"
309
+ placeholder="https://api.example.com/v1/chat"
310
+ class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
311
+ >
312
+ </div>
313
+ <div>
314
+ <label class="block text-sm font-medium text-gray-700 mb-1">Request Method</label>
315
+ <select id="api-method-select" class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
316
+ <option value="POST">POST</option>
317
+ <option value="GET">GET</option>
318
+ <option value="PUT">PUT</option>
319
+ </select>
320
+ </div>
321
+ <div>
322
+ <label class="block text-sm font-medium text-gray-700 mb-1">Headers (JSON)</label>
323
+ <textarea
324
+ id="api-headers-input"
325
+ placeholder='{"Content-Type": "application/json", "Authorization": "Bearer $API_KEY"}'
326
+ class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 h-20"
327
+ ></textarea>
328
+ </div>
329
+ <div>
330
+ <label class="block text-sm font-medium text-gray-700 mb-1">Request Body Template (JSON)</label>
331
+ <textarea
332
+ id="api-body-input"
333
+ placeholder='{"model": "$MODEL", "messages": $MESSAGES, "max_tokens": 1000}'
334
+ class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 h-20"
335
+ ></textarea>
336
+ </div>
337
+ </div>
338
+ </div>
339
+
340
+ <!-- Model Selection -->
341
+ <div>
342
+ <h3 class="font-medium text-gray-700 mb-2">Model Selection</h3>
343
+ <select id="model-select" class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
344
+ <option value="gpt-3.5-turbo">GPT-3.5 Turbo (Free)</option>
345
+ <option value="gpt-4">GPT-4</option>
346
+ <option value="claude-2">Claude 2</option>
347
+ <option value="gemini-pro">Gemini Pro</option>
348
+ <option value="custom">Custom Model</option>
349
+ </select>
350
+ </div>
351
+
352
+ <!-- Features Configuration -->
353
+ <div class="pt-4 border-t">
354
+ <h3 class="font-medium text-gray-700 mb-2">Features</h3>
355
+ <div class="space-y-3">
356
+ <label class="flex items-center justify-between cursor-pointer">
357
+ <div>
358
+ <span class="font-medium">Web Search</span>
359
+ <p class="text-xs text-gray-500">Enable internet search for up-to-date information</p>
360
+ </div>
361
+ <label class="relative inline-flex items-center cursor-pointer">
362
+ <input type="checkbox" id="enable-web-search-setting" class="sr-only peer">
363
+ <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-500"></div>
364
+ </label>
365
+ </label>
366
+ <label class="flex items-center justify-between cursor-pointer">
367
+ <div>
368
+ <span class="font-medium">File Upload</span>
369
+ <p class="text-xs text-gray-500">Process documents, images, and other files</p>
370
+ </div>
371
+ <label class="relative inline-flex items-center cursor-pointer">
372
+ <input type="checkbox" id="enable-file-upload-setting" class="sr-only peer">
373
+ <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-500"></div>
374
+ </label>
375
+ </label>
376
+ <label class="flex items-center justify-between cursor-pointer">
377
+ <div>
378
+ <span class="font-medium">Multimodal Support</span>
379
+ <p class="text-xs text-gray-500">Process both text and images together</p>
380
+ </div>
381
+ <label class="relative inline-flex items-center cursor-pointer">
382
+ <input type="checkbox" id="enable-multimodal-setting" class="sr-only peer">
383
+ <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-500"></div>
384
+ </label>
385
+ </label>
386
+ </div>
387
+ </div>
388
+
389
+ <!-- Save Button -->
390
+ <div class="pt-4 border-t">
391
+ <button id="save-settings" class="w-full bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded transition">
392
+ Save Configuration
393
+ </button>
394
+ </div>
395
+ </div>
396
+ </div>
397
+ </div>
398
+ </div>
399
+
400
+ <!-- Web Search Modal -->
401
+ <div id="web-search-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 hidden z-50">
402
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-md max-h-[90vh] overflow-y-auto">
403
+ <div class="p-6">
404
+ <div class="flex justify-between items-center mb-4">
405
+ <h2 class="text-xl font-bold text-gray-800">Web Search Configuration</h2>
406
+ <button id="close-web-search" class="text-gray-500 hover:text-gray-700">
407
+ <i class="fas fa-times"></i>
408
+ </button>
409
+ </div>
410
+
411
+ <div class="space-y-4">
412
+ <div>
413
+ <label class="block text-sm font-medium text-gray-700 mb-1">Search Query</label>
414
+ <input
415
+ type="text"
416
+ id="search-query-input"
417
+ placeholder="What would you like to search for?"
418
+ class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
419
+ >
420
+ </div>
421
+
422
+ <div>
423
+ <label class="block text-sm font-medium text-gray-700 mb-1">Search Engine</label>
424
+ <select id="search-engine-select" class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
425
+ <option value="google">Google</option>
426
+ <option value="bing">Bing</option>
427
+ <option value="custom">Custom</option>
428
+ </select>
429
+ </div>
430
+
431
+ <div id="custom-search-engine-section" class="hidden">
432
+ <label class="block text-sm font-medium text-gray-700 mb-1">Custom Search API</label>
433
+ <input
434
+ type="text"
435
+ id="custom-search-api-input"
436
+ placeholder="https://api.example.com/search?q={query}"
437
+ class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
438
+ >
439
+ </div>
440
+
441
+ <div>
442
+ <label class="block text-sm font-medium text-gray-700 mb-1">Number of Results</label>
443
+ <input
444
+ type="range"
445
+ id="search-results-count"
446
+ min="1"
447
+ max="10"
448
+ value="3"
449
+ class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"
450
+ >
451
+ <div class="flex justify-between text-xs text-gray-500">
452
+ <span>1</span>
453
+ <span>3</span>
454
+ <span>5</span>
455
+ <span>7</span>
456
+ <span>10</span>
457
+ </div>
458
+ </div>
459
+
460
+ <div class="pt-2">
461
+ <button id="perform-search" class="w-full bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded transition">
462
+ Perform Search
463
+ </button>
464
+ </div>
465
+ </div>
466
+ </div>
467
+ </div>
468
+ </div>
469
+
470
+ <script>
471
+ document.addEventListener('DOMContentLoaded', function() {
472
+ // DOM Elements
473
+ const settingsBtn = document.getElementById('settings-btn');
474
+ const closeSettings = document.getElementById('close-settings');
475
+ const settingsModal = document.getElementById('settings-modal');
476
+ const saveSettingsBtn = document.getElementById('save-settings');
477
+ const chatForm = document.getElementById('chat-form');
478
+ const userInput = document.getElementById('user-input');
479
+ const chatContainer = document.getElementById('chat-container');
480
+ const apiKeySection = document.getElementById('api-key-section');
481
+ const customApiSection = document.getElementById('custom-api-section');
482
+ const apiProviderRadios = document.querySelectorAll('input[name="api-provider"]');
483
+ const modelSelect = document.getElementById('model-select');
484
+ const tokenCounter = document.getElementById('token-counter');
485
+ const apiStatus = document.getElementById('api-status');
486
+ const fileUploadBtn = document.getElementById('file-upload-btn');
487
+ const dropzone = document.getElementById('dropzone');
488
+ const fileInput = document.getElementById('file-input');
489
+ const filePreviews = document.getElementById('file-previews');
490
+ const webSearchBtn = document.getElementById('web-search-btn');
491
+ const webSearchModal = document.getElementById('web-search-modal');
492
+ const closeWebSearch = document.getElementById('close-web-search');
493
+ const webSearchToggle = document.getElementById('web-search-toggle');
494
+ const enableWebSearch = document.getElementById('enable-web-search');
495
+ const toggleApiKey = document.getElementById('toggle-api-key');
496
+ const apiKeyInput = document.getElementById('api-key-input');
497
+ const customSearchEngineSection = document.getElementById('custom-search-engine-section');
498
+ const searchEngineSelect = document.getElementById('search-engine-select');
499
+ const performSearchBtn = document.getElementById('perform-search');
500
+
501
+ // State
502
+ let uploadedFiles = [];
503
+ let isWebSearchEnabled = false;
504
+ let searchResults = [];
505
+
506
+ // Load saved settings from localStorage
507
+ let settings = JSON.parse(localStorage.getItem('aiClientSettings')) || {
508
+ apiProvider: 'free',
509
+ apiKey: '',
510
+ apiEndpoint: '',
511
+ apiMethod: 'POST',
512
+ apiHeaders: '{"Content-Type": "application/json"}',
513
+ apiBody: '{"model": "$MODEL", "messages": $MESSAGES}',
514
+ model: 'gpt-3.5-turbo',
515
+ tokenUsage: 0,
516
+ features: {
517
+ webSearch: false,
518
+ fileUpload: true,
519
+ multimodal: false
520
+ }
521
+ };
522
+
523
+ // Update UI based on saved settings
524
+ updateUIFromSettings();
525
+
526
+ // Event Listeners
527
+ settingsBtn.addEventListener('click', () => {
528
+ settingsModal.classList.remove('hidden');
529
+ });
530
+
531
+ closeSettings.addEventListener('click', () => {
532
+ settingsModal.classList.add('hidden');
533
+ });
534
+
535
+ apiProviderRadios.forEach(radio => {
536
+ radio.addEventListener('change', function() {
537
+ updateAPIConfigurationUI();
538
+ });
539
+ });
540
+
541
+ searchEngineSelect.addEventListener('change', function() {
542
+ if (this.value === 'custom') {
543
+ customSearchEngineSection.classList.remove('hidden');
544
+ } else {
545
+ customSearchEngineSection.classList.add('hidden');
546
+ }
547
+ });
548
+
549
+ toggleApiKey.addEventListener('click', function() {
550
+ const type = apiKeyInput.getAttribute('type') === 'password' ? 'text' : 'password';
551
+ apiKeyInput.setAttribute('type', type);
552
+ this.innerHTML = type === 'password' ? '<i class="fas fa-eye"></i>' : '<i class="fas fa-eye-slash"></i>';
553
+ });
554
+
555
+ saveSettingsBtn.addEventListener('click', function() {
556
+ const selectedProvider = document.querySelector('input[name="api-provider"]:checked').value;
557
+ const apiKey = document.getElementById('api-key-input').value;
558
+ const apiEndpoint = document.getElementById('api-endpoint-input').value;
559
+ const apiMethod = document.getElementById('api-method-select').value;
560
+ const apiHeaders = document.getElementById('api-headers-input').value;
561
+ const apiBody = document.getElementById('api-body-input').value;
562
+ const model = document.getElementById('model-select').value;
563
+ const webSearchEnabled = document.getElementById('enable-web-search-setting').checked;
564
+ const fileUploadEnabled = document.getElementById('enable-file-upload-setting').checked;
565
+ const multimodalEnabled = document.getElementById('enable-multimodal-setting').checked;
566
+
567
+ settings = {
568
+ apiProvider: selectedProvider,
569
+ apiKey: apiKey,
570
+ apiEndpoint: apiEndpoint,
571
+ apiMethod: apiMethod,
572
+ apiHeaders: apiHeaders,
573
+ apiBody: apiBody,
574
+ model: model,
575
+ tokenUsage: settings.tokenUsage,
576
+ features: {
577
+ webSearch: webSearchEnabled,
578
+ fileUpload: fileUploadEnabled,
579
+ multimodal: multimodalEnabled
580
+ }
581
+ };
582
+
583
+ localStorage.setItem('aiClientSettings', JSON.stringify(settings));
584
+ updateUIFromSettings();
585
+ settingsModal.classList.add('hidden');
586
+
587
+ // Show confirmation
588
+ showToast('Settings saved successfully!', 'success');
589
+ });
590
+
591
+ fileUploadBtn.addEventListener('click', function() {
592
+ if (settings.features.fileUpload) {
593
+ fileInput.click();
594
+ } else {
595
+ showToast('File upload is disabled in settings', 'warning');
596
+ }
597
+ });
598
+
599
+ fileInput.addEventListener('change', function(e) {
600
+ handleFiles(e.target.files);
601
+ });
602
+
603
+ // Drag and drop functionality
604
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
605
+ dropzone.addEventListener(eventName, preventDefaults, false);
606
+ });
607
+
608
+ function preventDefaults(e) {
609
+ e.preventDefault();
610
+ e.stopPropagation();
611
+ }
612
+
613
+ ['dragenter', 'dragover'].forEach(eventName => {
614
+ dropzone.addEventListener(eventName, highlight, false);
615
+ });
616
+
617
+ ['dragleave', 'drop'].forEach(eventName => {
618
+ dropzone.addEventListener(eventName, unhighlight, false);
619
+ });
620
+
621
+ function highlight() {
622
+ dropzone.classList.add('active');
623
+ }
624
+
625
+ function unhighlight() {
626
+ dropzone.classList.remove('active');
627
+ }
628
+
629
+ dropzone.addEventListener('drop', function(e) {
630
+ if (settings.features.fileUpload) {
631
+ const dt = e.dataTransfer;
632
+ const files = dt.files;
633
+ handleFiles(files);
634
+ } else {
635
+ showToast('File upload is disabled in settings', 'warning');
636
+ }
637
+ });
638
+
639
+ dropzone.addEventListener('click', function() {
640
+ if (settings.features.fileUpload) {
641
+ fileInput.click();
642
+ } else {
643
+ showToast('File upload is disabled in settings', 'warning');
644
+ }
645
+ });
646
+
647
+ webSearchBtn.addEventListener('click', function() {
648
+ if (settings.features.webSearch) {
649
+ webSearchModal.classList.remove('hidden');
650
+ } else {
651
+ showToast('Web search is disabled in settings', 'warning');
652
+ }
653
+ });
654
+
655
+ closeWebSearch.addEventListener('click', function() {
656
+ webSearchModal.classList.add('hidden');
657
+ });
658
+
659
+ enableWebSearch.addEventListener('change', function() {
660
+ isWebSearchEnabled = this.checked;
661
+ if (this.checked) {
662
+ showToast('Web search enabled for this conversation', 'info');
663
+ }
664
+ });
665
+
666
+ performSearchBtn.addEventListener('click', function() {
667
+ const query = document.getElementById('search-query-input').value;
668
+ const searchEngine = document.getElementById('search-engine-select').value;
669
+ const customSearchAPI = document.getElementById('custom-search-api-input').value;
670
+ const resultCount = document.getElementById('search-results-count').value;
671
+
672
+ if (!query) {
673
+ showToast('Please enter a search query', 'warning');
674
+ return;
675
+ }
676
+
677
+ // Simulate search (in a real app, this would call an API)
678
+ simulateWebSearch(query, resultCount);
679
+ webSearchModal.classList.add('hidden');
680
+ });
681
+
682
+ chatForm.addEventListener('submit', function(e) {
683
+ e.preventDefault();
684
+ const message = userInput.value.trim();
685
+ if (message === '' && uploadedFiles.length === 0) return;
686
+
687
+ // Add user message to chat
688
+ addMessageToChat('user', message);
689
+ userInput.value = '';
690
+
691
+ // Show typing indicator
692
+ const typingIndicator = document.createElement('div');
693
+ typingIndicator.className = 'chat-message ai p-4 max-w-[80%]';
694
+ typingIndicator.innerHTML = `
695
+ <div class="flex items-start space-x-2">
696
+ <div class="bg-blue-500 text-white p-2 rounded-full">
697
+ <i class="fas fa-robot"></i>
698
+ </div>
699
+ <div>
700
+ <p class="font-medium text-gray-700">AI Assistant</p>
701
+ <div class="typing-indicator mt-1">
702
+ <span></span>
703
+ <span></span>
704
+ <span></span>
705
+ </div>
706
+ </div>
707
+ </div>
708
+ `;
709
+ chatContainer.appendChild(typingIndicator);
710
+ chatContainer.scrollTop = chatContainer.scrollHeight;
711
+
712
+ // Simulate API call with timeout
713
+ setTimeout(() => {
714
+ // Remove typing indicator
715
+ typingIndicator.remove();
716
+
717
+ // Generate response based on settings
718
+ let response;
719
+ if (settings.apiProvider === 'free') {
720
+ // Update token usage (random between 20-50 for demo)
721
+ const tokensUsed = Math.floor(Math.random() * 30) + 20;
722
+ settings.tokenUsage += tokensUsed;
723
+ localStorage.setItem('aiClientSettings', JSON.stringify(settings));
724
+ updateTokenCounter();
725
+
726
+ // Free API responses
727
+ const freeResponses = [
728
+ "I'm responding using the free API service. For more advanced features, consider using your own API key.",
729
+ "This is a sample response from the free tier. The free API has limited capabilities compared to premium services.",
730
+ "Free API response: I can help with basic queries. For complex tasks, you might want to configure your own API key in settings.",
731
+ "Using the free service, my responses are limited. Did you know you can connect your own API for better performance?",
732
+ "This is a simulated response from the free API. In a real implementation, this would connect to an actual AI service."
733
+ ];
734
+ response = freeResponses[Math.floor(Math.random() * freeResponses.length)];
735
+ } else {
736
+ // Simulate premium response
737
+ const premiumResponses = [
738
+ "This would be the actual response from your configured API. The web app would make a real API call to the service you specified.",
739
+ "If you had configured your API key, this response would come directly from the AI service provider you selected.",
740
+ "In a production environment, this would connect to the API endpoint you specified in the settings.",
741
+ "With a custom API, you would receive higher quality responses with more capabilities than the free tier.",
742
+ "Your own API key would enable more advanced features and longer conversations."
743
+ ];
744
+ response = premiumResponses[Math.floor(Math.random() * premiumResponses.length)];
745
+ }
746
+
747
+ // If web search was enabled, append results
748
+ if (isWebSearchEnabled && searchResults.length > 0) {
749
+ response += "\n\nWeb search results:\n";
750
+ searchResults.forEach((result, index) => {
751
+ response += `${index + 1}. ${result.title}\n ${result.url}\n`;
752
+ });
753
+ searchResults = []; // Clear results after showing
754
+ }
755
+
756
+ // If files were uploaded, mention them
757
+ if (uploadedFiles.length > 0) {
758
+ response += "\n\nI've processed the following files:\n";
759
+ uploadedFiles.forEach(file => {
760
+ response += `- ${file.name} (${file.type})\n`;
761
+ });
762
+
763
+ if (settings.features.multimodal) {
764
+ response += "\nI've analyzed the content of these files using multimodal capabilities.";
765
+ }
766
+
767
+ // Clear files after processing
768
+ uploadedFiles = [];
769
+ filePreviews.innerHTML = '';
770
+ filePreviews.classList.add('hidden');
771
+ dropzone.classList.add('hidden');
772
+ }
773
+
774
+ addMessageToChat('ai', response);
775
+ }, 1500);
776
+ });
777
+
778
+ // Functions
779
+ function updateAPIConfigurationUI() {
780
+ const selectedProvider = document.querySelector('input[name="api-provider"]:checked').value;
781
+
782
+ if (selectedProvider === 'free') {
783
+ apiKeySection.classList.add('hidden');
784
+ customApiSection.classList.add('hidden');
785
+ modelSelect.innerHTML = `
786
+ <option value="gpt-3.5-turbo">GPT-3.5 Turbo (Free)</option>
787
+ <option value="gpt-3.5-turbo-16k">GPT-3.5 Turbo 16k</option>
788
+ `;
789
+ } else if (selectedProvider === 'custom') {
790
+ apiKeySection.classList.remove('hidden');
791
+ customApiSection.classList.remove('hidden');
792
+ modelSelect.innerHTML = `
793
+ <option value="custom">Custom Model</option>
794
+ `;
795
+ } else {
796
+ apiKeySection.classList.remove('hidden');
797
+ customApiSection.classList.add('hidden');
798
+ if (selectedProvider === 'openai') {
799
+ modelSelect.innerHTML = `
800
+ <option value="gpt-3.5-turbo">GPT-3.5 Turbo</option>
801
+ <option value="gpt-4">GPT-4</option>
802
+ <option value="gpt-4-turbo">GPT-4 Turbo</option>
803
+ <option value="dall-e">DALL·E (Image Generation)</option>
804
+ `;
805
+ } else if (selectedProvider === 'anthropic') {
806
+ modelSelect.innerHTML = `
807
+ <option value="claude-2">Claude 2</option>
808
+ <option value="claude-instant">Claude Instant</option>
809
+ `;
810
+ } else if (selectedProvider === 'google') {
811
+ modelSelect.innerHTML = `
812
+ <option value="gemini-pro">Gemini Pro</option>
813
+ <option value="gemini-vision">Gemini Vision</option>
814
+ `;
815
+ }
816
+ }
817
+ }
818
+
819
+ function updateUIFromSettings() {
820
+ // Set radio button
821
+ document.querySelector(`input[name="api-provider"][value="${settings.apiProvider}"]`).checked = true;
822
+
823
+ // Set input fields
824
+ document.getElementById('api-key-input').value = settings.apiKey;
825
+ document.getElementById('api-endpoint-input').value = settings.apiEndpoint;
826
+ document.getElementById('api-method-select').value = settings.apiMethod;
827
+ document.getElementById('api-headers-input').value = settings.apiHeaders;
828
+ document.getElementById('api-body-input').value = settings.apiBody;
829
+
830
+ // Set model select
831
+ modelSelect.value = settings.model;
832
+
833
+ // Set feature toggles
834
+ document.getElementById('enable-web-search-setting').checked = settings.features.webSearch;
835
+ document.getElementById('enable-file-upload-setting').checked = settings.features.fileUpload;
836
+ document.getElementById('enable-multimodal-setting').checked = settings.features.multimodal;
837
+
838
+ // Update API configuration UI
839
+ updateAPIConfigurationUI();
840
+
841
+ // Update token counter
842
+ updateTokenCounter();
843
+
844
+ // Update status
845
+ apiStatus.textContent = `Using: ${settings.apiProvider === 'free' ? 'Free API' : `${settings.apiProvider.charAt(0).toUpperCase() + settings.apiProvider.slice(1)} API`}`;
846
+
847
+ // Show/hide web search toggle
848
+ if (settings.features.webSearch) {
849
+ webSearchToggle.classList.remove('hidden');
850
+ } else {
851
+ webSearchToggle.classList.add('hidden');
852
+ }
853
+ }
854
+
855
+ function updateTokenCounter() {
856
+ if (settings.apiProvider === 'free') {
857
+ const remaining = Math.max(0, 1000 - settings.tokenUsage);
858
+ tokenCounter.textContent = `Tokens: ${settings.tokenUsage}/1000 (${remaining} remaining)`;
859
+
860
+ if (remaining < 200) {
861
+ tokenCounter.classList.add('text-red-500');
862
+ tokenCounter.classList.remove('text-gray-500');
863
+ } else {
864
+ tokenCounter.classList.remove('text-red-500');
865
+ tokenCounter.classList.add('text-gray-500');
866
+ }
867
+ } else {
868
+ tokenCounter.textContent = `Using: ${settings.apiProvider.charAt(0).toUpperCase() + settings.apiProvider.slice(1)} API`;
869
+ tokenCounter.classList.remove('text-red-500');
870
+ tokenCounter.classList.add('text-gray-500');
871
+ }
872
+ }
873
+
874
+ function addMessageToChat(role, content) {
875
+ const messageDiv = document.createElement('div');
876
+ messageDiv.className = `chat-message ${role} p-4 max-w-[80%] slide-in`;
877
+
878
+ if (role === 'user') {
879
+ messageDiv.innerHTML = `
880
+ <div class="flex items-start space-x-2 justify-end">
881
+ <div>
882
+ <p class="font-medium text-gray-700 text-right">You</p>
883
+ <p class="text-gray-600 mt-1">${content}</p>
884
+ </div>
885
+ <div class="bg-gray-500 text-white p-2 rounded-full">
886
+ <i class="fas fa-user"></i>
887
+ </div>
888
+ </div>
889
+ `;
890
+ } else {
891
+ messageDiv.innerHTML = `
892
+ <div class="flex items-start space-x-2">
893
+ <div class="bg-blue-500 text-white p-2 rounded-full">
894
+ <i class="fas fa-robot"></i>
895
+ </div>
896
+ <div>
897
+ <p class="font-medium text-gray-700">AI Assistant</p>
898
+ <p class="text-gray-600 mt-1 whitespace-pre-line">${content}</p>
899
+ </div>
900
+ </div>
901
+ `;
902
+ }
903
+
904
+ chatContainer.appendChild(messageDiv);
905
+ chatContainer.scrollTop = chatContainer.scrollHeight;
906
+ }
907
+
908
+ function handleFiles(files) {
909
+ if (!settings.features.fileUpload) {
910
+ showToast('File upload is disabled in settings', 'warning');
911
+ return;
912
+ }
913
+
914
+ for (let i = 0; i < files.length; i++) {
915
+ const file = files[i];
916
+ if (file.size > 10 * 1024 * 1024) { // 10MB limit
917
+ showToast(`File ${file.name} is too large (max 10MB)`, 'error');
918
+ continue;
919
+ }
920
+
921
+ uploadedFiles.push(file);
922
+ previewFile(file);
923
+ }
924
+
925
+ if (uploadedFiles.length > 0) {
926
+ dropzone.classList.add('hidden');
927
+ filePreviews.classList.remove('hidden');
928
+ }
929
+ }
930
+
931
+ function previewFile(file) {
932
+ const previewDiv = document.createElement('div');
933
+ previewDiv.className = 'flex items-center justify-between p-2 bg-gray-100 rounded mb-2';
934
+
935
+ const fileInfoDiv = document.createElement('div');
936
+ fileInfoDiv.className = 'flex items-center space-x-2';
937
+
938
+ let icon;
939
+ if (file.type.startsWith('image/')) {
940
+ icon = '<i class="fas fa-image text-blue-500"></i>';
941
+
942
+ // Create image preview
943
+ const reader = new FileReader();
944
+ reader.onload = function(e) {
945
+ const imgPreview = document.createElement('img');
946
+ imgPreview.src = e.target.result;
947
+ imgPreview.className = 'file-preview';
948
+ fileInfoDiv.appendChild(imgPreview);
949
+ };
950
+ reader.readAsDataURL(file);
951
+ } else if (file.type === 'application/pdf') {
952
+ icon = '<i class="fas fa-file-pdf text-red-500"></i>';
953
+ } else if (file.type.startsWith('text/')) {
954
+ icon = '<i class="fas fa-file-alt text-green-500"></i>';
955
+ } else {
956
+ icon = '<i class="fas fa-file text-gray-500"></i>';
957
+ }
958
+
959
+ fileInfoDiv.innerHTML += `
960
+ ${icon}
961
+ <div>
962
+ <p class="text-sm font-medium">${file.name}</p>
963
+ <p class="text-xs text-gray-500">${formatFileSize(file.size)}</p>
964
+ <div class="progress-bar">
965
+ <div class="progress-bar-fill" style="width: 100%"></div>
966
+ </div>
967
+ </div>
968
+ `;
969
+
970
+ const removeBtn = document.createElement('button');
971
+ removeBtn.className = 'text-red-500 hover:text-red-700';
972
+ removeBtn.innerHTML = '<i class="fas fa-times"></i>';
973
+ removeBtn.onclick = function() {
974
+ uploadedFiles = uploadedFiles.filter(f => f !== file);
975
+ previewDiv.remove();
976
+ if (uploadedFiles.length === 0) {
977
+ filePreviews.classList.add('hidden');
978
+ dropzone.classList.remove('hidden');
979
+ }
980
+ };
981
+
982
+ previewDiv.appendChild(fileInfoDiv);
983
+ previewDiv.appendChild(removeBtn);
984
+ filePreviews.appendChild(previewDiv);
985
+ }
986
+
987
+ function formatFileSize(bytes) {
988
+ if (bytes < 1024) return bytes + ' bytes';
989
+ else if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
990
+ else return (bytes / 1048576).toFixed(1) + ' MB';
991
+ }
992
+
993
+ function simulateWebSearch(query, count) {
994
+ // Simulate search results
995
+ searchResults = [];
996
+ for (let i = 0; i < count; i++) {
997
+ searchResults.push({
998
+ title: `Search result for "${query}" (${i + 1})`,
999
+ url: `https://example.com/search?q=${encodeURIComponent(query)}&result=${i + 1}`,
1000
+ snippet: `This is a simulated snippet for search result ${i + 1} about "${query}". In a real implementation, this would come from an actual search API.`
1001
+ });
1002
+ }
1003
+
1004
+ showToast(`Found ${count} web results for "${query}"`, 'success');
1005
+ isWebSearchEnabled = true;
1006
+ enableWebSearch.checked = true;
1007
+ }
1008
+
1009
+ function showToast(message, type) {
1010
+ const colors = {
1011
+ success: 'bg-green-500',
1012
+ error: 'bg-red-500',
1013
+ warning: 'bg-yellow-500',
1014
+ info: 'bg-blue-500'
1015
+ };
1016
+
1017
+ const toast = document.createElement('div');
1018
+ toast.className = `fixed bottom-4 right-4 ${colors[type]} text-white px-4 py-2 rounded shadow-lg flex items-center space-x-2 animate-fade-in`;
1019
+ toast.innerHTML = `
1020
+ <i class="fas ${type === 'success' ? 'fa-check-circle' : type === 'error' ? 'fa-exclamation-circle' : type === 'warning' ? 'fa-exclamation-triangle' : 'fa-info-circle'}"></i>
1021
+ <span>${message}</span>
1022
+ `;
1023
+
1024
+ document.body.appendChild(toast);
1025
+
1026
+ setTimeout(() => {
1027
+ toast.classList.add('animate-fade-out');
1028
+ setTimeout(() => {
1029
+ toast.remove();
1030
+ }, 300);
1031
+ }, 3000);
1032
+ }
1033
+
1034
+ // Initialize UI
1035
+ updateTokenCounter();
1036
+ if (settings.features.fileUpload) {
1037
+ dropzone.classList.remove('hidden');
1038
+ }
1039
+ });
1040
+ </script>
1041
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=chagtptmm/chatbox" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
1042
+ </html>