Soltane777 commited on
Commit
fc03084
·
verified ·
1 Parent(s): 7cdf1d4

Upload 4 files

Browse files
Files changed (3) hide show
  1. frontend/index.html +124 -186
  2. frontend/script.js +660 -731
  3. frontend/style.css +455 -275
frontend/index.html CHANGED
@@ -1,25 +1,25 @@
1
  <!DOCTYPE html>
2
- <html lang="en" dir="ltr">
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>AI Web Application</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
- <link rel="stylesheet" href="/static/style.css">
9
  </head>
10
- <body class="bg-gradient-to-br from-gray-100 to-gray-200 dark:from-gray-900 dark:to-gray-800 font-sans min-h-screen transition-colors duration-200">
11
- <div class="max-w-6xl mx-auto mt-6 p-6">
12
- <div class="bg-white dark:bg-gray-800 rounded-2xl shadow-xl overflow-hidden transition-colors duration-200">
13
- <div class="bg-gradient-to-r from-purple-600 to-indigo-600 p-6 relative">
14
- <h1 class="text-3xl font-bold text-center text-white">AI Web Application</h1>
15
- <p class="text-center text-purple-100 mt-2">Integrated interface for AI services</p>
16
-
17
- <!-- Theme Toggle Button -->
18
- <div class="absolute right-6 top-6">
19
- <button id="theme-toggle" class="p-2 rounded-full bg-white/20 hover:bg-white/30 transition-colors" onclick="toggleTheme()">
20
  <!-- Sun icon (for dark mode) -->
21
  <svg id="sun-icon" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-yellow-300 hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor">
22
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
23
  </svg>
24
  <!-- Moon icon (for light mode) -->
25
  <svg id="moon-icon" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@@ -27,191 +27,129 @@
27
  </svg>
28
  </button>
29
  </div>
30
- </div>
31
-
32
- <!-- Tabs Navigation -->
33
- <div class="flex border-b overflow-x-auto dark:border-gray-700 transition-colors duration-200">
34
- <button id="tab-upload" class="tab-button active px-6 py-4 font-medium text-purple-600 dark:text-purple-400 border-b-2 border-purple-600 dark:border-purple-400 whitespace-nowrap transition-colors duration-200">
35
- Document Analysis
36
- </button>
37
- <button id="tab-text" class="tab-button px-6 py-4 font-medium text-gray-500 dark:text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 whitespace-nowrap transition-colors duration-200">
38
- Text Processing
39
- </button>
40
- <button id="tab-qa" class="tab-button px-6 py-4 font-medium text-gray-500 dark:text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 whitespace-nowrap transition-colors duration-200">
41
- Q&A
42
- </button>
43
- <button id="tab-code" class="tab-button px-6 py-4 font-medium text-gray-500 dark:text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 whitespace-nowrap transition-colors duration-200">
44
- Code Generation
45
- </button>
46
- </div>
47
-
48
- <!-- Tab Content -->
49
- <div class="p-6">
50
- <!-- Upload Document Section -->
51
- <div id="content-upload" class="tab-content">
52
- <div class="bg-purple-50 dark:bg-purple-900/20 p-6 rounded-xl transition-colors duration-200">
53
- <h2 class="text-2xl font-semibold text-purple-800 dark:text-purple-300 mb-4 transition-colors duration-200">Document & Image Analysis</h2>
54
- <p class="text-gray-600 dark:text-gray-300 mb-4 transition-colors duration-200">Upload a text file or image for analysis</p>
55
-
56
- <div class="flex flex-col items-center justify-center border-2 border-dashed border-purple-300 dark:border-purple-700 rounded-lg p-8 mb-4 bg-white dark:bg-gray-700 transition-colors duration-200">
57
- <svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 text-purple-400 dark:text-purple-300 mb-3 transition-colors duration-200" fill="none" viewBox="0 0 24 24" stroke="currentColor">
58
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
59
  </svg>
60
- <label for="fileInput" class="cursor-pointer bg-purple-600 text-white px-4 py-2 rounded-lg hover:bg-purple-700 transition">
61
- Choose File
62
- </label>
63
- <input type="file" id="fileInput" class="hidden">
64
- <p id="fileName" class="mt-2 text-sm text-gray-500 dark:text-gray-300 transition-colors duration-200">No file chosen</p>
65
  </div>
66
-
67
- <div class="flex flex-wrap gap-3 justify-center">
68
- <button onclick="uploadFile('extract_text')" class="px-5 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition flex items-center">
69
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
70
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
71
- </svg>
72
- Extract Text
73
- </button>
74
- <button onclick="uploadFile('image_caption')" class="px-5 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition flex items-center">
75
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
76
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
77
- </svg>
78
- Analyze Image
79
- </button>
80
  </div>
81
  </div>
82
  </div>
83
-
84
- <!-- Text Processing Section -->
85
- <div id="content-text" class="tab-content hidden">
86
- <div class="bg-blue-50 dark:bg-blue-900/20 p-6 rounded-xl transition-colors duration-200">
87
- <h2 class="text-2xl font-semibold text-blue-800 dark:text-blue-300 mb-4 transition-colors duration-200">Text Processing</h2>
88
- <p class="text-gray-600 dark:text-gray-300 mb-4 transition-colors duration-200">Enter text you want to summarize or translate</p>
89
-
90
- <div class="mb-4">
91
- <textarea id="textInput" class="w-full p-4 border border-blue-300 dark:border-blue-700 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 transition-colors duration-200" rows="6" placeholder="Enter text here..."></textarea>
92
- </div>
93
-
94
- <!-- Language Selection for Translation -->
95
- <div id="translationOptions" class="mb-4 p-4 bg-white dark:bg-gray-700 rounded-lg border border-blue-200 dark:border-blue-800 hidden transition-colors duration-200">
96
- <h3 class="text-lg font-medium text-blue-700 dark:text-blue-300 mb-3 transition-colors duration-200">Translation Options</h3>
97
- <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
98
- <div>
99
- <label for="sourceLanguage" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1 transition-colors duration-200">From Language:</label>
100
- <select id="sourceLanguage" class="w-full p-2 border border-blue-300 dark:border-blue-700 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 transition-colors duration-200">
101
- <option value="ar">Arabic</option>
102
- <option value="en" selected>English</option>
103
- <option value="fr">French</option>
104
- <option value="es">Spanish</option>
105
- <option value="zh">Chinese</option>
106
- </select>
107
- </div>
108
- <div>
109
- <label for="targetLanguage" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1 transition-colors duration-200">To Language:</label>
110
- <select id="targetLanguage" class="w-full p-2 border border-blue-300 dark:border-blue-700 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 transition-colors duration-200">
111
- <option value="ar" selected>Arabic</option>
112
- <option value="en">English</option>
113
- <option value="fr">French</option>
114
- <option value="es">Spanish</option>
115
- <option value="zh">Chinese</option>
116
- </select>
117
- </div>
118
- </div>
119
- </div>
120
-
121
- <div class="flex flex-wrap gap-3 justify-center">
122
- <button onclick="processText('summarize')" class="px-5 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition flex items-center">
123
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
124
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h8m-8 6h16" />
125
- </svg>
126
- Summarize Text
127
- </button>
128
- <button id="translateBtn" class="px-5 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition flex items-center">
129
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
130
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129" />
131
- </svg>
132
- Translate Text
133
- </button>
134
- </div>
135
- </div>
136
- </div>
137
-
138
- <!-- Q&A Section -->
139
- <div id="content-qa" class="tab-content hidden">
140
- <div class="bg-amber-50 dark:bg-amber-900/20 p-6 rounded-xl transition-colors duration-200">
141
- <h2 class="text-2xl font-semibold text-amber-800 dark:text-amber-300 mb-4 transition-colors duration-200">Questions & Answers</h2>
142
- <p class="text-gray-600 dark:text-gray-300 mb-4 transition-colors duration-200">Enter reference text and your question to get an answer</p>
143
-
144
- <div class="mb-4">
145
- <label for="contextInput" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1 transition-colors duration-200">Reference Text:</label>
146
- <textarea id="contextInput" class="w-full p-4 border border-amber-300 dark:border-amber-700 rounded-lg focus:ring-2 focus:ring-amber-500 focus:border-amber-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 transition-colors duration-200" rows="4" placeholder="Enter reference text here..."></textarea>
147
- </div>
148
-
149
- <div class="mb-4">
150
- <label for="questionInput" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1 transition-colors duration-200">Question:</label>
151
- <input type="text" id="questionInput" class="w-full p-4 border border-amber-300 dark:border-amber-700 rounded-lg focus:ring-2 focus:ring-amber-500 focus:border-amber-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 transition-colors duration-200" placeholder="Enter your question here...">
152
- </div>
153
-
154
- <div class="flex justify-center">
155
- <button onclick="askQuestion()" class="px-5 py-2 bg-amber-600 text-white rounded-lg hover:bg-amber-700 transition flex items-center">
156
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
157
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
158
- </svg>
159
- Get Answer
160
- </button>
161
- </div>
162
  </div>
163
  </div>
164
-
165
- <!-- Code Generation Section -->
166
- <div id="content-code" class="tab-content hidden">
167
- <div class="bg-teal-50 dark:bg-teal-900/20 p-6 rounded-xl transition-colors duration-200">
168
- <h2 class="text-2xl font-semibold text-teal-800 dark:text-teal-300 mb-4 transition-colors duration-200">Code Generation</h2>
169
- <p class="text-gray-600 dark:text-gray-300 mb-4 transition-colors duration-200">Enter a description of the code you want to generate</p>
170
-
171
- <div class="mb-4">
172
- <textarea id="codeInput" class="w-full p-4 border border-teal-300 dark:border-teal-700 rounded-lg focus:ring-2 focus:ring-teal-500 focus:border-teal-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 transition-colors duration-200" rows="6" placeholder="Example: Write code to create a chart for sales data..."></textarea>
173
- </div>
174
-
175
- <div class="flex justify-center">
176
- <button onclick="generateCode()" class="px-5 py-2 bg-teal-600 text-white rounded-lg hover:bg-teal-700 transition flex items-center">
177
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
178
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
179
- </svg>
180
- Generate Code
181
- </button>
182
- </div>
183
- </div>
184
  </div>
185
-
186
- <!-- Output Section (Common for all tabs) -->
187
- <div class="mt-6">
188
- <div class="bg-gray-100 dark:bg-gray-700 p-4 rounded-xl transition-colors duration-200">
189
- <div class="flex justify-between items-center mb-2">
190
- <h3 class="text-lg font-semibold text-gray-700 dark:text-gray-200 transition-colors duration-200">Results</h3>
191
- <button id="copyOutput" class="text-sm text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 flex items-center transition-colors duration-200">
192
- <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
193
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
 
 
194
  </svg>
195
- Copy
196
- </button>
197
- </div>
198
- <div class="relative">
199
- <div id="output" class="p-4 bg-white dark:bg-gray-800 border dark:border-gray-600 rounded-lg h-60 overflow-auto text-gray-800 dark:text-gray-200 transition-colors duration-200"></div>
200
- <div id="loader" class="hidden absolute inset-0 flex items-center justify-center bg-white dark:bg-gray-800 bg-opacity-80 dark:bg-opacity-80 rounded-lg transition-colors duration-200">
201
- <div class="animate-spin rounded-full h-12 w-12 border-b-2 border-purple-700 dark:border-purple-400 transition-colors duration-200"></div>
202
- </div>
203
  </div>
204
- </div>
 
 
 
 
 
205
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  </div>
207
  </div>
208
-
209
- <footer class="mt-6 text-center text-gray-500 dark:text-gray-400 text-sm transition-colors duration-200">
210
- <p>Developed by Rezaiguia Soltane © 2025</p>
211
- </footer>
212
  </div>
213
-
214
- <script src="/static/script.js"></script>
215
- <script src="/static/theme-fix.js"></script>
 
 
216
  </body>
217
  </html>
 
1
  <!DOCTYPE html>
2
+ <html lang="en" dir="ltr" class="">
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AI Chat Assistant</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="style.css">
9
  </head>
10
+ <body class="bg-gray-100 font-sans min-h-screen">
11
+ <div class="app-container">
12
+ <div class="app-content">
13
+ <!-- Header -->
14
+ <header class="bg-gradient-to-r from-purple-600 to-indigo-600 p-4 shadow-md rounded-t-lg">
15
+ <div class="container mx-auto flex justify-between items-center">
16
+ <h1 class="text-2xl font-bold text-white">AI Assistant</h1>
17
+
18
+ <!-- Theme Toggle Button -->
19
+ <button id="theme-toggle" class="p-2 rounded-full bg-white/20 hover:bg-white/30 transition-colors">
20
  <!-- Sun icon (for dark mode) -->
21
  <svg id="sun-icon" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-yellow-300 hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor">
22
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707-.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
23
  </svg>
24
  <!-- Moon icon (for light mode) -->
25
  <svg id="moon-icon" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
 
27
  </svg>
28
  </button>
29
  </div>
30
+ </header>
31
+
32
+ <!-- Main Chat Area -->
33
+ <main class="flex-1 overflow-hidden flex flex-col bg-white">
34
+ <!-- Chat Messages Container -->
35
+ <div id="chat-messages" class="flex-1 overflow-y-auto p-4 space-y-4 bg-white">
36
+ <!-- Welcome Message -->
37
+ <div class="flex items-start">
38
+ <div class="flex-shrink-0 bg-purple-600 rounded-full p-2">
39
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
40
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  </svg>
 
 
 
 
 
42
  </div>
43
+ <div class="ml-3 bg-white rounded-lg p-4 shadow-sm max-w-3xl border border-gray-100">
44
+ <p class="text-gray-800">Welcome! I'm your AI assistant. I can help you with:</p>
45
+ <ul class="mt-2 list-disc list-inside text-gray-700">
46
+ <li>Summarizing text</li>
47
+ <li>Translating between languages</li>
48
+ <li>Answering questions</li>
49
+ <li>Generating code</li>
50
+ <li>Analyzing documents and images</li>
51
+ </ul>
52
+ <p class="mt-2 text-gray-800">How can I assist you today?</p>
 
 
 
 
53
  </div>
54
  </div>
55
  </div>
56
+
57
+ <!-- Feature Selection Bar -->
58
+ <div class="bg-white border-t border-gray-100 p-3">
59
+ <div class="flex overflow-x-auto space-x-2 pb-2 feature-buttons">
60
+ <button class="feature-btn active" data-feature="chat">
61
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
62
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z" />
63
+ </svg>
64
+ Chat
65
+ </button>
66
+ <button class="feature-btn" data-feature="summarize">
67
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
68
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h8m-8 6h16" />
69
+ </svg>
70
+ Summarize
71
+ </button>
72
+ <button class="feature-btn" data-feature="translate">
73
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
74
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129" />
75
+ </svg>
76
+ Translate
77
+ </button>
78
+ <button class="feature-btn" data-feature="qa">
79
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
80
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
81
+ </svg>
82
+ Q&A
83
+ </button>
84
+ <button class="feature-btn" data-feature="code">
85
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
86
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
87
+ </svg>
88
+ Code
89
+ </button>
90
+ <button class="feature-btn" data-feature="document">
91
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
92
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
93
+ </svg>
94
+ Document
95
+ </button>
96
+ <button class="feature-btn" data-feature="image">
97
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
98
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
99
+ </svg>
100
+ Image
101
+ </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  </div>
103
  </div>
104
+
105
+ <!-- Feature-specific Options (initially hidden) -->
106
+ <div id="feature-options" class="bg-white border-t border-gray-100 p-3 hidden">
107
+ <!-- Content will be dynamically inserted based on selected feature -->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  </div>
109
+
110
+ <!-- Input Area -->
111
+ <div class="bg-white border-t border-gray-100 p-4 rounded-b-lg">
112
+ <form id="chat-form" class="flex items-end gap-2">
113
+ <div class="flex-1 relative">
114
+ <textarea id="user-input" rows="1" class="w-full p-3 pr-10 border border-gray-200 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 bg-white text-gray-900 resize-none" placeholder="Type your message..."></textarea>
115
+
116
+ <!-- File Upload Button (initially hidden) -->
117
+ <label id="file-upload-label" for="file-upload" class="absolute right-3 bottom-3 cursor-pointer hidden">
118
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500 hover:text-purple-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
119
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13" />
120
  </svg>
121
+ </label>
122
+ <input id="file-upload" type="file" class="hidden">
 
 
 
 
 
 
123
  </div>
124
+ <button type="submit" class="p-3 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition flex-shrink-0">
125
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
126
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 5l7 7-7 7M5 5l7 7-7 7" />
127
+ </svg>
128
+ </button>
129
+ </form>
130
  </div>
131
+ </main>
132
+ </div>
133
+ </div>
134
+
135
+ <!-- History Panel (hidden by default) -->
136
+ <div id="historyPanel" class="fixed right-0 top-0 h-full w-80 bg-white shadow-lg transform translate-x-full transition-transform duration-300 ease-in-out z-50 overflow-y-auto">
137
+ <div class="p-4 bg-purple-600 text-white">
138
+ <div class="flex justify-between items-center">
139
+ <h3 class="text-lg font-semibold">History</h3>
140
+ <button id="closeHistory" class="text-white hover:text-purple-200">
141
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
142
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
143
+ </svg>
144
+ </button>
145
  </div>
146
  </div>
147
+ <div id="historyItems" class="p-4 space-y-3"></div>
 
 
 
148
  </div>
149
+
150
+ <!-- Notification Container -->
151
+ <div id="notification-container" class="fixed top-4 right-4 z-50"></div>
152
+
153
+ <script src="script.js"></script>
154
  </body>
155
  </html>
frontend/script.js CHANGED
@@ -1,762 +1,691 @@
1
- // Tab switching functionality
2
  document.addEventListener("DOMContentLoaded", () => {
3
- // Initialize theme
4
- initTheme()
5
-
6
- // Tab switching
7
- const tabButtons = document.querySelectorAll(".tab-button")
8
- const tabContents = document.querySelectorAll(".tab-content")
9
-
10
- tabButtons.forEach((button) => {
11
- button.addEventListener("click", () => {
12
- // Remove active class from all buttons and hide all contents
13
- tabButtons.forEach((btn) => btn.classList.remove("active"))
14
- tabContents.forEach((content) => content.classList.add("hidden"))
15
-
16
- // Add active class to clicked button and show corresponding content
17
- button.classList.add("active")
18
- const contentId = "content-" + button.id.split("-")[1]
19
- document.getElementById(contentId).classList.remove("hidden")
20
-
21
- // Save active tab to localStorage
22
- localStorage.setItem("activeTab", button.id)
23
- })
24
- })
25
-
26
- // Restore active tab from localStorage
27
- const activeTab = localStorage.getItem("activeTab")
28
- if (activeTab) {
29
- document.getElementById(activeTab)?.click()
30
- }
31
-
32
- // File input handling
33
- const fileInput = document.getElementById("fileInput")
34
- const fileName = document.getElementById("fileName")
35
-
36
- fileInput.addEventListener("change", () => {
37
- if (fileInput.files.length > 0) {
38
- fileName.textContent = fileInput.files[0].name
39
  } else {
40
- fileName.textContent = "No file chosen"
41
  }
 
 
 
 
 
 
42
  })
43
-
44
- // Copy output button
45
- const copyButton = document.getElementById("copyOutput")
46
- copyButton.addEventListener("click", () => {
47
- const output = document.getElementById("output")
48
- navigator.clipboard
49
- .writeText(output.textContent)
50
- .then(() => {
51
- showNotification("Results copied successfully!")
52
- })
53
- .catch((err) => {
54
- console.error("Failed to copy text: ", err)
55
- showNotification("Failed to copy text: " + err.message, "error")
56
- })
57
- })
58
-
59
- // Translation options toggle
60
- const translateBtn = document.getElementById("translateBtn")
61
- const translationOptions = document.getElementById("translationOptions")
62
-
63
- translateBtn.addEventListener("click", () => {
64
- // Toggle translation options visibility
65
- if (translationOptions.classList.contains("hidden")) {
66
- translationOptions.classList.remove("hidden")
67
- } else {
68
- // If options are already visible, perform translation
69
- translateText()
70
- }
71
- })
72
-
73
- // Theme toggle
74
- const themeToggle = document.getElementById("theme-toggle")
75
- themeToggle.addEventListener("click", toggleTheme)
76
-
77
- // Add download results button
78
- const outputContainer = document.querySelector(".bg-gray-100.dark\\:bg-gray-700.p-4.rounded-xl")
79
- const downloadButton = document.createElement("button")
80
- downloadButton.id = "downloadOutput"
81
- downloadButton.className =
82
- "text-sm text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 flex items-center ml-4 transition-colors duration-200"
83
- downloadButton.innerHTML = `
84
- <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
85
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
86
- </svg>
87
- Download
88
- `
89
- downloadButton.addEventListener("click", downloadResults)
90
-
91
- // Add the download button next to the copy button
92
- const resultHeader = outputContainer.querySelector(".flex.justify-between.items-center.mb-2")
93
- resultHeader.appendChild(downloadButton)
94
-
95
- // Initialize history panel
96
- initHistoryPanel()
97
-
98
- // Make sure theme toggle is properly initialized
99
- const themeToggleBtn = document.getElementById("theme-toggle")
100
- if (themeToggleBtn) {
101
- themeToggleBtn.addEventListener("click", toggleTheme)
102
- console.log("Theme toggle button initialized")
103
- } else {
104
- console.error("Theme toggle button not found")
105
- }
106
-
107
- // Initialize theme immediately
108
- initTheme()
109
- console.log("Theme initialized:", document.documentElement.classList.contains("dark") ? "dark" : "light")
110
  })
111
-
112
- // Theme functions
113
- function initTheme() {
114
- // Check if user preference exists in localStorage
115
- const savedTheme = localStorage.getItem("theme")
116
-
117
- if (savedTheme === "dark" || (!savedTheme && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
118
- // Set dark mode
119
- document.documentElement.classList.add("dark")
120
- document.getElementById("sun-icon")?.classList.remove("hidden")
121
- document.getElementById("moon-icon")?.classList.add("hidden")
122
- } else {
123
- // Set light mode
124
- document.documentElement.classList.remove("dark")
125
- document.getElementById("sun-icon")?.classList.add("hidden")
126
- document.getElementById("moon-icon")?.classList.remove("hidden")
127
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  }
129
-
130
- function toggleTheme() {
131
- // Toggle the dark class on the html element
132
- document.documentElement.classList.toggle("dark")
133
-
134
- // Update the icons
135
- const sunIcon = document.getElementById("sun-icon")
136
- const moonIcon = document.getElementById("moon-icon")
137
-
138
- if (sunIcon && moonIcon) {
139
- if (document.documentElement.classList.contains("dark")) {
140
- // Dark mode is active
141
- sunIcon.classList.remove("hidden")
142
- moonIcon.classList.add("hidden")
143
- localStorage.setItem("theme", "dark")
144
- showNotification("Dark mode activated")
145
- } else {
146
- // Light mode is active
147
- sunIcon.classList.add("hidden")
148
- moonIcon.classList.remove("hidden")
149
- localStorage.setItem("theme", "light")
150
- showNotification("Light mode activated")
151
- }
152
- }
153
- }
154
-
155
- // Show notification function
156
- function showNotification(message, type = "success") {
157
- // Create notification element
158
- const notification = document.createElement("div")
159
-
160
- // Set class based on notification type
161
- if (type === "success") {
162
- notification.className =
163
- "notification fixed top-4 right-4 bg-green-100 border-l-4 border-green-500 text-green-700 p-4 rounded shadow-md dark:bg-green-900 dark:text-green-100 dark:border-green-400"
164
- } else if (type === "error") {
165
- notification.className =
166
- "notification fixed top-4 right-4 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded shadow-md dark:bg-red-900 dark:text-red-100 dark:border-red-400"
167
- } else if (type === "warning") {
168
- notification.className =
169
- "notification fixed top-4 right-4 bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4 rounded shadow-md dark:bg-yellow-900 dark:text-yellow-100 dark:border-yellow-400"
170
- } else if (type === "info") {
171
- notification.className =
172
- "notification fixed top-4 right-4 bg-blue-100 border-l-4 border-blue-500 text-blue-700 p-4 rounded shadow-md dark:bg-blue-900 dark:text-blue-100 dark:border-blue-400"
173
- }
174
-
175
- notification.innerHTML = message
176
-
177
- // Add to document
178
- document.body.appendChild(notification)
179
-
180
- // Remove after 3 seconds
181
- setTimeout(() => {
182
- notification.style.opacity = "0"
183
- notification.style.transform = "translateY(-20px)"
184
- notification.style.transition = "opacity 0.5s, transform 0.5s"
185
-
186
- setTimeout(() => {
187
- notification.remove()
188
- }, 500)
189
- }, 3000)
190
- }
191
-
192
- // Show/hide loader
193
- function toggleLoader(show) {
194
- const loader = document.getElementById("loader")
195
- if (show) {
196
- loader.classList.remove("hidden")
197
  } else {
198
- loader.classList.add("hidden")
 
 
199
  }
200
- }
201
-
202
- // Download results function
203
- function downloadResults() {
204
- const output = document.getElementById("output")
205
- const content = output.textContent || output.innerText
206
-
207
- if (!content || content.trim() === "") {
208
- showNotification("No content to download", "warning")
209
- return
210
- }
211
-
212
- // Create a blob with the content
213
- const blob = new Blob([content], { type: "text/plain" })
214
- const url = URL.createObjectURL(blob)
215
-
216
- // Create a temporary link and trigger download
217
- const a = document.createElement("a")
218
- a.href = url
219
- a.download = "ai-results-" + new Date().toISOString().slice(0, 10) + ".txt"
220
- document.body.appendChild(a)
221
- a.click()
222
-
223
- // Clean up
224
- setTimeout(() => {
225
- document.body.removeChild(a)
226
- URL.revokeObjectURL(url)
227
- }, 100)
228
-
229
- showNotification("Results downloaded successfully!")
230
-
231
- // Add to history
232
- addToHistory("Downloaded results", content.substring(0, 50) + (content.length > 50 ? "..." : ""))
233
- }
234
-
235
- // History management
236
- function initHistoryPanel() {
237
- // Create history panel
238
- const historyPanel = document.createElement("div")
239
- historyPanel.id = "historyPanel"
240
- historyPanel.className =
241
- "fixed right-0 top-0 h-full w-80 bg-white dark:bg-gray-800 shadow-lg transform translate-x-full transition-transform duration-300 ease-in-out z-50 overflow-y-auto"
242
-
243
- // Add history header
244
- historyPanel.innerHTML = `
245
- <div class="p-4 bg-purple-600 text-white">
246
- <div class="flex justify-between items-center">
247
- <h3 class="text-lg font-semibold">History</h3>
248
- <button id="closeHistory" class="text-white hover:text-purple-200">
249
- <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
250
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
251
- </svg>
252
- </button>
253
  </div>
254
- </div>
255
- <div id="historyItems" class="p-4 space-y-3"></div>
256
- `
257
-
258
- document.body.appendChild(historyPanel)
259
-
260
- // Add history button to the header
261
- const header = document.querySelector(".bg-gradient-to-r.from-purple-600.to-indigo-600.p-6.relative")
262
- const historyButton = document.createElement("button")
263
- historyButton.id = "historyButton"
264
- historyButton.className = "absolute left-6 top-6 p-2 rounded-full bg-white/20 hover:bg-white/30 transition-colors"
265
- historyButton.innerHTML = `
266
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
267
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  </svg>
 
 
269
  `
270
-
271
- header.appendChild(historyButton)
272
-
273
- // Add event listeners
274
- document.getElementById("historyButton").addEventListener("click", toggleHistoryPanel)
275
- document.getElementById("closeHistory").addEventListener("click", toggleHistoryPanel)
276
-
277
- // Load history from localStorage
278
- loadHistory()
279
- }
280
-
281
- function toggleHistoryPanel() {
282
- const historyPanel = document.getElementById("historyPanel")
283
- if (historyPanel.classList.contains("translate-x-full")) {
284
- historyPanel.classList.remove("translate-x-full")
285
- } else {
286
- historyPanel.classList.add("translate-x-full")
287
  }
 
 
 
 
 
288
  }
289
-
290
- function addToHistory(action, details) {
291
- // Get existing history or initialize empty array
292
- const history = JSON.parse(localStorage.getItem("aiAppHistory") || "[]")
293
-
294
- // Add new item
295
- history.unshift({
296
- action,
297
- details,
298
- timestamp: new Date().toISOString(),
299
- })
300
-
301
- // Keep only the last 20 items
302
- if (history.length > 20) {
303
- history.pop()
304
- }
305
-
306
- // Save back to localStorage
307
- localStorage.setItem("aiAppHistory", JSON.stringify(history))
308
-
309
- // Update UI if history panel exists
310
- loadHistory()
311
  }
312
-
313
- function loadHistory() {
314
- const historyItemsContainer = document.getElementById("historyItems")
315
- if (!historyItemsContainer) return
316
-
317
- // Clear existing items
318
- historyItemsContainer.innerHTML = ""
319
-
320
- // Get history from localStorage
321
- const history = JSON.parse(localStorage.getItem("aiAppHistory") || "[]")
322
-
323
- if (history.length === 0) {
324
- historyItemsContainer.innerHTML = `<p class="text-gray-500 dark:text-gray-400 text-center">No history yet</p>`
325
- return
326
- }
327
-
328
- // Add each history item
329
- history.forEach((item, index) => {
330
- const historyItem = document.createElement("div")
331
- historyItem.className = "bg-gray-100 dark:bg-gray-700 p-3 rounded-lg"
332
-
333
- const date = new Date(item.timestamp)
334
- const formattedDate = date.toLocaleDateString() + " " + date.toLocaleTimeString()
335
-
336
- historyItem.innerHTML = `
337
- <div class="flex justify-between items-start">
338
- <h4 class="font-medium text-gray-800 dark:text-gray-200">${item.action}</h4>
339
- <span class="text-xs text-gray-500 dark:text-gray-400">${formattedDate}</span>
340
- </div>
341
- <p class="text-sm text-gray-600 dark:text-gray-300 mt-1">${item.details}</p>
342
- `
343
-
344
- historyItemsContainer.appendChild(historyItem)
345
- })
346
  }
347
-
348
- // Format the output based on the type of result
349
- function formatOutput(data, type) {
350
- const outputElement = document.getElementById("output")
351
- outputElement.innerHTML = "" // Clear previous content
352
-
353
- // Create a container for formatted output
354
- const container = document.createElement("div")
355
- container.className = "formatted-output"
356
-
357
- switch (type) {
358
- case "translation":
359
- // Get language names
360
- const sourceLang = getLanguageName(document.getElementById("sourceLanguage").value)
361
- const targetLang = getLanguageName(document.getElementById("targetLanguage").value)
362
-
363
- // Create translation result elements
364
- const header = document.createElement("div")
365
- header.className = "result-header"
366
- header.innerHTML = `<h3>Translation Result</h3>`
367
-
368
- const originalBox = document.createElement("div")
369
- originalBox.className = "text-box original"
370
- originalBox.innerHTML = `
371
- <div class="text-box-header">${sourceLang}</div>
372
- <div class="text-content">${document.getElementById("textInput").value}</div>
373
- `
374
-
375
- const translatedBox = document.createElement("div")
376
- translatedBox.className = "text-box translated"
377
-
378
- // Add text-to-speech button for translated text
379
- const translatedText = data.translation || data.result || data.text || JSON.stringify(data)
380
- translatedBox.innerHTML = `
381
- <div class="text-box-header flex justify-between items-center">
382
- <span>${targetLang}</span>
383
- <button class="text-to-speech-btn p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-600" title="Listen to text">
384
- <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
385
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.536 8.464a5 5 0 010 7.072m2.828-9.9a9 9 0 010 12.728M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z" />
386
- </svg>
387
- </button>
388
- </div>
389
- <div class="text-content">${translatedText}</div>
390
- `
391
-
392
- container.appendChild(header)
393
- container.appendChild(originalBox)
394
- container.appendChild(translatedBox)
395
-
396
- // Add event listener for text-to-speech
397
- setTimeout(() => {
398
- const ttsBtn = container.querySelector(".text-to-speech-btn")
399
- if (ttsBtn) {
400
- ttsBtn.addEventListener("click", () => {
401
- speakText(translatedText, document.getElementById("targetLanguage").value)
402
- })
403
- }
404
- }, 0)
405
-
406
- // Add to history
407
- addToHistory("Translation", `${sourceLang} to ${targetLang}`)
408
- break
409
-
410
- case "summarize":
411
- const summaryHeader = document.createElement("div")
412
- summaryHeader.className = "result-header"
413
- summaryHeader.innerHTML = `<h3>Summary</h3>`
414
-
415
- const summaryContent = document.createElement("div")
416
- summaryContent.className = "text-box summary"
417
- summaryContent.innerHTML = `
418
- <div class="text-content">${data.summary || data.result || data.text || JSON.stringify(data)}</div>
419
- `
420
-
421
- container.appendChild(summaryHeader)
422
- container.appendChild(summaryContent)
423
-
424
- // Add to history
425
- addToHistory("Summarization", "Text summarized")
426
- break
427
-
428
- case "qa":
429
- const qaHeader = document.createElement("div")
430
- qaHeader.className = "result-header"
431
- qaHeader.innerHTML = `<h3>Answer</h3>`
432
-
433
- const questionBox = document.createElement("div")
434
- questionBox.className = "text-box question"
435
- questionBox.innerHTML = `
436
- <div class="text-box-header">Question</div>
437
- <div class="text-content">${document.getElementById("questionInput").value}</div>
438
- `
439
-
440
- const answerBox = document.createElement("div")
441
- answerBox.className = "text-box answer"
442
- answerBox.innerHTML = `
443
- <div class="text-box-header">Answer</div>
444
- <div class="text-content">${data.answer || data.result || JSON.stringify(data)}</div>
445
- `
446
-
447
- container.appendChild(qaHeader)
448
- container.appendChild(questionBox)
449
- container.appendChild(answerBox)
450
-
451
- // Add to history
452
- addToHistory("Question Answered", document.getElementById("questionInput").value)
453
- break
454
-
455
- case "code":
456
- const codeHeader = document.createElement("div")
457
- codeHeader.className = "result-header"
458
- codeHeader.innerHTML = `<h3>Generated Code</h3>`
459
-
460
- const codeBox = document.createElement("div")
461
- codeBox.className = "text-box code"
462
-
463
- // Format code with syntax highlighting if possible
464
- const codeContent = document.createElement("pre")
465
- codeContent.className = "code-content"
466
- codeContent.textContent = data.code || data.result || JSON.stringify(data, null, 2)
467
-
468
- codeBox.appendChild(codeContent)
469
- container.appendChild(codeHeader)
470
- container.appendChild(codeBox)
471
-
472
- // Add to history
473
- addToHistory("Code Generation", document.getElementById("codeInput").value.substring(0, 50) + "...")
474
- break
475
-
476
- case "extract_text":
477
- const extractHeader = document.createElement("div")
478
- extractHeader.className = "result-header"
479
- extractHeader.innerHTML = `<h3>Extracted Text</h3>`
480
-
481
- const extractBox = document.createElement("div")
482
- extractBox.className = "text-box extracted"
483
- extractBox.innerHTML = `
484
- <div class="text-content">${data.text || data.result || JSON.stringify(data)}</div>
485
- `
486
-
487
- container.appendChild(extractHeader)
488
- container.appendChild(extractBox)
489
-
490
- // Add to history
491
- addToHistory("Text Extraction", "Extracted from file")
492
- break
493
-
494
- case "image_caption":
495
- const captionHeader = document.createElement("div")
496
- captionHeader.className = "result-header"
497
- captionHeader.innerHTML = `<h3>Image Analysis</h3>`
498
-
499
- const captionBox = document.createElement("div")
500
- captionBox.className = "text-box caption"
501
- captionBox.innerHTML = `
502
- <div class="text-content">${data.caption || data.result || JSON.stringify(data)}</div>
503
- `
504
-
505
- container.appendChild(captionHeader)
506
- container.appendChild(captionBox)
507
-
508
- // Add to history
509
- addToHistory("Image Analysis", "Image analyzed")
510
- break
511
-
512
- default:
513
- // Fallback to JSON display for unknown types
514
- container.innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`
515
- }
516
-
517
- outputElement.appendChild(container)
518
  }
519
-
520
- // Text-to-speech function
521
- function speakText(text, langCode) {
522
- if (!text) return
523
-
524
- // Stop any ongoing speech
525
- window.speechSynthesis.cancel()
526
-
527
- // Create utterance
528
- const utterance = new SpeechSynthesisUtterance(text)
529
-
530
- // Map language codes to BCP 47 language tags
531
- const langMap = {
532
- ar: "ar-SA",
533
- en: "en-US",
534
- fr: "fr-FR",
535
- es: "es-ES",
536
- zh: "zh-CN",
537
- }
538
-
539
- // Set language
540
- utterance.lang = langMap[langCode] || "en-US"
541
-
542
- // Get available voices
543
- const voices = window.speechSynthesis.getVoices()
544
-
545
- // Try to find a voice for the selected language
546
- const voice = voices.find((v) => v.lang.startsWith(langMap[langCode]?.split("-")[0] || "en"))
547
- if (voice) {
548
- utterance.voice = voice
549
- }
550
-
551
- // Speak
552
- window.speechSynthesis.speak(utterance)
553
-
554
- // Show notification
555
- showNotification("Playing audio...", "info")
556
- }
557
-
558
- // Helper function to get language name from code
559
- function getLanguageName(code) {
560
- const languages = {
561
- ar: "Arabic",
562
- en: "English",
563
- fr: "French",
564
- es: "Spanish",
565
- zh: "Chinese",
566
- }
567
- return languages[code] || code
568
  }
569
-
570
- // API Functions
571
- async function uploadFile(endpoint) {
572
- const fileInput = document.getElementById("fileInput")
573
- const file = fileInput.files[0]
574
-
575
- if (!file) {
576
- showNotification("Please select a file first.", "warning")
577
- return
578
- }
579
-
580
- toggleLoader(true)
581
- const formData = new FormData()
582
- formData.append("file", file)
583
-
584
- try {
585
- const response = await fetch(`https://soltane777-textgeneration.hf.space/${endpoint}`, {
586
- method: "POST",
587
- body: formData,
588
- })
589
-
590
- if (!response.ok) {
591
- throw new Error(`Server responded with status: ${response.status}`)
 
 
 
 
 
 
 
 
 
 
 
 
592
  }
593
-
594
- const data = await response.json()
595
- formatOutput(data, endpoint)
596
- } catch (error) {
597
- console.error("Error:", error)
598
- document.getElementById("output").innerHTML =
599
- `<div class="error-message">Error: ${error.message || "Failed to fetch data"}</div>`
600
- showNotification("Error processing file: " + (error.message || "Unknown error"), "error")
601
- } finally {
602
- toggleLoader(false)
603
- }
604
- }
605
-
606
- async function processText(endpoint) {
607
- // If endpoint is translate, we now handle it separately
608
- if (endpoint === "translate") {
609
- // Show translation options if they're hidden
610
- if (document.getElementById("translationOptions").classList.contains("hidden")) {
611
- document.getElementById("translationOptions").classList.remove("hidden")
612
  return
613
  }
614
- return translateText()
615
- }
616
-
617
- const text = document.getElementById("textInput").value
618
-
619
- if (!text) {
620
- showNotification("Please enter text first.", "warning")
621
- return
622
- }
623
-
624
- toggleLoader(true)
625
- const formData = new FormData()
626
- formData.append("text", text)
627
-
628
- try {
629
- const response = await fetch(`https://soltane777-textgeneration.hf.space/${endpoint}`, {
630
- method: "POST",
631
- body: formData,
632
- })
633
-
634
- if (!response.ok) {
635
- throw new Error(`Server responded with status: ${response.status}`)
636
  }
637
-
638
- const data = await response.json()
639
- formatOutput(data, endpoint)
640
- } catch (error) {
641
- console.error("Error:", error)
642
- document.getElementById("output").innerHTML =
643
- `<div class="error-message">Error: ${error.message || "Failed to fetch data"}</div>`
644
- showNotification("Error processing text: " + (error.message || "Unknown error"), "error")
645
- } finally {
646
- toggleLoader(false)
647
- }
648
- }
649
-
650
- async function translateText() {
651
- const text = document.getElementById("textInput").value
652
- const sourceLanguage = document.getElementById("sourceLanguage").value
653
- const targetLanguage = document.getElementById("targetLanguage").value
654
-
655
- if (!text) {
656
- showNotification("Please enter text to translate.", "warning")
 
 
657
  return
658
- }
659
-
660
- toggleLoader(true)
661
- const formData = new FormData()
662
- formData.append("text", text)
663
- formData.append("source_lang", sourceLanguage)
664
- formData.append("target_lang", targetLanguage)
665
-
666
- try {
667
- const response = await fetch("https://soltane777-textgeneration.hf.space/translate", {
668
- method: "POST",
669
- body: formData,
670
- })
671
-
672
- if (!response.ok) {
673
- throw new Error(`Server responded with status: ${response.status}`)
674
- }
675
-
676
- const data = await response.json()
677
- formatOutput(data, "translation")
678
- } catch (error) {
679
- console.error("Error:", error)
680
- document.getElementById("output").innerHTML =
681
- `<div class="error-message">Error: ${error.message || "Failed to fetch translation data"}</div>`
682
- showNotification("Error translating text: " + (error.message || "Unknown error"), "error")
683
- } finally {
684
- toggleLoader(false)
685
- }
686
  }
687
-
688
- async function askQuestion() {
689
- const context = document.getElementById("contextInput").value
690
- const question = document.getElementById("questionInput").value
691
-
692
- if (!context || !question) {
693
- showNotification("Please enter both reference text and question.", "warning")
694
- return
695
- }
696
-
697
- toggleLoader(true)
698
- try {
699
- const response = await fetch("https://soltane777-textgeneration.hf.space/qa/", {
700
- method: "POST",
701
- headers: { "Content-Type": "application/json" },
702
- body: JSON.stringify({ context: context, question: question }),
703
  })
704
-
705
- if (!response.ok) {
706
- throw new Error(`Server responded with status: ${response.status}`)
707
- }
708
-
709
- const data = await response.json()
710
- formatOutput(data, "qa")
711
- } catch (error) {
712
- console.error("Error:", error)
713
- document.getElementById("output").innerHTML =
714
- `<div class="error-message">Error: ${error.message || "Failed to fetch data"}</div>`
715
- showNotification("Error getting answer: " + (error.message || "Unknown error"), "error")
716
- } finally {
717
- toggleLoader(false)
718
- }
719
- }
720
-
721
- async function generateCode() {
722
- const prompt = document.getElementById("codeInput").value
723
-
724
- if (!prompt) {
725
- showNotification("Please enter a description for the code.", "warning")
726
- return
727
- }
728
-
729
- toggleLoader(true)
730
- const formData = new FormData()
731
- formData.append("prompt", prompt)
732
-
733
- try {
734
- const response = await fetch("https://soltane777-textgeneration.hf.space/generate_code/", {
735
- method: "POST",
736
- body: formData,
737
  })
738
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
739
  if (!response.ok) {
740
  throw new Error(`Server responded with status: ${response.status}`)
741
  }
742
-
743
- const data = await response.json()
744
- formatOutput(data, "code")
745
- } catch (error) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
746
  console.error("Error:", error)
747
- document.getElementById("output").innerHTML =
748
- `<div class="error-message">Error: ${error.message || "Failed to fetch data"}</div>`
749
- showNotification("Error generating code: " + (error.message || "Unknown error"), "error")
750
- } finally {
751
- toggleLoader(false)
752
- }
 
 
 
 
 
 
 
753
  }
754
-
755
- // Initialize speech synthesis voices
756
- if ("speechSynthesis" in window) {
757
- // Load voices
758
- speechSynthesis.onvoiceschanged = () => {
759
- speechSynthesis.getVoices()
760
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
761
  }
762
-
 
 
 
1
  document.addEventListener("DOMContentLoaded", () => {
2
+ // Auto-resize textarea
3
+ const textarea = document.getElementById("user-input")
4
+ textarea.addEventListener("input", autoResizeTextarea)
5
+
6
+ // Feature button selection
7
+ const featureButtons = document.querySelectorAll(".feature-btn")
8
+ const featureOptions = document.getElementById("feature-options")
9
+ const fileUploadLabel = document.getElementById("file-upload-label")
10
+
11
+ featureButtons.forEach((button) => {
12
+ button.addEventListener("click", () => {
13
+ // Remove active class from all buttons
14
+ featureButtons.forEach((btn) => btn.classList.remove("active"))
15
+
16
+ // Add active class to clicked button
17
+ button.classList.add("active")
18
+
19
+ // Show/hide file upload button based on feature
20
+ const feature = button.dataset.feature
21
+ if (feature === "document" || feature === "image") {
22
+ fileUploadLabel.classList.remove("hidden")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  } else {
24
+ fileUploadLabel.classList.add("hidden")
25
  }
26
+
27
+ // Update feature options
28
+ updateFeatureOptions(feature)
29
+
30
+ // Save active feature to localStorage
31
+ localStorage.setItem("activeFeature", feature)
32
  })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  })
34
+
35
+ // Restore active feature from localStorage
36
+ const activeFeature = localStorage.getItem("activeFeature") || "chat"
37
+ document.querySelector(`.feature-btn[data-feature="${activeFeature}"]`)?.click()
38
+
39
+ // File input handling
40
+ const fileInput = document.getElementById("file-upload")
41
+ fileInput.addEventListener("change", handleFileSelection)
42
+
43
+ // Chat form submission
44
+ const chatForm = document.getElementById("chat-form")
45
+ chatForm.addEventListener("submit", handleChatSubmit)
46
+
47
+ // History panel toggle
48
+ const historyButton = document.createElement("button")
49
+ historyButton.id = "historyButton"
50
+ historyButton.className = "absolute left-4 top-4 p-2 rounded-full bg-white/20 hover:bg-white/30 transition-colors"
51
+ historyButton.innerHTML = `
52
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
53
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
54
+ </svg>
55
+ `
56
+ document.querySelector("header .container").appendChild(historyButton)
57
+
58
+ historyButton.addEventListener("click", toggleHistoryPanel)
59
+ document.getElementById("closeHistory").addEventListener("click", toggleHistoryPanel)
60
+
61
+ // Theme toggle functionality
62
+ const themeToggle = document.getElementById("theme-toggle")
63
+ const sunIcon = document.getElementById("sun-icon")
64
+ const moonIcon = document.getElementById("moon-icon")
65
+
66
+ // Check for saved theme preference or use system preference
67
+ const savedTheme = localStorage.getItem("theme")
68
+ const systemPrefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches
69
+
70
+ if (savedTheme === "dark" || (!savedTheme && systemPrefersDark)) {
71
+ document.documentElement.classList.add("dark")
72
+ sunIcon.classList.remove("hidden")
73
+ moonIcon.classList.add("hidden")
74
  }
75
+
76
+ // Toggle theme when button is clicked
77
+ themeToggle.addEventListener("click", () => {
78
+ const isDark = document.documentElement.classList.toggle("dark")
79
+
80
+ // Toggle icons
81
+ if (isDark) {
82
+ sunIcon.classList.remove("hidden")
83
+ moonIcon.classList.add("hidden")
84
+ localStorage.setItem("theme", "dark")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  } else {
86
+ sunIcon.classList.add("hidden")
87
+ moonIcon.classList.remove("hidden")
88
+ localStorage.setItem("theme", "light")
89
  }
90
+
91
+ // Show notification
92
+ showNotification(`Switched to ${isDark ? "dark" : "light"} mode`, "info")
93
+ })
94
+
95
+ // Load history from localStorage
96
+ loadHistory()
97
+ })
98
+
99
+ // Auto-resize textarea
100
+ function autoResizeTextarea() {
101
+ this.style.height = "auto"
102
+ this.style.height = this.scrollHeight + "px"
103
+ }
104
+
105
+ // Update feature options based on selected feature
106
+ function updateFeatureOptions(feature) {
107
+ const optionsContainer = document.getElementById("feature-options")
108
+
109
+ // Clear previous options
110
+ optionsContainer.innerHTML = ""
111
+
112
+ switch (feature) {
113
+ case "chat":
114
+ // No special options for chat
115
+ optionsContainer.classList.add("hidden")
116
+ break
117
+
118
+ case "translate":
119
+ optionsContainer.classList.remove("hidden")
120
+ optionsContainer.innerHTML = `
121
+ <div class="language-selector">
122
+ <div>
123
+ <label for="sourceLanguage" class="block text-sm font-medium text-gray-700 mb-1">From:</label>
124
+ <select id="sourceLanguage" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 bg-white text-gray-900">
125
+ <option value="ar">Arabic</option>
126
+ <option value="en" selected>English</option>
127
+ <option value="fr">French</option>
128
+ <option value="es">Spanish</option>
129
+ <option value="zh">Chinese</option>
130
+ </select>
131
+ </div>
132
+ <div>
133
+ <label for="targetLanguage" class="block text-sm font-medium text-gray-700 mb-1">To:</label>
134
+ <select id="targetLanguage" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 bg-white text-gray-900">
135
+ <option value="ar" selected>Arabic</option>
136
+ <option value="en">English</option>
137
+ <option value="fr">French</option>
138
+ <option value="es">Spanish</option>
139
+ <option value="zh">Chinese</option>
140
+ </select>
141
+ </div>
 
142
  </div>
143
+ `
144
+ break
145
+
146
+ case "qa":
147
+ optionsContainer.classList.remove("hidden")
148
+ optionsContainer.innerHTML = `
149
+ <div class="mb-2">
150
+ <label for="contextInput" class="block text-sm font-medium text-gray-700 mb-1">Reference Text:</label>
151
+ <textarea id="contextInput" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 bg-white text-gray-900 resize-none" rows="2" placeholder="Enter reference text here..."></textarea>
152
+ </div>
153
+ `
154
+ // Auto-resize for the new textarea
155
+ document.getElementById("contextInput").addEventListener("input", autoResizeTextarea)
156
+ break
157
+
158
+ default:
159
+ optionsContainer.classList.add("hidden")
160
+ break
161
+ }
162
+ }
163
+
164
+ // Handle file selection
165
+ function handleFileSelection() {
166
+ const fileInput = document.getElementById("file-upload")
167
+ const userInput = document.getElementById("user-input")
168
+
169
+ if (fileInput.files.length > 0) {
170
+ const file = fileInput.files[0]
171
+
172
+ // Create file preview element
173
+ const filePreview = document.createElement("div")
174
+ filePreview.className = "file-preview"
175
+ filePreview.innerHTML = `
176
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
177
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13" />
178
  </svg>
179
+ <span class="file-preview-name">${file.name}</span>
180
+ <span class="file-preview-remove" onclick="removeFile()">×</span>
181
  `
182
+
183
+ // Add file preview after textarea
184
+ const inputContainer = userInput.parentElement
185
+
186
+ // Remove any existing file preview
187
+ const existingPreview = inputContainer.querySelector(".file-preview")
188
+ if (existingPreview) {
189
+ existingPreview.remove()
 
 
 
 
 
 
 
 
 
190
  }
191
+
192
+ inputContainer.appendChild(filePreview)
193
+
194
+ // Update placeholder text
195
+ userInput.placeholder = "Add a message about this file..."
196
  }
197
+ }
198
+
199
+ // Remove selected file
200
+ const removeFile = () => {
201
+ const fileInput = document.getElementById("file-upload")
202
+ const userInput = document.getElementById("user-input")
203
+ const filePreview = document.querySelector(".file-preview")
204
+
205
+ // Reset file input
206
+ fileInput.value = ""
207
+
208
+ // Remove file preview
209
+ if (filePreview) {
210
+ filePreview.remove()
 
 
 
 
 
 
 
 
211
  }
212
+
213
+ // Reset placeholder
214
+ userInput.placeholder = "Type your message..."
215
+ }
216
+
217
+ window.removeFile = removeFile
218
+
219
+ // Handle chat form submission
220
+ function handleChatSubmit(e) {
221
+ e.preventDefault()
222
+
223
+ const userInput = document.getElementById("user-input")
224
+ const fileInput = document.getElementById("file-upload")
225
+ const activeFeature = document.querySelector(".feature-btn.active").dataset.feature
226
+
227
+ // Get user message
228
+ const userMessage = userInput.value.trim()
229
+
230
+ // Check if there's a message or file
231
+ if (!userMessage && fileInput.files.length === 0) {
232
+ showNotification("Please enter a message or select a file", "warning")
233
+ return
 
 
 
 
 
 
 
 
 
 
 
 
234
  }
235
+
236
+ // Add user message to chat
237
+ if (userMessage) {
238
+ addMessageToChat("user", userMessage)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  }
240
+
241
+ // Process based on active feature
242
+ processRequest(activeFeature, userMessage, fileInput.files[0])
243
+
244
+ // Clear input and file
245
+ userInput.value = ""
246
+ userInput.style.height = "auto"
247
+
248
+ if (fileInput.files.length > 0) {
249
+ removeFile()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  }
251
+
252
+ // Scroll to bottom
253
+ scrollToBottom()
254
+ }
255
+
256
+ // Process request based on feature
257
+ function processRequest(feature, message, file) {
258
+ // Show loading indicator
259
+ showLoadingIndicator()
260
+
261
+ // Determine which API endpoint to use
262
+ let endpoint
263
+ const formData = new FormData()
264
+
265
+ switch (feature) {
266
+ case "summarize":
267
+ endpoint = "summarize"
268
+ formData.append("text", message)
269
+ break
270
+
271
+ case "translate":
272
+ endpoint = "translate"
273
+ formData.append("text", message)
274
+ formData.append("source_lang", document.getElementById("sourceLanguage").value)
275
+ formData.append("target_lang", document.getElementById("targetLanguage").value)
276
+ break
277
+
278
+ case "qa":
279
+ endpoint = "qa/"
280
+ // Use fetch with JSON instead of FormData for QA
281
+ const context = document.getElementById("contextInput").value.trim()
282
+ if (!context) {
283
+ hideLoadingIndicator()
284
+ showNotification("Please enter reference text", "warning")
285
+ return
286
  }
287
+ // We'll handle this differently below
288
+ break
289
+
290
+ case "code":
291
+ endpoint = "generate_code/"
292
+ formData.append("prompt", message)
293
+ break
294
+
295
+ case "document":
296
+ if (!file) {
297
+ hideLoadingIndicator()
298
+ showNotification("Please select a document file", "warning")
 
 
 
 
 
 
 
299
  return
300
  }
301
+ endpoint = "extract_text"
302
+ formData.append("file", file)
303
+ break
304
+
305
+ case "image":
306
+ if (!file) {
307
+ hideLoadingIndicator()
308
+ showNotification("Please select an image file", "warning")
309
+ return
 
 
 
 
 
 
 
 
 
 
 
 
 
310
  }
311
+ endpoint = "image_caption"
312
+ formData.append("file", file)
313
+ break
314
+
315
+ default:
316
+ // For regular chat, we'll simulate a response
317
+ setTimeout(() => {
318
+ const responses = [
319
+ "I'm here to help! What would you like to know?",
320
+ "That's an interesting question. Let me think about it...",
321
+ "I can assist with summarizing text, translating languages, answering questions, generating code, and analyzing documents and images. What would you like me to help you with?",
322
+ "I understand. Is there anything specific you'd like me to explain further?",
323
+ "I'm your AI assistant. I'm designed to be helpful, harmless, and honest.",
324
+ ]
325
+ const randomResponse = responses[Math.floor(Math.random() * responses.length)]
326
+ addMessageToChat("assistant", randomResponse)
327
+ hideLoadingIndicator()
328
+ scrollToBottom()
329
+
330
+ // Add to history
331
+ addToHistory("Chat", message.substring(0, 30) + (message.length > 30 ? "..." : ""))
332
+ }, 1000)
333
  return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  }
335
+
336
+ // Special handling for QA
337
+ if (feature === "qa") {
338
+ const context = document.getElementById("contextInput").value.trim()
339
+ const question = message
340
+
341
+ fetch("https://soltane777-textgeneration.hf.space/qa/", {
342
+ method: "POST",
343
+ headers: { "Content-Type": "application/json" },
344
+ body: JSON.stringify({ context: context, question: question }),
345
+ })
346
+ .then((response) => {
347
+ if (!response.ok) {
348
+ throw new Error(`Server responded with status: ${response.status}`)
349
+ }
350
+ return response.json()
351
  })
352
+ .then((data) => {
353
+ const answer = data.answer || "Sorry, I couldn't find an answer to that question."
354
+ addMessageToChat("assistant", answer)
355
+ addToHistory("Question Answered", question.substring(0, 30) + (question.length > 30 ? "..." : ""))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
356
  })
357
+ .catch((error) => {
358
+ console.error("Error:", error)
359
+ addMessageToChat("assistant", `Error: ${error.message || "Failed to get an answer"}`)
360
+ showNotification("Error getting answer: " + (error.message || "Unknown error"), "error")
361
+ })
362
+ .finally(() => {
363
+ hideLoadingIndicator()
364
+ scrollToBottom()
365
+ })
366
+
367
+ return
368
+ }
369
+
370
+ // Process other requests
371
+ fetch(`https://soltane777-textgeneration.hf.space/${endpoint}`, {
372
+ method: "POST",
373
+ body: formData,
374
+ })
375
+ .then((response) => {
376
  if (!response.ok) {
377
  throw new Error(`Server responded with status: ${response.status}`)
378
  }
379
+ return response.json()
380
+ })
381
+ .then((data) => {
382
+ let result
383
+
384
+ switch (feature) {
385
+ case "summarize":
386
+ result = data.summary || "Sorry, I couldn't summarize that text."
387
+ addToHistory("Summarization", "Text summarized")
388
+ break
389
+
390
+ case "translate":
391
+ const sourceLang = getLanguageName(document.getElementById("sourceLanguage").value)
392
+ const targetLang = getLanguageName(document.getElementById("targetLanguage").value)
393
+ result = data.translation || "Sorry, I couldn't translate that text."
394
+ addToHistory("Translation", `${sourceLang} to ${targetLang}`)
395
+ break
396
+
397
+ case "code":
398
+ result = formatCodeResponse(data)
399
+ addToHistory("Code Generation", message.substring(0, 30) + (message.length > 30 ? "..." : ""))
400
+ break
401
+
402
+ case "document":
403
+ result = data.text || "Sorry, I couldn't extract text from that document."
404
+ addToHistory("Text Extraction", "Extracted from file")
405
+ break
406
+
407
+ case "image":
408
+ result = data.caption || "Sorry, I couldn't analyze that image."
409
+ addToHistory("Image Analysis", "Image analyzed")
410
+ break
411
+
412
+ default:
413
+ result = JSON.stringify(data)
414
+ }
415
+
416
+ addMessageToChat("assistant", result)
417
+ })
418
+ .catch((error) => {
419
  console.error("Error:", error)
420
+ addMessageToChat("assistant", `Error: ${error.message || "Failed to process request"}`)
421
+ showNotification("Error: " + (error.message || "Unknown error"), "error")
422
+ })
423
+ .finally(() => {
424
+ hideLoadingIndicator()
425
+ scrollToBottom()
426
+ })
427
+ }
428
+
429
+ // Format code response
430
+ function formatCodeResponse(data) {
431
+ if (!data.code && !data[0]) {
432
+ return "Sorry, I couldn't generate code for that prompt."
433
  }
434
+
435
+ const code = data.code || data[0].generated_text || JSON.stringify(data)
436
+
437
+ // Format as code block
438
+ return "\`\`\`\n" + code + "\n\`\`\`"
439
+ }
440
+
441
+ // Add message to chat
442
+ function addMessageToChat(sender, message) {
443
+ const chatMessages = document.getElementById("chat-messages")
444
+ const messageDiv = document.createElement("div")
445
+
446
+ if (sender === "user") {
447
+ messageDiv.className = "flex justify-end mb-4"
448
+ messageDiv.innerHTML = `
449
+ <div class="max-w-3xl user-message p-4 shadow-sm">
450
+ <p class="text-gray-800 whitespace-pre-wrap">${formatMessage(message)}</p>
451
+ </div>
452
+ `
453
+ } else {
454
+ messageDiv.className = "flex items-start mb-4"
455
+ messageDiv.innerHTML = `
456
+ <div class="flex-shrink-0 bg-purple-600 rounded-full p-2 mr-3">
457
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
458
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
459
+ </svg>
460
+ </div>
461
+ <div class="max-w-3xl assistant-message p-4 shadow-sm">
462
+ <div class="text-gray-800 whitespace-pre-wrap">${formatMessage(message)}</div>
463
+ <div class="message-actions">
464
+ <button class="message-action-btn copy-btn" onclick="copyToClipboard(this)">
465
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 mr-1 inline" fill="none" viewBox="0 0 24 24" stroke="currentColor">
466
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
467
+ </svg>
468
+ Copy
469
+ </button>
470
+ </div>
471
+ </div>
472
+ `
473
+ }
474
+
475
+ chatMessages.appendChild(messageDiv)
476
+ }
477
+
478
+ // Format message with code blocks and links
479
+ function formatMessage(message) {
480
+ // Replace code blocks
481
+ message = message.replace(/\`\`\`([\s\S]*?)\`\`\`/g, (match, code) => {
482
+ return `<pre class="code-content">${escapeHtml(code)}</pre>`
483
+ })
484
+
485
+ // Replace inline code
486
+ message = message.replace(/`([^`]+)`/g, (match, code) => {
487
+ return `<code class="bg-gray-100 px-1 py-0.5 rounded text-sm font-mono">${escapeHtml(code)}</code>`
488
+ })
489
+
490
+ // Replace URLs with links
491
+ message = message.replace(/(https?:\/\/[^\s]+)/g, (url) => {
492
+ return `<a href="${url}" target="_blank" class="text-purple-600 hover:underline">${url}</a>`
493
+ })
494
+
495
+ return message
496
+ }
497
+
498
+ // Escape HTML
499
+ function escapeHtml(unsafe) {
500
+ return unsafe
501
+ .replace(/&/g, "&amp;")
502
+ .replace(/</g, "&lt;")
503
+ .replace(/>/g, "&gt;")
504
+ .replace(/"/g, "&quot;")
505
+ .replace(/'/g, "&#039;")
506
+ }
507
+
508
+ // Copy to clipboard
509
+ window.copyToClipboard = (button) => {
510
+ const messageDiv = button.closest(".assistant-message")
511
+ const textContent = messageDiv.querySelector(".whitespace-pre-wrap").innerText
512
+
513
+ navigator.clipboard
514
+ .writeText(textContent)
515
+ .then(() => {
516
+ // Change button text temporarily
517
+ const originalText = button.innerHTML
518
+ button.innerHTML = `
519
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 mr-1 inline" fill="none" viewBox="0 0 24 24" stroke="currentColor">
520
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
521
+ </svg>
522
+ Copied!
523
+ `
524
+
525
+ setTimeout(() => {
526
+ button.innerHTML = originalText
527
+ }, 2000)
528
+
529
+ showNotification("Text copied to clipboard!")
530
+ })
531
+ .catch((err) => {
532
+ console.error("Failed to copy text: ", err)
533
+ showNotification("Failed to copy text: " + err.message, "error")
534
+ })
535
+ }
536
+
537
+ // Show loading indicator
538
+ function showLoadingIndicator() {
539
+ const chatMessages = document.getElementById("chat-messages")
540
+
541
+ // Create loading message
542
+ const loadingDiv = document.createElement("div")
543
+ loadingDiv.id = "loading-indicator"
544
+ loadingDiv.className = "flex items-start mb-4"
545
+ loadingDiv.innerHTML = `
546
+ <div class="flex-shrink-0 bg-purple-600 rounded-full p-2 mr-3">
547
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
548
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
549
+ </svg>
550
+ </div>
551
+ <div class="max-w-3xl assistant-message p-4 shadow-sm flex items-center">
552
+ <div class="spinner mr-3"></div>
553
+ <p class="text-gray-600">Thinking...</p>
554
+ </div>
555
+ `
556
+
557
+ chatMessages.appendChild(loadingDiv)
558
+ scrollToBottom()
559
+ }
560
+
561
+ // Hide loading indicator
562
+ function hideLoadingIndicator() {
563
+ const loadingIndicator = document.getElementById("loading-indicator")
564
+ if (loadingIndicator) {
565
+ loadingIndicator.remove()
566
+ }
567
+ }
568
+
569
+ // Scroll to bottom of chat
570
+ function scrollToBottom() {
571
+ const chatMessages = document.getElementById("chat-messages")
572
+ chatMessages.scrollTop = chatMessages.scrollHeight
573
+ }
574
+
575
+ // Show notification
576
+ function showNotification(message, type = "success") {
577
+ const container = document.getElementById("notification-container")
578
+
579
+ // Create notification element
580
+ const notification = document.createElement("div")
581
+
582
+ // Set class based on notification type
583
+ if (type === "success") {
584
+ notification.className =
585
+ "notification mb-2 bg-green-100 border-l-4 border-green-500 text-green-700 p-4 rounded shadow-md"
586
+ } else if (type === "error") {
587
+ notification.className = "notification mb-2 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded shadow-md"
588
+ } else if (type === "warning") {
589
+ notification.className =
590
+ "notification mb-2 bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4 rounded shadow-md"
591
+ } else if (type === "info") {
592
+ notification.className =
593
+ "notification mb-2 bg-blue-100 border-l-4 border-blue-500 text-blue-700 p-4 rounded shadow-md"
594
+ }
595
+
596
+ notification.innerHTML = message
597
+
598
+ // Add to container
599
+ container.appendChild(notification)
600
+
601
+ // Remove after 3 seconds
602
+ setTimeout(() => {
603
+ notification.style.opacity = "0"
604
+ notification.style.transform = "translateY(-20px)"
605
+ notification.style.transition = "opacity 0.5s, transform 0.5s"
606
+
607
+ setTimeout(() => {
608
+ notification.remove()
609
+ }, 500)
610
+ }, 3000)
611
+ }
612
+
613
+ // History panel functions
614
+ function toggleHistoryPanel() {
615
+ const historyPanel = document.getElementById("historyPanel")
616
+ if (historyPanel.classList.contains("translate-x-full")) {
617
+ historyPanel.classList.remove("translate-x-full")
618
+ } else {
619
+ historyPanel.classList.add("translate-x-full")
620
+ }
621
+ }
622
+
623
+ function addToHistory(action, details) {
624
+ // Get existing history or initialize empty array
625
+ const history = JSON.parse(localStorage.getItem("aiAppHistory") || "[]")
626
+
627
+ // Add new item
628
+ history.unshift({
629
+ action,
630
+ details,
631
+ timestamp: new Date().toISOString(),
632
+ })
633
+
634
+ // Keep only the last 20 items
635
+ if (history.length > 20) {
636
+ history.pop()
637
+ }
638
+
639
+ // Save back to localStorage
640
+ localStorage.setItem("aiAppHistory", JSON.stringify(history))
641
+
642
+ // Update UI if history panel exists
643
+ loadHistory()
644
+ }
645
+
646
+ function loadHistory() {
647
+ const historyItemsContainer = document.getElementById("historyItems")
648
+ if (!historyItemsContainer) return
649
+
650
+ // Clear existing items
651
+ historyItemsContainer.innerHTML = ""
652
+
653
+ // Get history from localStorage
654
+ const history = JSON.parse(localStorage.getItem("aiAppHistory") || "[]")
655
+
656
+ if (history.length === 0) {
657
+ historyItemsContainer.innerHTML = `<p class="text-gray-500 text-center">No history yet</p>`
658
+ return
659
+ }
660
+
661
+ // Add each history item
662
+ history.forEach((item) => {
663
+ const historyItem = document.createElement("div")
664
+ historyItem.className = "bg-gray-100 p-3 rounded-lg"
665
+
666
+ const date = new Date(item.timestamp)
667
+ const formattedDate = date.toLocaleDateString() + " " + date.toLocaleTimeString()
668
+
669
+ historyItem.innerHTML = `
670
+ <div class="flex justify-between items-start">
671
+ <h4 class="font-medium text-gray-800">${item.action}</h4>
672
+ <span class="text-xs text-gray-500">${formattedDate}</span>
673
+ </div>
674
+ <p class="text-sm text-gray-600 mt-1">${item.details}</p>
675
+ `
676
+
677
+ historyItemsContainer.appendChild(historyItem)
678
+ })
679
+ }
680
+
681
+ // Helper function to get language name from code
682
+ function getLanguageName(code) {
683
+ const languages = {
684
+ ar: "Arabic",
685
+ en: "English",
686
+ fr: "French",
687
+ es: "Spanish",
688
+ zh: "Chinese",
689
  }
690
+ return languages[code] || code
691
+ }
frontend/style.css CHANGED
@@ -1,286 +1,466 @@
1
- /* Add these styles to ensure dark mode works properly */
2
- :root {
3
- color-scheme: light;
4
- }
5
-
6
- html.dark {
7
- color-scheme: dark;
8
- }
9
-
10
- /* Make sure transitions are smooth */
11
- html {
12
- transition: background-color 0.3s ease, color 0.3s ease;
13
- }
14
-
15
- @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap");
16
-
17
- body {
18
- font-family: "Poppins", Arial, sans-serif;
19
- margin: 0;
20
- padding: 0;
21
- }
22
-
23
- /* Ensure dark mode styles are applied */
24
- html.dark body {
25
- background-color: #1a202c;
26
- color: #f7fafc;
27
- }
28
-
29
- .tab-button.active {
30
- color: #7c3aed;
31
- border-bottom: 2px solid #7c3aed;
32
- }
33
-
34
- html.dark .tab-button.active {
35
- color: #a78bfa;
36
- border-bottom: 2px solid #a78bfa;
37
- }
38
-
39
- .tab-button:not(.active) {
40
- border-bottom: 2px solid transparent;
41
- }
42
-
43
- #output {
44
- font-family: "Poppins", Arial, sans-serif;
45
- }
46
-
47
- /* Custom scrollbar */
48
- ::-webkit-scrollbar {
49
- width: 8px;
50
- height: 8px;
51
- }
52
-
53
- ::-webkit-scrollbar-track {
54
- background: #f1f1f1;
55
- }
56
-
57
- html.dark ::-webkit-scrollbar-track {
58
- background: #374151;
59
- }
60
-
61
- ::-webkit-scrollbar-thumb {
62
- background: #c4b5fd;
63
- border-radius: 10px;
64
- }
65
-
66
- ::-webkit-scrollbar-thumb:hover {
67
- background: #a78bfa;
68
- }
69
-
70
- /* Animation for notifications */
71
- @keyframes fadeIn {
72
- from {
73
- opacity: 0;
74
- transform: translateY(-10px);
75
- }
76
- to {
77
- opacity: 1;
78
- transform: translateY(0);
79
- }
80
- }
81
-
82
- .notification {
83
- animation: fadeIn 0.3s ease-out forwards;
84
- }
85
-
86
- /* Language selection styles */
87
- #translationOptions {
88
- transition: all 0.3s ease;
89
- }
90
-
91
- #translationOptions.hidden {
92
- display: none;
93
- }
94
-
95
- select {
96
- appearance: none;
97
- background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
98
- background-repeat: no-repeat;
99
- background-position: right 0.7rem center;
100
- background-size: 1em;
101
- padding-right: 2.5rem;
102
- }
103
-
104
- /* Formatted output styles */
105
- .formatted-output {
106
- display: flex;
107
- flex-direction: column;
108
- gap: 16px;
109
- width: 100%;
110
- }
111
-
112
- .result-header {
113
- margin-bottom: 8px;
114
- }
115
-
116
- .result-header h3 {
117
- font-size: 18px;
118
- font-weight: 600;
119
- color: #4b5563;
120
- margin: 0;
121
- padding-bottom: 8px;
122
- border-bottom: 1px solid #e5e7eb;
123
- }
124
-
125
- html.dark .result-header h3 {
126
- color: #e5e7eb;
127
- border-bottom: 1px solid #4b5563;
128
- }
129
-
130
- .text-box {
131
- background-color: white;
132
- border-radius: 8px;
133
- border: 1px solid #e5e7eb;
134
- overflow: hidden;
135
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
136
- }
137
-
138
- html.dark .text-box {
139
- background-color: #1f2937;
140
- border: 1px solid #374151;
141
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
142
- }
143
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  .text-box-header {
145
- background-color: #f9fafb;
146
- padding: 8px 12px;
147
- font-weight: 500;
148
- color: #4b5563;
149
- border-bottom: 1px solid #e5e7eb;
150
- font-size: 14px;
151
- }
152
-
153
- html.dark .text-box-header {
154
- background-color: #374151;
155
- color: #e5e7eb;
156
- border-bottom: 1px solid #4b5563;
157
  }
158
-
159
  .text-content {
160
- padding: 12px 16px;
161
- line-height: 1.6;
162
- color: #1f2937;
163
- }
164
-
165
- html.dark .text-content {
166
- color: #f3f4f6;
167
- }
168
-
169
- .original .text-box-header {
170
- background-color: #eff6ff;
171
- color: #1e40af;
172
- }
173
-
174
- html.dark .original .text-box-header {
175
- background-color: #1e3a8a;
176
- color: #93c5fd;
177
- }
178
-
179
- .translated .text-box-header {
180
- background-color: #f0fdf4;
181
- color: #166534;
182
- }
183
-
184
- html.dark .translated .text-box-header {
185
- background-color: #14532d;
186
- color: #86efac;
187
- }
188
-
189
- .question .text-box-header {
190
- background-color: #fff7ed;
191
- color: #9a3412;
192
- }
193
-
194
- html.dark .question .text-box-header {
195
- background-color: #7c2d12;
196
- color: #fdba74;
197
- }
198
-
199
- .answer .text-box-header {
200
- background-color: #ecfdf5;
201
- color: #065f46;
202
- }
203
-
204
- html.dark .answer .text-box-header {
205
- background-color: #064e3b;
206
- color: #6ee7b7;
207
- }
208
-
209
- .code-content {
210
- padding: 12px 16px;
211
- font-family: monospace;
212
- white-space: pre-wrap;
213
- background-color: #f8fafc;
214
- color: #334155;
215
- line-height: 1.5;
216
  font-size: 14px;
217
- overflow-x: auto;
218
- }
219
-
220
- html.dark .code-content {
221
- background-color: #0f172a;
222
- color: #e2e8f0;
223
- }
224
-
225
- .error-message {
226
- color: #b91c1c;
227
- padding: 12px;
228
- background-color: #fee2e2;
229
- border-radius: 6px;
230
- font-weight: 500;
231
- }
232
-
233
- html.dark .error-message {
234
- color: #fca5a5;
235
- background-color: #7f1d1d;
236
  }
237
-
238
- /* Responsive adjustments */
239
- @media (max-width: 640px) {
240
- .text-box-header {
241
- padding: 6px 10px;
242
- font-size: 13px;
243
- }
244
-
245
- .text-content {
246
- padding: 10px 12px;
247
- font-size: 14px;
248
- }
249
- }
250
-
251
- /* Theme toggle button animation */
252
- #theme-toggle {
253
- cursor: pointer;
254
- transition: transform 0.2s ease, background-color 0.2s ease;
255
- }
256
-
257
- #theme-toggle:hover {
258
- background-color: rgba(255, 255, 255, 0.3);
259
- }
260
-
261
- #theme-toggle:active {
262
- transform: scale(0.9);
263
  }
264
-
265
- /* History panel styles */
266
- #historyPanel {
267
- box-shadow: -2px 0 10px rgba(0, 0, 0, 0.1);
268
  }
269
-
270
- html.dark #historyPanel {
271
- box-shadow: -2px 0 10px rgba(0, 0, 0, 0.3);
 
 
 
272
  }
273
-
274
- /* Text-to-speech button styles */
275
- .text-to-speech-btn {
276
- transition: all 0.2s ease;
277
  }
278
-
279
- .text-to-speech-btn:hover svg {
280
- color: #7c3aed;
 
281
  }
282
-
283
- html.dark .text-to-speech-btn:hover svg {
284
- color: #a78bfa;
 
285
  }
286
-
 
1
+ @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap");
2
+
3
+ body {
4
+ font-family: "Poppins", Arial, sans-serif;
5
+ margin: 0;
6
+ padding: 0;
7
+ background-color: #f5f7fa;
8
+ transition: background-color 0.3s ease;
9
+ }
10
+
11
+ /* Dark mode styles */
12
+ html.dark body {
13
+ background-color: #111827;
14
+ color: #f3f4f6;
15
+ }
16
+
17
+ html.dark .app-content {
18
+ border-left: 1px solid #374151;
19
+ border-right: 1px solid #374151;
20
+ }
21
+
22
+ html.dark main,
23
+ html.dark #chat-messages,
24
+ html.dark .feature-btn:not(.active),
25
+ html.dark .user-message,
26
+ html.dark textarea,
27
+ html.dark select {
28
+ background-color: #1f2937;
29
+ color: #f3f4f6;
30
+ }
31
+
32
+ html.dark .feature-btn:not(.active) {
33
+ border-color: #374151;
34
+ color: #d1d5db;
35
+ }
36
+
37
+ html.dark .feature-btn:hover:not(.active) {
38
+ background-color: #374151;
39
+ }
40
+
41
+ html.dark .assistant-message {
42
+ background-color: #1f2937;
43
+ border-color: #374151;
44
+ border-left: 3px solid #7c3aed;
45
+ }
46
+
47
+ html.dark .user-message {
48
+ border-color: #374151;
49
+ }
50
+
51
+ html.dark .border-gray-100,
52
+ html.dark .border-gray-200,
53
+ html.dark .border-gray-300 {
54
+ border-color: #374151;
55
+ }
56
+
57
+ html.dark .text-gray-700,
58
+ html.dark .text-gray-800,
59
+ html.dark .text-gray-900 {
60
+ color: #e5e7eb;
61
+ }
62
+
63
+ html.dark .text-gray-500,
64
+ html.dark .text-gray-600 {
65
+ color: #9ca3af;
66
+ }
67
+
68
+ html.dark .bg-white {
69
+ background-color: #1f2937;
70
+ }
71
+
72
+ html.dark .bg-gray-100 {
73
+ background-color: #374151;
74
+ }
75
+
76
+ html.dark .code-content {
77
+ background-color: #111827;
78
+ border-color: #374151;
79
+ color: #e5e7eb;
80
+ }
81
+
82
+ html.dark .message-action-btn {
83
+ background-color: #374151;
84
+ color: #9ca3af;
85
+ border-color: #4b5563;
86
+ }
87
+
88
+ html.dark .message-action-btn:hover {
89
+ background-color: #4b5563;
90
+ color: #e5e7eb;
91
+ }
92
+
93
+ html.dark #historyPanel {
94
+ background-color: #1f2937;
95
+ }
96
+
97
+ html.dark #historyItems .bg-gray-100 {
98
+ background-color: #374151;
99
+ border-color: #4b5563;
100
+ }
101
+
102
+ html.dark .file-preview {
103
+ background-color: #374151;
104
+ border-color: #4b5563;
105
+ }
106
+
107
+ /* App container with borders */
108
+ .app-container {
109
+ display: flex;
110
+ flex-direction: column;
111
+ height: 100vh;
112
+ padding: 20px;
113
+ }
114
+
115
+ .app-content {
116
+ max-width: 1200px;
117
+ width: 100%;
118
+ margin: 0 auto;
119
+ display: flex;
120
+ flex-direction: column;
121
+ height: 100%;
122
+ border-radius: 12px;
123
+ overflow: hidden;
124
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
125
+ border-left: 1px solid #e5e7eb;
126
+ border-right: 1px solid #e5e7eb;
127
+ }
128
+
129
+ /* Feature buttons styling */
130
+ .feature-buttons {
131
+ -ms-overflow-style: none; /* Hide scrollbar in IE and Edge */
132
+ scrollbar-width: none; /* Hide scrollbar in Firefox */
133
+ }
134
+
135
+ .feature-buttons::-webkit-scrollbar {
136
+ display: none; /* Hide scrollbar in Chrome, Safari and Opera */
137
+ }
138
+
139
+ .feature-btn {
140
+ display: flex;
141
+ align-items: center;
142
+ gap: 0.5rem;
143
+ padding: 0.5rem 1rem;
144
+ border-radius: 0.5rem;
145
+ font-size: 0.875rem;
146
+ font-weight: 500;
147
+ color: #4b5563;
148
+ background-color: #f9fafb;
149
+ transition: all 0.2s ease;
150
+ white-space: nowrap;
151
+ border: 1px solid #f0f0f0;
152
+ }
153
+
154
+ .feature-btn:hover {
155
+ background-color: #f3f4f6;
156
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
157
+ }
158
+
159
+ .feature-btn.active {
160
+ color: #ffffff;
161
+ background-color: #7c3aed;
162
+ border: 1px solid #7c3aed;
163
+ }
164
+
165
+ /* Chat message styling */
166
+ .user-message {
167
+ background-color: #f9fafb;
168
+ border-radius: 0.75rem 0.75rem 0 0.75rem;
169
+ border: 1px solid #f0f0f0;
170
+ }
171
+
172
+ .assistant-message {
173
+ background-color: #ffffff;
174
+ border-radius: 0.75rem 0.75rem 0.75rem 0;
175
+ border-left: 3px solid #7c3aed;
176
+ border-top: 1px solid #f0f0f0;
177
+ border-right: 1px solid #f0f0f0;
178
+ border-bottom: 1px solid #f0f0f0;
179
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
180
+ }
181
+
182
+ /* Auto-resize textarea */
183
+ textarea {
184
+ overflow-y: hidden;
185
+ min-height: 44px;
186
+ max-height: 200px;
187
+ background-color: #ffffff;
188
+ }
189
+
190
+ /* Custom scrollbar */
191
+ ::-webkit-scrollbar {
192
+ width: 8px;
193
+ height: 8px;
194
+ }
195
+
196
+ ::-webkit-scrollbar-track {
197
+ background: #ffffff;
198
+ }
199
+
200
+ ::-webkit-scrollbar-thumb {
201
+ background: #c4b5fd;
202
+ border-radius: 10px;
203
+ }
204
+
205
+ ::-webkit-scrollbar-thumb:hover {
206
+ background: #a78bfa;
207
+ }
208
+
209
+ /* Animation for notifications */
210
+ @keyframes fadeIn {
211
+ from {
212
+ opacity: 0;
213
+ transform: translateY(-10px);
214
+ }
215
+ to {
216
+ opacity: 1;
217
+ transform: translateY(0);
218
+ }
219
+ }
220
+
221
+ .notification {
222
+ animation: fadeIn 0.3s ease-out forwards;
223
+ }
224
+
225
+ /* Language selection styles */
226
+ .language-selector {
227
+ display: grid;
228
+ grid-template-columns: 1fr 1fr;
229
+ gap: 0.75rem;
230
+ }
231
+
232
+ select {
233
+ appearance: none;
234
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
235
+ background-repeat: no-repeat;
236
+ background-position: right 0.7rem center;
237
+ background-size: 1em;
238
+ padding-right: 2.5rem;
239
+ background-color: #ffffff;
240
+ }
241
+
242
+ /* Formatted output styles */
243
+ .formatted-output {
244
+ display: flex;
245
+ flex-direction: column;
246
+ gap: 16px;
247
+ width: 100%;
248
+ }
249
+
250
+ .result-header {
251
+ margin-bottom: 8px;
252
+ }
253
+
254
+ .result-header h3 {
255
+ font-size: 18px;
256
+ font-weight: 600;
257
+ color: #4b5563;
258
+ margin: 0;
259
+ padding-bottom: 8px;
260
+ border-bottom: 1px solid #f0f0f0;
261
+ }
262
+
263
+ .text-box {
264
+ background-color: white;
265
+ border-radius: 8px;
266
+ border: 1px solid #f0f0f0;
267
+ overflow: hidden;
268
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
269
+ }
270
+
271
+ .text-box-header {
272
+ background-color: #f9fafb;
273
+ padding: 8px 12px;
274
+ font-weight: 500;
275
+ color: #4b5563;
276
+ border-bottom: 1px solid #f0f0f0;
277
+ font-size: 14px;
278
+ display: flex;
279
+ justify-content: space-between;
280
+ align-items: center;
281
+ }
282
+
283
+ .text-content {
284
+ padding: 12px 16px;
285
+ line-height: 1.6;
286
+ color: #1f2937;
287
+ background-color: #ffffff;
288
+ }
289
+
290
+ .code-content {
291
+ padding: 12px 16px;
292
+ font-family: monospace;
293
+ white-space: pre-wrap;
294
+ background-color: #f9fafb;
295
+ color: #334155;
296
+ line-height: 1.5;
297
+ font-size: 14px;
298
+ overflow-x: auto;
299
+ border: 1px solid #f0f0f0;
300
+ border-radius: 6px;
301
+ }
302
+
303
+ .error-message {
304
+ color: #b91c1c;
305
+ padding: 12px;
306
+ background-color: #fee2e2;
307
+ border-radius: 6px;
308
+ font-weight: 500;
309
+ }
310
+
311
+ /* File upload preview */
312
+ .file-preview {
313
+ display: flex;
314
+ align-items: center;
315
+ gap: 0.5rem;
316
+ padding: 0.5rem;
317
+ background-color: #f9fafb;
318
+ border-radius: 0.5rem;
319
+ margin-top: 0.5rem;
320
+ border: 1px solid #f0f0f0;
321
+ }
322
+
323
+ .file-preview-name {
324
+ font-size: 0.875rem;
325
+ color: #4b5563;
326
+ flex: 1;
327
+ white-space: nowrap;
328
+ overflow: hidden;
329
+ text-overflow: ellipsis;
330
+ }
331
+
332
+ .file-preview-remove {
333
+ color: #ef4444;
334
+ cursor: pointer;
335
+ }
336
+
337
+ /* Loading spinner */
338
+ .spinner {
339
+ border: 3px solid rgba(124, 58, 237, 0.3);
340
+ border-radius: 50%;
341
+ border-top: 3px solid #7c3aed;
342
+ width: 24px;
343
+ height: 24px;
344
+ animation: spin 1s linear infinite;
345
+ }
346
+
347
+ @keyframes spin {
348
+ 0% {
349
+ transform: rotate(0deg);
350
+ }
351
+ 100% {
352
+ transform: rotate(360deg);
353
+ }
354
+ }
355
+
356
+ /* Message actions */
357
+ .message-actions {
358
+ display: flex;
359
+ gap: 0.5rem;
360
+ margin-top: 0.5rem;
361
+ }
362
+
363
+ .message-action-btn {
364
+ padding: 0.25rem 0.5rem;
365
+ font-size: 0.75rem;
366
+ color: #6b7280;
367
+ background-color: #f9fafb;
368
+ border-radius: 0.25rem;
369
+ transition: all 0.2s ease;
370
+ border: 1px solid #f0f0f0;
371
+ }
372
+
373
+ .message-action-btn:hover {
374
+ background-color: #f3f4f6;
375
+ color: #4b5563;
376
+ }
377
+
378
+ /* History panel */
379
+ #historyPanel {
380
+ background-color: #ffffff;
381
+ box-shadow: -2px 0 10px rgba(0, 0, 0, 0.05);
382
+ }
383
+
384
+ #historyItems .bg-gray-100 {
385
+ background-color: #f9fafb;
386
+ border: 1px solid #f0f0f0;
387
+ }
388
+
389
+ /* Theme toggle animation */
390
+ #theme-toggle {
391
+ transition: transform 0.3s ease;
392
+ }
393
+
394
+ #theme-toggle:hover {
395
+ transform: rotate(15deg);
396
+ }
397
+
398
+ /* Responsive adjustments */
399
+ @media (max-width: 1024px) {
400
+ .app-container {
401
+ padding: 15px;
402
+ }
403
+ }
404
+
405
+ @media (max-width: 768px) {
406
+ .app-container {
407
+ padding: 10px;
408
+ }
409
+
410
+ .feature-btn {
411
+ padding: 0.4rem 0.8rem;
412
+ font-size: 0.8rem;
413
+ }
414
+ }
415
+
416
+ @media (max-width: 640px) {
417
+ .app-container {
418
+ padding: 5px;
419
+ }
420
+
421
+ .feature-btn {
422
+ padding: 0.375rem 0.75rem;
423
+ font-size: 0.75rem;
424
+ }
425
+
426
  .text-box-header {
427
+ padding: 6px 10px;
428
+ font-size: 13px;
 
 
 
 
 
 
 
 
 
 
429
  }
430
+
431
  .text-content {
432
+ padding: 10px 12px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433
  font-size: 14px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
  }
435
+
436
+ /* Make sure the chat form is properly sized on mobile */
437
+ #chat-form {
438
+ flex-direction: column;
439
+ gap: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
440
  }
441
+
442
+ #chat-form button {
443
+ align-self: flex-end;
 
444
  }
445
+ }
446
+
447
+ /* Fix for very small screens */
448
+ @media (max-width: 380px) {
449
+ .app-container {
450
+ padding: 0;
451
  }
452
+
453
+ .app-content {
454
+ border-radius: 0;
 
455
  }
456
+
457
+ .feature-btn {
458
+ padding: 0.25rem 0.5rem;
459
+ font-size: 0.7rem;
460
  }
461
+
462
+ .feature-btn svg {
463
+ width: 16px;
464
+ height: 16px;
465
  }
466
+ }