Jayreddin commited on
Commit
a87a430
·
verified ·
1 Parent(s): 2f211ef

undefined - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +653 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Jjjj
3
- emoji: 🌖
4
- colorFrom: gray
5
- colorTo: purple
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: jjjj
3
+ emoji: 🐳
4
+ colorFrom: pink
5
+ colorTo: yellow
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,653 @@
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>Puter AI Chat</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
+ <script>
10
+ tailwind.config = {
11
+ theme: {
12
+ extend: {
13
+ colors: {
14
+ dark: {
15
+ 900: '#121212',
16
+ 800: '#1e1e1e',
17
+ 700: '#2d2d2d',
18
+ 600: '#3d3d3d',
19
+ }
20
+ }
21
+ }
22
+ }
23
+ }
24
+ </script>
25
+ <style>
26
+ .scrollbar-hidden::-webkit-scrollbar {
27
+ display: none;
28
+ }
29
+ .scrollbar-hidden {
30
+ -ms-overflow-style: none;
31
+ scrollbar-width: none;
32
+ }
33
+ .fade-in {
34
+ animation: fadeIn 0.3s ease-in;
35
+ }
36
+ @keyframes fadeIn {
37
+ from { opacity: 0; transform: translateY(10px); }
38
+ to { opacity: 1; transform: translateY(0); }
39
+ }
40
+ .model-card {
41
+ transition: all 0.2s ease;
42
+ }
43
+ .model-card:hover {
44
+ transform: translateY(-2px);
45
+ box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
46
+ }
47
+ .settings-tab {
48
+ transition: all 0.3s ease;
49
+ }
50
+ </style>
51
+ </head>
52
+ <body class="bg-gray-100 dark:bg-dark-900 text-gray-900 dark:text-gray-100 min-h-screen flex flex-col">
53
+ <!-- Header -->
54
+ <header class="bg-white dark:bg-dark-800 shadow-md p-4 flex items-center justify-between z-10">
55
+ <div class="flex items-center space-x-3">
56
+ <button id="newChatBtn" class="p-2 rounded-full bg-blue-500 text-white hover:bg-blue-600 transition-colors">
57
+ <i class="fas fa-plus"></i>
58
+ </button>
59
+ <h1 class="text-xl font-bold">Puter AI Chat</h1>
60
+ </div>
61
+
62
+ <div class="flex items-center space-x-3">
63
+ <div class="relative">
64
+ <select id="modelSelect" class="bg-white dark:bg-dark-700 border border-gray-300 dark:border-dark-600 rounded-lg py-2 pl-3 pr-8 appearance-none focus:outline-none focus:ring-2 focus:ring-blue-500">
65
+ <!-- Options will be populated by JavaScript -->
66
+ </select>
67
+ <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700 dark:text-gray-300">
68
+ <i class="fas fa-chevron-down"></i>
69
+ </div>
70
+ </div>
71
+
72
+ <button id="settingsBtn" class="p-2 rounded-full hover:bg-gray-200 dark:hover:bg-dark-700 transition-colors">
73
+ <i class="fas fa-cog text-xl"></i>
74
+ </button>
75
+ </div>
76
+ </header>
77
+
78
+ <!-- Chat Container -->
79
+ <div id="chatContainer" class="flex-1 overflow-y-auto p-4 scrollbar-hidden">
80
+ <!-- Messages will be inserted here -->
81
+ <div class="flex justify-center items-center h-full text-gray-500 dark:text-gray-400">
82
+ <div class="text-center">
83
+ <i class="fas fa-robot text-4xl mb-2"></i>
84
+ <p>Start a conversation with Puter AI</p>
85
+ </div>
86
+ </div>
87
+ </div>
88
+
89
+ <!-- Input Area -->
90
+ <div class="bg-white dark:bg-dark-800 border-t border-gray-200 dark:border-dark-700 p-4">
91
+ <div class="max-w-4xl mx-auto flex">
92
+ <input
93
+ type="text"
94
+ id="messageInput"
95
+ placeholder="Type your message..."
96
+ class="flex-1 bg-gray-100 dark:bg-dark-700 border border-gray-300 dark:border-dark-600 rounded-l-lg py-3 px-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
97
+ >
98
+ <button
99
+ id="sendBtn"
100
+ class="bg-blue-500 hover:bg-blue-600 text-white px-6 rounded-r-lg transition-colors"
101
+ >
102
+ <i class="fas fa-paper-plane"></i>
103
+ </button>
104
+ </div>
105
+ </div>
106
+
107
+ <!-- Footer -->
108
+ <footer class="bg-white dark:bg-dark-800 border-t border-gray-200 dark:border-dark-700 py-3 text-center text-sm text-gray-600 dark:text-gray-400">
109
+ Created By <a href="https://jayreddin.github.io/" target="_blank" class="text-blue-500 hover:underline">Jamie Reddin</a>
110
+ using <a href="https://puter.com/" target="_blank" class="text-blue-500 hover:underline">Puter.com</a> | Version: 1.0
111
+ </footer>
112
+
113
+ <!-- Settings Modal -->
114
+ <div id="settingsModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50 hidden">
115
+ <div class="bg-white dark:bg-dark-800 rounded-xl w-full max-w-2xl max-h-[90vh] overflow-hidden flex flex-col">
116
+ <div class="p-5 border-b border-gray-200 dark:border-dark-700 flex justify-between items-center">
117
+ <h2 class="text-xl font-bold">Settings</h2>
118
+ <button id="closeSettings" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
119
+ <i class="fas fa-times"></i>
120
+ </button>
121
+ </div>
122
+
123
+ <div class="flex border-b border-gray-200 dark:border-dark-700">
124
+ <button class="settings-tab px-5 py-3 font-medium border-b-2 border-blue-500 text-blue-500">UI Settings</button>
125
+ <button class="settings-tab px-5 py-3 font-medium text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">AI Models</button>
126
+ </div>
127
+
128
+ <div class="flex-1 overflow-y-auto p-5">
129
+ <!-- UI Settings Tab -->
130
+ <div id="uiSettingsTab">
131
+ <div class="mb-6">
132
+ <label class="block mb-2 font-medium">Theme</label>
133
+ <select id="themeSelect" class="w-full bg-white dark:bg-dark-700 border border-gray-300 dark:border-dark-600 rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-blue-500">
134
+ <option value="light">Light</option>
135
+ <option value="dark">Dark</option>
136
+ </select>
137
+ </div>
138
+
139
+ <div>
140
+ <label class="block mb-2 font-medium">Text Size: <span id="textSizeValue">50%</span></label>
141
+ <input
142
+ type="range"
143
+ id="textSizeSlider"
144
+ min="1"
145
+ max="100"
146
+ value="50"
147
+ class="w-full h-2 bg-gray-200 dark:bg-dark-700 rounded-lg appearance-none cursor-pointer"
148
+ >
149
+ </div>
150
+ </div>
151
+
152
+ <!-- AI Models Tab -->
153
+ <div id="aiModelsTab" class="hidden">
154
+ <div class="mb-4">
155
+ <p class="text-gray-600 dark:text-gray-400 mb-3">Select models to include in the dropdown:</p>
156
+ <button id="selectAllBtn" class="text-sm bg-blue-500 hover:bg-blue-600 text-white px-3 py-1 rounded mr-2">Select All</button>
157
+ <button id="deselectAllBtn" class="text-sm bg-gray-500 hover:bg-gray-600 text-white px-3 py-1 rounded">Deselect All</button>
158
+ </div>
159
+
160
+ <div class="space-y-6">
161
+ <!-- Provider sections will be generated here -->
162
+ </div>
163
+ </div>
164
+ </div>
165
+
166
+ <div class="p-5 border-t border-gray-200 dark:border-dark-700 flex justify-end space-x-3">
167
+ <button id="cancelSettings" class="px-4 py-2 border border-gray-300 dark:border-dark-600 rounded-lg hover:bg-gray-100 dark:hover:bg-dark-700">Cancel</button>
168
+ <button id="saveSettings" class="px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-lg">Save Changes</button>
169
+ </div>
170
+ </div>
171
+ </div>
172
+
173
+ <script>
174
+ // App state
175
+ const state = {
176
+ currentChat: [],
177
+ selectedModel: 'gpt-4o',
178
+ enabledModels: [
179
+ 'gpt-5', 'gpt-4.1', 'gpt-4o',
180
+ 'claude-sonnet-4', 'google/gemini-2.0-flash-exp:free',
181
+ 'qwen/qwen3-235b-a22b-07-25', 'meta-llama/llama-4-maverick',
182
+ 'deepseek-chat', 'x-ai/grok-4'
183
+ ],
184
+ theme: 'light',
185
+ textSize: 50,
186
+ models: {
187
+ 'OpenAI': [
188
+ 'gpt-5', 'gpt-5-mini', 'gpt-5-nano', 'gpt-5-chat-latest',
189
+ 'gpt-4.1', 'gpt-4.1-mini', 'gpt-4.1-nano', 'gpt-4.5-preview',
190
+ 'gpt-4o', 'gpt-4o-mini', 'o1', 'o1-mini', 'o1-pro', 'o3', 'o3-mini', 'o4-mini'
191
+ ],
192
+ 'Claude': [
193
+ 'claude-sonnet-4', 'claude-opus-4', 'claude-3-7-sonnet', 'claude-3-5-sonnet'
194
+ ],
195
+ 'DeepSeek': [
196
+ 'deepseek-chat', 'deepseek-reasoner'
197
+ ],
198
+ 'Gemini': [
199
+ 'google/gemini-2.0-flash-001', 'google/gemini-2.0-flash-exp:free',
200
+ 'google/gemini-2.0-flash-lite-001', 'google/gemini-2.5-flash',
201
+ 'google/gemini-2.5-flash-lite-preview-06-17', 'google/gemini-2.5-flash-preview',
202
+ 'google/gemini-2.5-flash-preview-05-20', 'google/gemini-2.5-flash-preview-05-20:thinking',
203
+ 'google/gemini-2.5-flash-preview:thinking', 'google/gemini-2.5-pro',
204
+ 'google/gemini-2.5-pro-exp-03-25', 'google/gemini-2.5-pro-preview',
205
+ 'google/gemini-2.5-pro-preview-05-06', 'google/gemini-flash-1.5',
206
+ 'google/gemini-flash-1.5-8b', 'google/gemini-pro-1.5'
207
+ ],
208
+ 'Llama': [
209
+ 'meta-llama/llama-3-70b-instruct', 'meta-llama/llama-3-8b-instruct',
210
+ 'meta-llama/llama-3.1-405b', 'meta-llama/llama-3.1-405b-instruct',
211
+ 'meta-llama/llama-3.1-70b-instruct', 'meta-llama/llama-3.1-8b-instruct',
212
+ 'meta-llama/llama-3.2-11b-vision-instruct', 'meta-llama/llama-3.2-11b-vision-instruct:free',
213
+ 'meta-llama/llama-3.2-1b-instruct', 'meta-llama/llama-3.2-3b-instruct',
214
+ 'meta-llama/llama-3.2-90b-vision-instruct', 'meta-llama/llama-3.3-70b-instruct',
215
+ 'meta-llama/llama-3.3-70b-instruct:free', 'meta-llama/llama-4-maverick',
216
+ 'meta-llama/llama-4-maverick:free', 'meta-llama/llama-4-scout',
217
+ 'meta-llama/llama-4-scout:free', 'meta-llama/llama-guard-2-8b',
218
+ 'meta-llama/llama-guard-3-8b', 'meta-llama/llama-guard-4-12b'
219
+ ],
220
+ 'Grok': [
221
+ 'x-ai/grok-2-1212', 'x-ai/grok-2-vision-1212', 'x-ai/grok-3',
222
+ 'x-ai/grok-3-beta', 'x-ai/grok-3-mini', 'x-ai/grok-3-mini-beta',
223
+ 'x-ai/grok-4', 'x-ai/grok-vision-beta'
224
+ ],
225
+ 'Mistral': [
226
+ 'mistral-large-latest', 'mistral-medium-latest', 'mistral-small-latest',
227
+ 'open-mistral-nemo', 'codestral-latest'
228
+ ],
229
+ 'Qwen': [
230
+ 'qwen/qwen-2-72b-instruct', 'qwen/qwen-2.5-72b-instruct',
231
+ 'qwen/qwen-2.5-72b-instruct:free', 'qwen/qwen-2.5-7b-instruct',
232
+ 'qwen/qwen-2.5-coder-32b-instruct', 'qwen/qwen-2.5-coder-32b-instruct:free',
233
+ 'qwen/qwen-2.5-vl-7b-instruct', 'qwen/qwen-max', 'qwen/qwen-plus',
234
+ 'qwen/qwen-turbo', 'qwen/qwen-vl-max', 'qwen/qwen-vl-plus',
235
+ 'qwen/qwen2.5-vl-32b-instruct', 'qwen/qwen2.5-vl-32b-instruct:free',
236
+ 'qwen/qwen2.5-vl-72b-instruct', 'qwen/qwen2.5-vl-72b-instruct:free',
237
+ 'qwen/qwen3-14b', 'qwen/qwen3-14b:free', 'qwen/qwen3-235b-a22b',
238
+ 'qwen/qwen3-235b-a22b-07-25', 'qwen/qwen3-235b-a22b-07-25:free',
239
+ 'qwen/qwen3-235b-a22b:free', 'qwen/qwen3-30b-a3b', 'qwen/qwen3-30b-a3b:free',
240
+ 'qwen/qwen3-32b', 'qwen/qwen3-4b:free', 'qwen/qwen3-8b', 'qwen/qwen3-8b:free',
241
+ 'qwen/qwen3-coder', 'qwen/qwq-32b', 'qwen/qwq-32b-preview', 'qwen/qwq-32b:free'
242
+ ]
243
+ }
244
+ };
245
+
246
+ // DOM Elements
247
+ const elements = {
248
+ chatContainer: document.getElementById('chatContainer'),
249
+ messageInput: document.getElementById('messageInput'),
250
+ sendBtn: document.getElementById('sendBtn'),
251
+ modelSelect: document.getElementById('modelSelect'),
252
+ newChatBtn: document.getElementById('newChatBtn'),
253
+ settingsBtn: document.getElementById('settingsBtn'),
254
+ settingsModal: document.getElementById('settingsModal'),
255
+ closeSettings: document.getElementById('closeSettings'),
256
+ cancelSettings: document.getElementById('cancelSettings'),
257
+ saveSettings: document.getElementById('saveSettings'),
258
+ uiSettingsTab: document.getElementById('uiSettingsTab'),
259
+ aiModelsTab: document.getElementById('aiModelsTab'),
260
+ themeSelect: document.getElementById('themeSelect'),
261
+ textSizeSlider: document.getElementById('textSizeSlider'),
262
+ textSizeValue: document.getElementById('textSizeValue'),
263
+ selectAllBtn: document.getElementById('selectAllBtn'),
264
+ deselectAllBtn: document.getElementById('deselectAllBtn')
265
+ };
266
+
267
+ // Initialize the app
268
+ function initApp() {
269
+ renderModelOptions();
270
+ setupEventListeners();
271
+ loadSettings();
272
+ updateTheme();
273
+ }
274
+
275
+ // Render model options in dropdown
276
+ function renderModelOptions() {
277
+ elements.modelSelect.innerHTML = '';
278
+ state.enabledModels.forEach(model => {
279
+ const option = document.createElement('option');
280
+ option.value = model;
281
+ option.textContent = model;
282
+ if (model === state.selectedModel) {
283
+ option.selected = true;
284
+ }
285
+ elements.modelSelect.appendChild(option);
286
+ });
287
+ }
288
+
289
+ // Setup event listeners
290
+ function setupEventListeners() {
291
+ // Send message on button click
292
+ elements.sendBtn.addEventListener('click', sendMessage);
293
+
294
+ // Send message on Enter key
295
+ elements.messageInput.addEventListener('keypress', (e) => {
296
+ if (e.key === 'Enter') {
297
+ sendMessage();
298
+ }
299
+ });
300
+
301
+ // Model selection change
302
+ elements.modelSelect.addEventListener('change', (e) => {
303
+ state.selectedModel = e.target.value;
304
+ });
305
+
306
+ // New chat
307
+ elements.newChatBtn.addEventListener('click', () => {
308
+ state.currentChat = [];
309
+ renderChat();
310
+ });
311
+
312
+ // Settings modal
313
+ elements.settingsBtn.addEventListener('click', () => {
314
+ renderSettingsModal();
315
+ elements.settingsModal.classList.remove('hidden');
316
+ });
317
+
318
+ elements.closeSettings.addEventListener('click', () => {
319
+ elements.settingsModal.classList.add('hidden');
320
+ });
321
+
322
+ elements.cancelSettings.addEventListener('click', () => {
323
+ elements.settingsModal.classList.add('hidden');
324
+ });
325
+
326
+ // Close modal when clicking outside
327
+ elements.settingsModal.addEventListener('click', (e) => {
328
+ if (e.target === elements.settingsModal) {
329
+ elements.settingsModal.classList.add('hidden');
330
+ }
331
+ });
332
+
333
+ // Save settings
334
+ elements.saveSettings.addEventListener('click', saveSettings);
335
+
336
+ // UI Settings tab
337
+ document.querySelectorAll('.settings-tab')[0].addEventListener('click', () => {
338
+ elements.uiSettingsTab.classList.remove('hidden');
339
+ elements.aiModelsTab.classList.add('hidden');
340
+ document.querySelectorAll('.settings-tab')[0].classList.add('border-blue-500', 'text-blue-500');
341
+ document.querySelectorAll('.settings-tab')[0].classList.remove('text-gray-500', 'dark:text-gray-400');
342
+ document.querySelectorAll('.settings-tab')[1].classList.remove('border-blue-500', 'text-blue-500');
343
+ document.querySelectorAll('.settings-tab')[1].classList.add('text-gray-500', 'dark:text-gray-400');
344
+ });
345
+
346
+ // AI Models tab
347
+ document.querySelectorAll('.settings-tab')[1].addEventListener('click', () => {
348
+ elements.uiSettingsTab.classList.add('hidden');
349
+ elements.aiModelsTab.classList.remove('hidden');
350
+ document.querySelectorAll('.settings-tab')[1].classList.add('border-blue-500', 'text-blue-500');
351
+ document.querySelectorAll('.settings-tab')[1].classList.remove('text-gray-500', 'dark:text-gray-400');
352
+ document.querySelectorAll('.settings-tab')[0].classList.remove('border-blue-500', 'text-blue-500');
353
+ document.querySelectorAll('.settings-tab')[0].classList.add('text-gray-500', 'dark:text-gray-400');
354
+ });
355
+
356
+ // Text size slider
357
+ elements.textSizeSlider.addEventListener('input', (e) => {
358
+ elements.textSizeValue.textContent = `${e.target.value}%`;
359
+ });
360
+
361
+ // Select all/deselect all
362
+ elements.selectAllBtn.addEventListener('click', () => {
363
+ document.querySelectorAll('.model-checkbox').forEach(checkbox => {
364
+ checkbox.checked = true;
365
+ });
366
+ });
367
+
368
+ elements.deselectAllBtn.addEventListener('click', () => {
369
+ document.querySelectorAll('.model-checkbox').forEach(checkbox => {
370
+ checkbox.checked = false;
371
+ });
372
+ });
373
+ }
374
+
375
+ // Render settings modal content
376
+ function renderSettingsModal() {
377
+ // UI Settings
378
+ elements.themeSelect.value = state.theme;
379
+ elements.textSizeSlider.value = state.textSize;
380
+ elements.textSizeValue.textContent = `${state.textSize}%`;
381
+
382
+ // AI Models
383
+ const aiModelsContainer = elements.aiModelsTab.querySelector('div');
384
+ aiModelsContainer.innerHTML = '';
385
+
386
+ Object.keys(state.models).forEach(provider => {
387
+ const providerSection = document.createElement('div');
388
+ providerSection.className = 'model-card bg-gray-100 dark:bg-dark-700 rounded-lg p-4';
389
+
390
+ const providerTitle = document.createElement('h3');
391
+ providerTitle.className = 'font-bold mb-3 text-lg';
392
+ providerTitle.textContent = provider;
393
+ providerSection.appendChild(providerTitle);
394
+
395
+ const modelsGrid = document.createElement('div');
396
+ modelsGrid.className = 'grid grid-cols-1 md:grid-cols-2 gap-2';
397
+
398
+ state.models[provider].forEach(model => {
399
+ const modelItem = document.createElement('div');
400
+ modelItem.className = 'flex items-center p-2 hover:bg-gray-200 dark:hover:bg-dark-600 rounded';
401
+
402
+ const checkbox = document.createElement('input');
403
+ checkbox.type = 'checkbox';
404
+ checkbox.className = 'model-checkbox mr-2 h-4 w-4 text-blue-500 rounded';
405
+ checkbox.id = `model-${model}`;
406
+ checkbox.value = model;
407
+ checkbox.checked = state.enabledModels.includes(model);
408
+
409
+ const label = document.createElement('label');
410
+ label.className = 'text-sm flex-1 cursor-pointer';
411
+ label.htmlFor = `model-${model}`;
412
+ label.textContent = model;
413
+
414
+ modelItem.appendChild(checkbox);
415
+ modelItem.appendChild(label);
416
+ modelsGrid.appendChild(modelItem);
417
+ });
418
+
419
+ providerSection.appendChild(modelsGrid);
420
+ aiModelsContainer.appendChild(providerSection);
421
+ });
422
+ }
423
+
424
+ // Save settings
425
+ function saveSettings() {
426
+ // Save UI settings
427
+ state.theme = elements.themeSelect.value;
428
+ state.textSize = parseInt(elements.textSizeSlider.value);
429
+
430
+ // Save AI model settings
431
+ const selectedModels = [];
432
+ document.querySelectorAll('.model-checkbox:checked').forEach(checkbox => {
433
+ selectedModels.push(checkbox.value);
434
+ });
435
+
436
+ if (selectedModels.length > 0) {
437
+ state.enabledModels = selectedModels;
438
+ state.selectedModel = selectedModels[0];
439
+ renderModelOptions();
440
+ }
441
+
442
+ // Apply settings
443
+ updateTheme();
444
+ updateTextSize();
445
+
446
+ // Close modal
447
+ elements.settingsModal.classList.add('hidden');
448
+ }
449
+
450
+ // Update theme
451
+ function updateTheme() {
452
+ if (state.theme === 'dark') {
453
+ document.documentElement.classList.add('dark');
454
+ } else {
455
+ document.documentElement.classList.remove('dark');
456
+ }
457
+ }
458
+
459
+ // Update text size
460
+ function updateTextSize() {
461
+ document.body.style.fontSize = `${state.textSize}%`;
462
+ }
463
+
464
+ // Load settings from localStorage
465
+ function loadSettings() {
466
+ const savedSettings = localStorage.getItem('puterAiChatSettings');
467
+ if (savedSettings) {
468
+ const settings = JSON.parse(savedSettings);
469
+ state.theme = settings.theme || 'light';
470
+ state.textSize = settings.textSize || 50;
471
+ state.enabledModels = settings.enabledModels || state.enabledModels;
472
+ state.selectedModel = settings.selectedModel || state.selectedModel;
473
+ }
474
+ }
475
+
476
+ // Save settings to localStorage
477
+ function saveSettingsToStorage() {
478
+ const settings = {
479
+ theme: state.theme,
480
+ textSize: state.textSize,
481
+ enabledModels: state.enabledModels,
482
+ selectedModel: state.selectedModel
483
+ };
484
+ localStorage.setItem('puterAiChatSettings', JSON.stringify(settings));
485
+ }
486
+
487
+ // Send message
488
+ function sendMessage() {
489
+ const message = elements.messageInput.value.trim();
490
+ if (!message) return;
491
+
492
+ // Add user message to chat
493
+ const userMessage = {
494
+ id: Date.now(),
495
+ text: message,
496
+ sender: 'user',
497
+ timestamp: new Date()
498
+ };
499
+
500
+ state.currentChat.unshift(userMessage);
501
+
502
+ // Add AI response placeholder
503
+ const aiMessage = {
504
+ id: Date.now() + 1,
505
+ text: 'Thinking...',
506
+ sender: 'ai',
507
+ model: state.selectedModel,
508
+ timestamp: new Date()
509
+ };
510
+
511
+ state.currentChat.unshift(aiMessage);
512
+
513
+ // Clear input and render chat
514
+ elements.messageInput.value = '';
515
+ renderChat();
516
+
517
+ // Call Puter AI API
518
+ puter.ai.chat(message, { model: state.selectedModel })
519
+ .then(response => {
520
+ // Update AI message with actual response
521
+ const aiMessageIndex = state.currentChat.findIndex(msg => msg.id === aiMessage.id);
522
+ if (aiMessageIndex !== -1) {
523
+ state.currentChat[aiMessageIndex].text = response;
524
+ state.currentChat[aiMessageIndex].timestamp = new Date();
525
+ renderChat();
526
+ }
527
+ })
528
+ .catch(error => {
529
+ console.error('AI API Error:', error);
530
+ // Update AI message with error response
531
+ const aiMessageIndex = state.currentChat.findIndex(msg => msg.id === aiMessage.id);
532
+ if (aiMessageIndex !== -1) {
533
+ state.currentChat[aiMessageIndex].text = "Sorry, I encountered an error processing your request.";
534
+ state.currentChat[aiMessageIndex].timestamp = new Date();
535
+ renderChat();
536
+ }
537
+ });
538
+ }
539
+
540
+ // Render chat messages
541
+ function renderChat() {
542
+ if (state.currentChat.length === 0) {
543
+ elements.chatContainer.innerHTML = `
544
+ <div class="flex justify-center items-center h-full text-gray-500 dark:text-gray-400">
545
+ <div class="text-center">
546
+ <i class="fas fa-robot text-4xl mb-2"></i>
547
+ <p>Start a conversation with Puter AI</p>
548
+ </div>
549
+ </div>
550
+ `;
551
+ return;
552
+ }
553
+
554
+ elements.chatContainer.innerHTML = '';
555
+
556
+ state.currentChat.forEach(message => {
557
+ const messageElement = document.createElement('div');
558
+ messageElement.className = `fade-in mb-6 ${message.sender === 'user' ? 'flex justify-end' : 'flex justify-start'}`;
559
+
560
+ const messageContent = document.createElement('div');
561
+ messageContent.className = `max-w-[80%] rounded-2xl p-4 ${
562
+ message.sender === 'user'
563
+ ? 'bg-blue-500 text-white rounded-br-none'
564
+ : 'bg-gray-200 dark:bg-dark-700 rounded-bl-none'
565
+ }`;
566
+
567
+ // Timestamp
568
+ const timestamp = document.createElement('div');
569
+ timestamp.className = `text-xs mb-1 ${
570
+ message.sender === 'user' ? 'text-blue-100' : 'text-gray-500 dark:text-gray-400'
571
+ }`;
572
+ timestamp.textContent = formatTime(message.timestamp);
573
+
574
+ // Model name for AI messages
575
+ if (message.sender === 'ai') {
576
+ const modelLabel = document.createElement('div');
577
+ modelLabel.className = 'text-xs font-bold mb-1 text-blue-500';
578
+ modelLabel.textContent = message.model;
579
+ messageContent.appendChild(modelLabel);
580
+ }
581
+
582
+ // Message text
583
+ const textElement = document.createElement('div');
584
+ textElement.className = 'mb-2';
585
+ textElement.textContent = message.text;
586
+ messageContent.appendChild(timestamp);
587
+ messageContent.appendChild(textElement);
588
+
589
+ // Action buttons
590
+ const actions = document.createElement('div');
591
+ actions.className = 'flex justify-end space-x-2 mt-2';
592
+
593
+ // Resend button
594
+ const resendBtn = document.createElement('button');
595
+ resendBtn.className = 'text-xs p-1 rounded hover:bg-black/10 dark:hover:bg-white/10';
596
+ resendBtn.innerHTML = '<i class="fas fa-redo"></i>';
597
+ resendBtn.title = 'Resend';
598
+ resendBtn.addEventListener('click', () => {
599
+ elements.messageInput.value = message.text;
600
+ elements.messageInput.focus();
601
+ });
602
+
603
+ // Copy button
604
+ const copyBtn = document.createElement('button');
605
+ copyBtn.className = 'text-xs p-1 rounded hover:bg-black/10 dark:hover:bg-white/10';
606
+ copyBtn.innerHTML = '<i class="fas fa-copy"></i>';
607
+ copyBtn.title = 'Copy';
608
+ copyBtn.addEventListener('click', () => {
609
+ navigator.clipboard.writeText(message.text);
610
+ // Visual feedback
611
+ const originalIcon = copyBtn.innerHTML;
612
+ copyBtn.innerHTML = '<i class="fas fa-check"></i>';
613
+ setTimeout(() => {
614
+ copyBtn.innerHTML = originalIcon;
615
+ }, 1000);
616
+ });
617
+
618
+ // Delete button
619
+ const deleteBtn = document.createElement('button');
620
+ deleteBtn.className = 'text-xs p-1 rounded hover:bg-black/10 dark:hover:bg-white/10';
621
+ deleteBtn.innerHTML = '<i class="fas fa-trash"></i>';
622
+ deleteBtn.title = 'Delete';
623
+ deleteBtn.addEventListener('click', () => {
624
+ state.currentChat = state.currentChat.filter(msg => msg.id !== message.id);
625
+ renderChat();
626
+ });
627
+
628
+ actions.appendChild(resendBtn);
629
+ actions.appendChild(copyBtn);
630
+ actions.appendChild(deleteBtn);
631
+ messageContent.appendChild(actions);
632
+
633
+ messageElement.appendChild(messageContent);
634
+ elements.chatContainer.appendChild(messageElement);
635
+ });
636
+
637
+ // Scroll to bottom
638
+ elements.chatContainer.scrollTop = elements.chatContainer.scrollHeight;
639
+ }
640
+
641
+ // Format time as HH:MM
642
+ function formatTime(date) {
643
+ return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
644
+ }
645
+
646
+ // Initialize the app when DOM is loaded
647
+ document.addEventListener('DOMContentLoaded', initApp);
648
+
649
+ // Save settings beforeunload
650
+ window.addEventListener('beforeunload', saveSettingsToStorage);
651
+ </script>
652
+ <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=Jayreddin/jjjj" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
653
+ </html>