ismdrobiul489 commited on
Commit
8e5f1c9
Β·
1 Parent(s): 3a25ef1

perf: Optimize HTML with external CSS/JS - faster loading

Browse files
Files changed (3) hide show
  1. static/css/styles.css +359 -0
  2. static/index.html +19 -1321
  3. static/js/main.js +682 -0
static/css/styles.css ADDED
@@ -0,0 +1,359 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* NCAkit - Main Styles */
2
+
3
+ :root {
4
+ --bg-primary: #0f0f1a;
5
+ --bg-secondary: #1a1a2e;
6
+ --bg-card: #16213e;
7
+ --accent: #6366f1;
8
+ --accent-glow: rgba(99, 102, 241, 0.3);
9
+ --text-primary: #f8fafc;
10
+ --text-secondary: #94a3b8;
11
+ --success: #22c55e;
12
+ --warning: #f59e0b;
13
+ --error: #ef4444;
14
+ --border: rgba(255, 255, 255, 0.1);
15
+ }
16
+
17
+ * {
18
+ margin: 0;
19
+ padding: 0;
20
+ box-sizing: border-box;
21
+ }
22
+
23
+ body {
24
+ font-family: 'Inter', sans-serif;
25
+ background: var(--bg-primary);
26
+ color: var(--text-primary);
27
+ min-height: 100vh;
28
+ }
29
+
30
+ .container {
31
+ max-width: 1200px;
32
+ margin: 0 auto;
33
+ padding: 2rem;
34
+ }
35
+
36
+ header {
37
+ text-align: center;
38
+ padding: 2rem 0 3rem;
39
+ }
40
+
41
+ header h1 {
42
+ font-size: 2.5rem;
43
+ background: linear-gradient(135deg, #6366f1, #a855f7, #ec4899);
44
+ -webkit-background-clip: text;
45
+ background-clip: text;
46
+ -webkit-text-fill-color: transparent;
47
+ margin-bottom: 0.5rem;
48
+ }
49
+
50
+ header p {
51
+ color: var(--text-secondary);
52
+ font-size: 1.1rem;
53
+ }
54
+
55
+ /* Tabs */
56
+ .tabs {
57
+ display: flex;
58
+ gap: 1rem;
59
+ margin-bottom: 2rem;
60
+ border-bottom: 1px solid var(--border);
61
+ padding-bottom: 1rem;
62
+ flex-wrap: wrap;
63
+ }
64
+
65
+ .tab-btn {
66
+ padding: 0.75rem 1.5rem;
67
+ background: transparent;
68
+ border: none;
69
+ color: var(--text-secondary);
70
+ font-size: 1rem;
71
+ cursor: pointer;
72
+ border-radius: 8px;
73
+ transition: all 0.2s;
74
+ display: flex;
75
+ align-items: center;
76
+ gap: 0.5rem;
77
+ }
78
+
79
+ .tab-btn:hover {
80
+ background: rgba(255, 255, 255, 0.05);
81
+ color: var(--text-primary);
82
+ }
83
+
84
+ .tab-btn.active {
85
+ background: var(--accent);
86
+ color: white;
87
+ box-shadow: 0 4px 20px var(--accent-glow);
88
+ }
89
+
90
+ .tab-content {
91
+ display: none;
92
+ }
93
+
94
+ .tab-content.active {
95
+ display: block;
96
+ }
97
+
98
+ /* Cards */
99
+ .card {
100
+ background: var(--bg-card);
101
+ border-radius: 16px;
102
+ padding: 2rem;
103
+ margin-bottom: 2rem;
104
+ border: 1px solid var(--border);
105
+ }
106
+
107
+ .card h2 {
108
+ font-size: 1.5rem;
109
+ margin-bottom: 1.5rem;
110
+ display: flex;
111
+ align-items: center;
112
+ gap: 0.5rem;
113
+ }
114
+
115
+ /* Form */
116
+ .form-group {
117
+ margin-bottom: 1.25rem;
118
+ }
119
+
120
+ label {
121
+ display: block;
122
+ margin-bottom: 0.5rem;
123
+ color: var(--text-secondary);
124
+ font-size: 0.9rem;
125
+ }
126
+
127
+ input,
128
+ select,
129
+ textarea {
130
+ width: 100%;
131
+ padding: 0.75rem 1rem;
132
+ background: var(--bg-secondary);
133
+ border: 1px solid var(--border);
134
+ border-radius: 8px;
135
+ color: var(--text-primary);
136
+ font-size: 1rem;
137
+ font-family: inherit;
138
+ transition: border-color 0.2s;
139
+ }
140
+
141
+ input:focus,
142
+ select:focus,
143
+ textarea:focus {
144
+ outline: none;
145
+ border-color: var(--accent);
146
+ }
147
+
148
+ textarea {
149
+ resize: vertical;
150
+ min-height: 100px;
151
+ }
152
+
153
+ /* Buttons */
154
+ .btn {
155
+ padding: 0.75rem 1.5rem;
156
+ border: none;
157
+ border-radius: 8px;
158
+ font-size: 1rem;
159
+ font-weight: 500;
160
+ cursor: pointer;
161
+ transition: all 0.2s;
162
+ }
163
+
164
+ .btn-primary {
165
+ background: linear-gradient(135deg, #6366f1, #8b5cf6);
166
+ color: white;
167
+ box-shadow: 0 4px 15px var(--accent-glow);
168
+ }
169
+
170
+ .btn-primary:hover {
171
+ transform: translateY(-2px);
172
+ box-shadow: 0 6px 25px var(--accent-glow);
173
+ }
174
+
175
+ .btn-secondary {
176
+ background: var(--bg-secondary);
177
+ color: var(--text-primary);
178
+ border: 1px solid var(--border);
179
+ }
180
+
181
+ /* Grid */
182
+ .form-row {
183
+ display: grid;
184
+ grid-template-columns: 1fr 1fr;
185
+ gap: 1rem;
186
+ }
187
+
188
+ @media (max-width: 768px) {
189
+ .form-row {
190
+ grid-template-columns: 1fr;
191
+ }
192
+ .tabs {
193
+ flex-direction: column;
194
+ }
195
+ }
196
+
197
+ /* Status */
198
+ .status {
199
+ padding: 1rem;
200
+ border-radius: 8px;
201
+ margin-top: 1rem;
202
+ }
203
+
204
+ .status.processing {
205
+ background: rgba(99, 102, 241, 0.1);
206
+ border: 1px solid var(--accent);
207
+ }
208
+
209
+ .status.success {
210
+ background: rgba(34, 197, 94, 0.1);
211
+ border: 1px solid var(--success);
212
+ }
213
+
214
+ .status.error {
215
+ background: rgba(239, 68, 68, 0.1);
216
+ border: 1px solid var(--error);
217
+ }
218
+
219
+ .hidden {
220
+ display: none !important;
221
+ }
222
+
223
+ /* Progress */
224
+ .progress-bar {
225
+ height: 6px;
226
+ background: var(--bg-secondary);
227
+ border-radius: 3px;
228
+ overflow: hidden;
229
+ margin-top: 0.5rem;
230
+ }
231
+
232
+ .progress-fill {
233
+ height: 100%;
234
+ background: linear-gradient(90deg, #6366f1, #8b5cf6);
235
+ transition: width 0.3s;
236
+ }
237
+
238
+ /* Character Profile */
239
+ .character-section {
240
+ background: rgba(139, 92, 246, 0.1);
241
+ border: 1px solid rgba(139, 92, 246, 0.3);
242
+ border-radius: 12px;
243
+ padding: 1.5rem;
244
+ margin-bottom: 1.5rem;
245
+ }
246
+
247
+ .character-section h3 {
248
+ color: #a78bfa;
249
+ margin-bottom: 1rem;
250
+ font-size: 1rem;
251
+ }
252
+
253
+ /* Chat Widget */
254
+ .chat-float-btn {
255
+ position: fixed;
256
+ bottom: 30px;
257
+ right: 30px;
258
+ width: 60px;
259
+ height: 60px;
260
+ border-radius: 50%;
261
+ background: linear-gradient(135deg, #6366f1, #a855f7);
262
+ border: none;
263
+ font-size: 28px;
264
+ cursor: pointer;
265
+ box-shadow: 0 4px 20px rgba(99, 102, 241, 0.5);
266
+ z-index: 9999;
267
+ transition: transform 0.2s;
268
+ }
269
+
270
+ .chat-float-btn:hover {
271
+ transform: scale(1.1);
272
+ }
273
+
274
+ .chat-modal {
275
+ position: fixed;
276
+ bottom: 100px;
277
+ right: 30px;
278
+ width: 380px;
279
+ height: 500px;
280
+ background: var(--bg-card);
281
+ border-radius: 16px;
282
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
283
+ display: flex;
284
+ flex-direction: column;
285
+ z-index: 9998;
286
+ overflow: hidden;
287
+ }
288
+
289
+ .chat-modal.hidden {
290
+ display: none;
291
+ }
292
+
293
+ .chat-header {
294
+ padding: 15px 20px;
295
+ background: linear-gradient(135deg, #6366f1, #a855f7);
296
+ display: flex;
297
+ justify-content: space-between;
298
+ align-items: center;
299
+ font-weight: 600;
300
+ }
301
+
302
+ .chat-header button {
303
+ background: none;
304
+ border: none;
305
+ color: white;
306
+ font-size: 18px;
307
+ cursor: pointer;
308
+ }
309
+
310
+ .chat-body {
311
+ flex: 1;
312
+ padding: 15px;
313
+ overflow-y: auto;
314
+ display: flex;
315
+ flex-direction: column;
316
+ gap: 10px;
317
+ }
318
+
319
+ .chat-message {
320
+ padding: 10px 14px;
321
+ border-radius: 12px;
322
+ max-width: 85%;
323
+ word-wrap: break-word;
324
+ }
325
+
326
+ .chat-message.user {
327
+ background: var(--accent);
328
+ align-self: flex-end;
329
+ }
330
+
331
+ .chat-message.bot {
332
+ background: var(--bg-secondary);
333
+ align-self: flex-start;
334
+ }
335
+
336
+ .chat-input-area {
337
+ display: flex;
338
+ padding: 10px;
339
+ gap: 10px;
340
+ border-top: 1px solid var(--border);
341
+ }
342
+
343
+ .chat-input-area input {
344
+ flex: 1;
345
+ padding: 12px;
346
+ border-radius: 8px;
347
+ border: none;
348
+ background: var(--bg-secondary);
349
+ color: var(--text-primary);
350
+ }
351
+
352
+ .chat-input-area button {
353
+ padding: 12px 20px;
354
+ border-radius: 8px;
355
+ border: none;
356
+ background: var(--accent);
357
+ color: white;
358
+ cursor: pointer;
359
+ }
static/index.html CHANGED
@@ -6,253 +6,7 @@
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
  <title>NCAkit - Neural Content Automation</title>
8
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
9
- <style>
10
- :root {
11
- --bg-primary: #0f0f1a;
12
- --bg-secondary: #1a1a2e;
13
- --bg-card: #16213e;
14
- --accent: #6366f1;
15
- --accent-glow: rgba(99, 102, 241, 0.3);
16
- --text-primary: #f8fafc;
17
- --text-secondary: #94a3b8;
18
- --success: #22c55e;
19
- --warning: #f59e0b;
20
- --error: #ef4444;
21
- --border: rgba(255, 255, 255, 0.1);
22
- }
23
-
24
- * {
25
- margin: 0;
26
- padding: 0;
27
- box-sizing: border-box;
28
- }
29
-
30
- body {
31
- font-family: 'Inter', sans-serif;
32
- background: var(--bg-primary);
33
- color: var(--text-primary);
34
- min-height: 100vh;
35
- }
36
-
37
- .container {
38
- max-width: 1200px;
39
- margin: 0 auto;
40
- padding: 2rem;
41
- }
42
-
43
- header {
44
- text-align: center;
45
- padding: 2rem 0 3rem;
46
- }
47
-
48
- header h1 {
49
- font-size: 2.5rem;
50
- background: linear-gradient(135deg, #6366f1, #a855f7, #ec4899);
51
- -webkit-background-clip: text;
52
- background-clip: text;
53
- -webkit-text-fill-color: transparent;
54
- margin-bottom: 0.5rem;
55
- }
56
-
57
- header p {
58
- color: var(--text-secondary);
59
- font-size: 1.1rem;
60
- }
61
-
62
- /* Tabs */
63
- .tabs {
64
- display: flex;
65
- gap: 1rem;
66
- margin-bottom: 2rem;
67
- border-bottom: 1px solid var(--border);
68
- padding-bottom: 1rem;
69
- }
70
-
71
- .tab-btn {
72
- padding: 0.75rem 1.5rem;
73
- background: transparent;
74
- border: none;
75
- color: var(--text-secondary);
76
- font-size: 1rem;
77
- cursor: pointer;
78
- border-radius: 8px;
79
- transition: all 0.2s;
80
- display: flex;
81
- align-items: center;
82
- gap: 0.5rem;
83
- }
84
-
85
- .tab-btn:hover {
86
- background: rgba(255, 255, 255, 0.05);
87
- color: var(--text-primary);
88
- }
89
-
90
- .tab-btn.active {
91
- background: var(--accent);
92
- color: white;
93
- box-shadow: 0 4px 20px var(--accent-glow);
94
- }
95
-
96
- .tab-content {
97
- display: none;
98
- }
99
-
100
- .tab-content.active {
101
- display: block;
102
- }
103
-
104
- /* Cards */
105
- .card {
106
- background: var(--bg-card);
107
- border-radius: 16px;
108
- padding: 2rem;
109
- margin-bottom: 2rem;
110
- border: 1px solid var(--border);
111
- }
112
-
113
- .card h2 {
114
- font-size: 1.5rem;
115
- margin-bottom: 1.5rem;
116
- display: flex;
117
- align-items: center;
118
- gap: 0.5rem;
119
- }
120
-
121
- /* Form */
122
- .form-group {
123
- margin-bottom: 1.25rem;
124
- }
125
-
126
- label {
127
- display: block;
128
- margin-bottom: 0.5rem;
129
- color: var(--text-secondary);
130
- font-size: 0.9rem;
131
- }
132
-
133
- input,
134
- select,
135
- textarea {
136
- width: 100%;
137
- padding: 0.75rem 1rem;
138
- background: var(--bg-secondary);
139
- border: 1px solid var(--border);
140
- border-radius: 8px;
141
- color: var(--text-primary);
142
- font-size: 1rem;
143
- font-family: inherit;
144
- transition: border-color 0.2s;
145
- }
146
-
147
- input:focus,
148
- select:focus,
149
- textarea:focus {
150
- outline: none;
151
- border-color: var(--accent);
152
- }
153
-
154
- textarea {
155
- resize: vertical;
156
- min-height: 100px;
157
- }
158
-
159
- /* Buttons */
160
- .btn {
161
- padding: 0.75rem 1.5rem;
162
- border: none;
163
- border-radius: 8px;
164
- font-size: 1rem;
165
- font-weight: 500;
166
- cursor: pointer;
167
- transition: all 0.2s;
168
- }
169
-
170
- .btn-primary {
171
- background: linear-gradient(135deg, #6366f1, #8b5cf6);
172
- color: white;
173
- box-shadow: 0 4px 15px var(--accent-glow);
174
- }
175
-
176
- .btn-primary:hover {
177
- transform: translateY(-2px);
178
- box-shadow: 0 6px 25px var(--accent-glow);
179
- }
180
-
181
- .btn-secondary {
182
- background: var(--bg-secondary);
183
- color: var(--text-primary);
184
- border: 1px solid var(--border);
185
- }
186
-
187
- /* Grid */
188
- .form-row {
189
- display: grid;
190
- grid-template-columns: 1fr 1fr;
191
- gap: 1rem;
192
- }
193
-
194
- @media (max-width: 768px) {
195
- .form-row {
196
- grid-template-columns: 1fr;
197
- }
198
- }
199
-
200
- /* Status */
201
- .status {
202
- padding: 1rem;
203
- border-radius: 8px;
204
- margin-top: 1rem;
205
- }
206
-
207
- .status.processing {
208
- background: rgba(99, 102, 241, 0.1);
209
- border: 1px solid var(--accent);
210
- }
211
-
212
- .status.success {
213
- background: rgba(34, 197, 94, 0.1);
214
- border: 1px solid var(--success);
215
- }
216
-
217
- .status.error {
218
- background: rgba(239, 68, 68, 0.1);
219
- border: 1px solid var(--error);
220
- }
221
-
222
- .hidden {
223
- display: none;
224
- }
225
-
226
- /* Progress */
227
- .progress-bar {
228
- height: 6px;
229
- background: var(--bg-secondary);
230
- border-radius: 3px;
231
- overflow: hidden;
232
- margin-top: 0.5rem;
233
- }
234
-
235
- .progress-fill {
236
- height: 100%;
237
- background: linear-gradient(90deg, #6366f1, #8b5cf6);
238
- transition: width 0.3s;
239
- }
240
-
241
- /* Character Profile */
242
- .character-section {
243
- background: rgba(139, 92, 246, 0.1);
244
- border: 1px solid rgba(139, 92, 246, 0.3);
245
- border-radius: 12px;
246
- padding: 1.5rem;
247
- margin-bottom: 1.5rem;
248
- }
249
-
250
- .character-section h3 {
251
- color: #a78bfa;
252
- margin-bottom: 1rem;
253
- font-size: 1rem;
254
- }
255
- </style>
256
  </head>
257
 
258
  <body>
@@ -264,27 +18,12 @@
264
 
265
  <!-- Tabs -->
266
  <div class="tabs">
267
- <button class="tab-btn active" data-tab="story">
268
- 🎬 Story Reels
269
- </button>
270
- <button class="tab-btn" data-tab="video">
271
- πŸ“Ή Video Creator
272
- </button>
273
- <button class="tab-btn" data-tab="fact">
274
- 🧠 Fact Image
275
- </button>
276
- <button class="tab-btn" data-tab="trends">
277
- πŸ“Š Trends
278
- </button>
279
- <button class="tab-btn" data-tab="quiz">
280
- 🎯 Quiz Reel
281
- </button>
282
- <button class="tab-btn" data-tab="textstory">
283
- πŸ“± Text Story
284
- </button>
285
- <button class="tab-btn" data-tab="media">
286
- πŸ“ Media Uploader
287
- </button>
288
  </div>
289
 
290
  <!-- Story Reels Tab -->
@@ -299,10 +38,8 @@
299
  <div class="form-group">
300
  <label>Voice Script *</label>
301
  <textarea id="storyScript" rows="4"
302
- placeholder="e.g., Have you ever wondered why some people seem to attract success effortlessly? The secret lies in their mindset. When you believe in yourself, opportunities start flowing toward you..."
303
  required></textarea>
304
- <small style="color: var(--text-secondary);">Enter the exact script you want converted to voice.
305
- This will be spoken directly.</small>
306
  </div>
307
 
308
  <div class="form-row">
@@ -313,8 +50,6 @@
313
  <option value="anime">Anime</option>
314
  <option value="cartoon">Cartoon</option>
315
  <option value="realistic">Realistic</option>
316
- <option value="watercolor">Watercolor</option>
317
- <option value="sticky animation">Sticky Animation</option>
318
  </select>
319
  </div>
320
  <div class="form-group">
@@ -376,7 +111,6 @@
376
 
377
  <button type="button" id="addScene" class="btn btn-secondary" style="margin-bottom: 1rem;">+ Add
378
  Scene</button>
379
-
380
  <button type="submit" class="btn btn-primary">🎬 Create Video</button>
381
  </form>
382
 
@@ -389,7 +123,7 @@
389
  <div class="card">
390
  <h2>🧠 Fact Image Video</h2>
391
  <p style="color: var(--text-secondary); margin-bottom: 1.5rem;">
392
- Generate a clean image β†’ Add fact text overlay β†’ Create short video (4-7s)
393
  </p>
394
 
395
  <form id="factForm">
@@ -420,13 +154,7 @@
420
  <option value="rgba(0, 0, 0, 0.45)">Black (45%)</option>
421
  <option value="rgba(0, 0, 0, 0.6)">Black (60%)</option>
422
  <option value="rgba(0, 31, 63, 0.75)">Navy Blue</option>
423
- <option value="rgba(1, 50, 32, 0.75)">Forest Green</option>
424
- <option value="rgba(62, 39, 35, 0.75)">Dark Brown</option>
425
  <option value="rgba(48, 25, 52, 0.75)">Deep Purple</option>
426
- <option value="rgba(54, 69, 79, 0.75)">Charcoal Grey</option>
427
- <option value="rgba(0, 77, 77, 0.75)">Dark Teal</option>
428
- <option value="rgba(128, 0, 0, 0.75)">Maroon</option>
429
- <option value="rgba(0, 35, 102, 0.75)">Royal Blue</option>
430
  </select>
431
  </div>
432
  <div class="form-group">
@@ -442,17 +170,13 @@
442
  <div class="form-group">
443
  <label>Image Prompt *</label>
444
  <textarea id="factImagePrompt" rows="2"
445
- placeholder="e.g., calm forest with soft sunlight filtering through trees, peaceful nature scene"
446
  required></textarea>
447
- <small style="color: var(--text-secondary);">Describe the background image. It will be generated
448
- without text.</small>
449
  </div>
450
 
451
  <div class="form-group">
452
  <label>Fact Heading (Optional)</label>
453
  <input type="text" id="factHeading" placeholder="e.g., Psychological Hack" maxlength="50">
454
- <small style="color: var(--text-secondary);">Bold heading with background - appears above fact
455
- text.</small>
456
  </div>
457
 
458
  <div class="form-group">
@@ -460,8 +184,6 @@
460
  <textarea id="factText" rows="3"
461
  placeholder="e.g., People who daydream more tend to have higher creative intelligence."
462
  required></textarea>
463
- <small style="color: var(--text-secondary);">The psychology/motivational fact to overlay on the
464
- image.</small>
465
  </div>
466
 
467
  <button type="submit" class="btn btn-primary">🎬 Generate Fact Video</button>
@@ -480,7 +202,6 @@
480
  Analyze Google Trends data for content planning
481
  </p>
482
 
483
- <!-- Trending Now Section -->
484
  <div style="margin-bottom: 2rem;">
485
  <h3 style="margin-bottom: 1rem;">πŸ”₯ Trending Now</h3>
486
  <form id="trendingForm">
@@ -492,10 +213,6 @@
492
  <option value="bangladesh">Bangladesh</option>
493
  <option value="india">India</option>
494
  <option value="united_kingdom">United Kingdom</option>
495
- <option value="japan">Japan</option>
496
- <option value="germany">Germany</option>
497
- <option value="brazil">Brazil</option>
498
- <option value="canada">Canada</option>
499
  </select>
500
  </div>
501
  <div class="form-group">
@@ -504,18 +221,16 @@
504
  <option value="10">10</option>
505
  <option value="20" selected>20</option>
506
  <option value="30">30</option>
507
- <option value="50">50</option>
508
  </select>
509
  </div>
510
  </div>
511
- <button type="submit" class="submit-btn">Get Trending</button>
512
  </form>
513
  <div id="trendingResults" style="margin-top: 1rem;"></div>
514
  </div>
515
 
516
  <hr style="border-color: var(--border); margin: 2rem 0;">
517
 
518
- <!-- Keyword Research Section -->
519
  <div>
520
  <h3 style="margin-bottom: 1rem;">πŸ” Keyword Research</h3>
521
  <form id="keywordForm">
@@ -531,7 +246,6 @@
531
  <option value="US">United States</option>
532
  <option value="BD">Bangladesh</option>
533
  <option value="IN">India</option>
534
- <option value="GB">United Kingdom</option>
535
  </select>
536
  </div>
537
  <div class="form-group">
@@ -539,8 +253,6 @@
539
  <select id="researchTimeframe">
540
  <option value="now_1d">Last 24 hours</option>
541
  <option value="now_7d">Last 7 days</option>
542
- <option value="today_1m">Last month</option>
543
- <option value="today_3m">Last 3 months</option>
544
  <option value="today_12m" selected>Last 12 months</option>
545
  </select>
546
  </div>
@@ -553,10 +265,6 @@
553
  <option value="computers_electronics">Computers & Electronics</option>
554
  <option value="games">Games</option>
555
  <option value="arts_entertainment">Arts & Entertainment</option>
556
- <option value="science">Science</option>
557
- <option value="news">News</option>
558
- <option value="sports">Sports</option>
559
- <option value="business_industrial">Business</option>
560
  </select>
561
  </div>
562
  <div class="form-group">
@@ -565,16 +273,13 @@
565
  <option value="web">Web Search</option>
566
  <option value="youtube">YouTube Search</option>
567
  <option value="news">News Search</option>
568
- <option value="images">Image Search</option>
569
  </select>
570
  </div>
571
  </div>
572
- <button type="submit" class="submit-btn">Analyze Keyword</button>
573
  </form>
574
  <div id="keywordResults" style="margin-top: 1rem;"></div>
575
  </div>
576
-
577
- <div id="trendsStatus" class="status hidden"></div>
578
  </div>
579
  </div>
580
 
@@ -588,7 +293,6 @@
588
 
589
  <form id="quizForm">
590
  <div id="quizContainer">
591
- <!-- Quiz 1 -->
592
  <div class="quiz-item"
593
  style="background: var(--bg-secondary); padding: 1.5rem; border-radius: 12px; margin-bottom: 1rem;">
594
  <h4 style="color: var(--accent); margin-bottom: 1rem;">Quiz 1</h4>
@@ -627,19 +331,16 @@
627
  </div>
628
  <div class="form-group">
629
  <label>Explain (shown after answer)</label>
630
- <input type="text" class="quiz-explain" placeholder="Brief explanation of the answer">
631
  </div>
632
  </div>
633
  </div>
634
 
635
  <div style="display: flex; gap: 1rem; margin-bottom: 1.5rem;">
636
- <button type="button" id="addQuizBtn" class="btn btn-secondary" style="flex: 1;">
637
- βž• Add Another Quiz
638
- </button>
639
  <button type="button" id="removeQuizBtn" class="btn btn-secondary"
640
- style="flex: 1; background: #dc2626;">
641
- βž– Remove Last Quiz
642
- </button>
643
  </div>
644
 
645
  <button type="submit" class="btn btn-primary" style="width: 100%;">🎯 Generate Quiz Video</button>
@@ -657,7 +358,6 @@
657
  Create viral iMessage-style fake conversation videos with AI voice
658
  </p>
659
 
660
- <!-- Mode Toggle -->
661
  <div class="form-group" style="margin-bottom: 1.5rem;">
662
  <label>Mode</label>
663
  <div style="display: flex; gap: 1rem;">
@@ -673,18 +373,13 @@
673
  </div>
674
 
675
  <form id="textStoryForm">
676
- <!-- AI Mode Section (Hidden by default) -->
677
  <div id="tsAiSection" style="display: none;">
678
  <div class="form-group"
679
  style="background: linear-gradient(135deg, rgba(99,102,241,0.1), rgba(168,85,247,0.1)); padding: 1.5rem; border-radius: 12px; margin-bottom: 1rem;">
680
  <label>πŸ€– AI Prompt - Describe the conversation</label>
681
  <textarea id="tsAiPrompt" rows="3"
682
- placeholder="e.g., A breakup conversation where the ex wants to get back together but gets rejected. Emotional and dramatic. End with a plot twist."></textarea>
683
- <small style="color: var(--text-secondary); display: block; margin-top: 0.5rem;">
684
- AI will generate a realistic conversation based on your prompt
685
- </small>
686
  </div>
687
-
688
  <div class="form-row">
689
  <div class="form-group">
690
  <label>Number of Messages</label>
@@ -702,13 +397,11 @@
702
  <option value="funny">Funny / Comedy</option>
703
  <option value="shocking">Shocking / Twist</option>
704
  <option value="romantic">Romantic</option>
705
- <option value="angry">Angry / Fight</option>
706
  </select>
707
  </div>
708
  </div>
709
  </div>
710
 
711
- <!-- Common Fields -->
712
  <div class="form-row">
713
  <div class="form-group">
714
  <label>Person A Name (You - Right/Blue)</label>
@@ -725,7 +418,6 @@
725
  <input type="text" id="tsAvatar" maxlength="2" placeholder="M" style="width: 80px;">
726
  </div>
727
 
728
- <!-- Manual Mode Section -->
729
  <div id="tsManualSection">
730
  <div id="tsMessagesContainer">
731
  <div class="ts-message-item"
@@ -787,894 +479,6 @@
787
  </div>
788
  </div>
789
 
790
- <!-- Media Uploader Tab -->
791
- <div id="media-tab" class="tab-content">
792
- <div class="card">
793
- <h2>πŸ“ YouTube to HF Uploader</h2>
794
- <p style="color: var(--text-secondary); margin-bottom: 1.5rem;">
795
- Download YouTube videos/playlists and upload directly to HuggingFace Dataset
796
- </p>
797
-
798
- <form id="mediaUploaderForm">
799
- <!-- URL Input -->
800
- <div class="form-group">
801
- <label>YouTube URL (Video or Playlist) *</label>
802
- <input type="text" id="mediaUrl"
803
- placeholder="https://youtube.com/watch?v=... or https://youtube.com/playlist?list=..." required>
804
- </div>
805
-
806
- <!-- Folder Selection -->
807
- <div class="form-row">
808
- <div class="form-group">
809
- <label>Target Folder *</label>
810
- <select id="mediaFolder" required>
811
- <option value="">Loading folders...</option>
812
- </select>
813
- </div>
814
- <div class="form-group" style="flex: 0 0 auto;">
815
- <label>&nbsp;</label>
816
- <button type="button" id="createFolderBtn" class="btn btn-secondary">
817
- βž• New Folder
818
- </button>
819
- </div>
820
- </div>
821
-
822
- <!-- Format Toggle -->
823
- <div class="form-group">
824
- <label>Download Format *</label>
825
- <div style="display: flex; gap: 1rem;">
826
- <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
827
- <input type="radio" name="mediaFormat" value="mp4" checked>
828
- 🎬 Video (MP4)
829
- </label>
830
- <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
831
- <input type="radio" name="mediaFormat" value="mp3">
832
- 🎡 Audio (MP3)
833
- </label>
834
- </div>
835
- </div>
836
-
837
- <!-- Category (for Audio) -->
838
- <div id="audioCategorySection" class="form-group" style="display: none;">
839
- <label>Music Category / Name</label>
840
- <input type="text" id="audioCategory" placeholder="e.g., emotional, energetic, lofi, sad, happy"
841
- style="max-width: 300px;">
842
- <small style="color: var(--text-secondary); display: block; margin-top: 0.5rem;">
843
- Files will be saved as: {category}_001.mp3, {category}_002.mp3, etc.
844
- </small>
845
- </div>
846
-
847
- <button type="submit" class="btn btn-primary" style="width: 100%;">
848
- πŸ“€ Download & Upload to HF
849
- </button>
850
- </form>
851
-
852
- <div id="mediaUploaderStatus" class="status hidden"></div>
853
-
854
- <!-- Results -->
855
- <div id="mediaResults" style="display: none; margin-top: 1.5rem;">
856
- <h3>βœ… Uploaded Files</h3>
857
- <div id="mediaResultsList" style="max-height: 300px; overflow-y: auto;"></div>
858
- </div>
859
- </div>
860
- </div>
861
-
862
- <script>
863
- // Tab switching
864
- document.querySelectorAll('.tab-btn').forEach(btn => {
865
- btn.addEventListener('click', () => {
866
- document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
867
- document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
868
- btn.classList.add('active');
869
- document.getElementById(btn.dataset.tab + '-tab').classList.add('active');
870
- });
871
- });
872
-
873
- // Story Form
874
- document.getElementById('storyForm').addEventListener('submit', async (e) => {
875
- e.preventDefault();
876
- const status = document.getElementById('storyStatus');
877
- status.className = 'status processing';
878
- status.innerHTML = '⏳ Starting generation...';
879
- status.classList.remove('hidden');
880
-
881
- const data = {
882
- script: document.getElementById('storyScript').value,
883
- image_style: document.getElementById('storyStyle').value,
884
- voice: document.getElementById('storyVoice').value
885
- };
886
-
887
- try {
888
- const res = await fetch('/api/story/story-reel', {
889
- method: 'POST',
890
- headers: { 'Content-Type': 'application/json' },
891
- body: JSON.stringify(data)
892
- });
893
- const result = await res.json();
894
-
895
- if (result.job_id) {
896
- status.innerHTML = `βœ… Job started! ID: ${result.job_id}<br>Checking status...`;
897
- pollStatus(result.job_id, 'story');
898
- }
899
- } catch (err) {
900
- status.className = 'status error';
901
- status.innerHTML = '❌ Error: ' + err.message;
902
- }
903
- });
904
-
905
- // Poll status
906
- async function pollStatus(jobId, type) {
907
- const status = document.getElementById(type + 'Status');
908
- const endpoint = type === 'story' ? '/api/story/story-reel/' : '/api/video/short-video/';
909
-
910
- const check = async () => {
911
- try {
912
- const res = await fetch(endpoint + jobId + '/status');
913
- const data = await res.json();
914
-
915
- const progress = data.progress || 0;
916
- status.innerHTML = `
917
- <div>Status: <strong>${data.status}</strong></div>
918
- <div class="progress-bar"><div class="progress-fill" style="width: ${progress}%"></div></div>
919
- `;
920
-
921
- if (data.status === 'ready') {
922
- status.className = 'status success';
923
- const downloadUrl = type === 'story' ? `/api/story/story-reel/${jobId}` : `/api/video/short-video/${jobId}`;
924
- status.innerHTML += `<br><a href="${downloadUrl}" class="btn btn-primary" style="margin-top: 1rem; display: inline-block;">πŸ“₯ Download Video</a>`;
925
- } else if (data.status === 'failed') {
926
- status.className = 'status error';
927
- status.innerHTML = '❌ Failed: ' + (data.error || 'Unknown error');
928
- } else {
929
- setTimeout(check, 2000);
930
- }
931
- } catch (err) {
932
- setTimeout(check, 3000);
933
- }
934
- };
935
- check();
936
- }
937
-
938
- // Add scene
939
- document.getElementById('addScene').addEventListener('click', () => {
940
- const container = document.getElementById('scenesContainer');
941
- const count = container.querySelectorAll('.scene-text').length + 1;
942
- container.innerHTML += `
943
- <div class="form-group">
944
- <label>Scene ${count} - Text</label>
945
- <textarea class="scene-text" rows="2" placeholder="Enter narration..." required></textarea>
946
- </div>
947
- <div class="form-group">
948
- <label>Keywords</label>
949
- <input type="text" class="scene-keywords" placeholder="nature, forest">
950
- </div>
951
- `;
952
- });
953
-
954
- // Toggle heading style row when heading is entered
955
-
956
- // Fact Image Form
957
- document.getElementById('factForm').addEventListener('submit', async (e) => {
958
- e.preventDefault();
959
- const status = document.getElementById('factStatus');
960
- status.className = 'status processing';
961
- status.innerHTML = '⏳ Generating fact video...';
962
- status.classList.remove('hidden');
963
-
964
- const heading = document.getElementById('factHeading').value.trim();
965
-
966
- const data = {
967
- model: document.getElementById('factModel').value,
968
- image_prompt: document.getElementById('factImagePrompt').value,
969
- fact_text: document.getElementById('factText').value,
970
- duration: parseInt(document.getElementById('factDuration').value)
971
- };
972
-
973
- // Add heading if provided
974
- if (heading) {
975
- data.fact_heading = heading;
976
- data.heading_background = {
977
- enabled: true,
978
- color: document.getElementById('headingBgColor').value,
979
- padding: 22,
980
- corner_radius: parseInt(document.getElementById('headingBgRadius').value)
981
- };
982
- }
983
-
984
- try {
985
- const res = await fetch('/api/fact-image/', {
986
- method: 'POST',
987
- headers: { 'Content-Type': 'application/json' },
988
- body: JSON.stringify(data)
989
- });
990
- const result = await res.json();
991
-
992
- if (result.job_id) {
993
- status.innerHTML = `βœ… Job started! ID: ${result.job_id}<br>Checking status...`;
994
- pollFactStatus(result.job_id);
995
- }
996
- } catch (err) {
997
- status.className = 'status error';
998
- status.innerHTML = '❌ Error: ' + err.message;
999
- }
1000
- });
1001
-
1002
- // Poll Fact Image status
1003
- async function pollFactStatus(jobId) {
1004
- const status = document.getElementById('factStatus');
1005
-
1006
- const check = async () => {
1007
- try {
1008
- const res = await fetch(`/api/fact-image/${jobId}/status`);
1009
- const data = await res.json();
1010
-
1011
- const progress = data.progress || 0;
1012
- status.innerHTML = `
1013
- <div>Status: <strong>${data.status}</strong></div>
1014
- <div class="progress-bar"><div class="progress-fill" style="width: ${progress}%"></div></div>
1015
- `;
1016
-
1017
- if (data.status === 'ready') {
1018
- status.className = 'status success';
1019
- status.innerHTML += `<br><a href="/api/fact-image/${jobId}" class="btn btn-primary" style="margin-top: 1rem; display: inline-block;">πŸ“₯ Download Video</a>`;
1020
- } else if (data.status === 'failed') {
1021
- status.className = 'status error';
1022
- status.innerHTML = '❌ Failed: ' + (data.error || 'Unknown error');
1023
- } else {
1024
- setTimeout(check, 2000);
1025
- }
1026
- } catch (err) {
1027
- setTimeout(check, 3000);
1028
- }
1029
- };
1030
- check();
1031
- }
1032
-
1033
- // Video Form
1034
- document.getElementById('videoForm').addEventListener('submit', async (e) => {
1035
- e.preventDefault();
1036
- const status = document.getElementById('videoStatus');
1037
- status.className = 'status processing';
1038
- status.innerHTML = '⏳ Creating video...';
1039
- status.classList.remove('hidden');
1040
-
1041
- const scenes = [];
1042
- document.querySelectorAll('.scene-text').forEach((textarea, i) => {
1043
- const keywords = document.querySelectorAll('.scene-keywords')[i]?.value || '';
1044
- scenes.push({
1045
- text: textarea.value,
1046
- searchTerms: keywords.split(',').map(k => k.trim()).filter(k => k)
1047
- });
1048
- });
1049
-
1050
- const data = {
1051
- scenes: scenes,
1052
- config: {
1053
- voice: document.getElementById('videoVoice').value,
1054
- music: document.getElementById('videoMusic').value || null
1055
- }
1056
- };
1057
-
1058
- try {
1059
- const res = await fetch('/api/video/short-video', {
1060
- method: 'POST',
1061
- headers: { 'Content-Type': 'application/json' },
1062
- body: JSON.stringify(data)
1063
- });
1064
- const result = await res.json();
1065
-
1066
- if (result.videoId) {
1067
- status.innerHTML = `βœ… Video started! ID: ${result.videoId}`;
1068
- pollStatus(result.videoId, 'video');
1069
- }
1070
- } catch (err) {
1071
- status.className = 'status error';
1072
- status.innerHTML = '❌ Error: ' + err.message;
1073
- }
1074
- });
1075
-
1076
- // ==========================================
1077
- // TRENDS MODULE
1078
- // ==========================================
1079
-
1080
- // Trending Now Form
1081
- document.getElementById('trendingForm').addEventListener('submit', async (e) => {
1082
- e.preventDefault();
1083
- const results = document.getElementById('trendingResults');
1084
- results.innerHTML = '<p>⏳ Loading trends...</p>';
1085
-
1086
- const data = {
1087
- country: document.getElementById('trendCountry').value,
1088
- limit: parseInt(document.getElementById('trendLimit').value)
1089
- };
1090
-
1091
- try {
1092
- const res = await fetch('/api/trends/trending-now', {
1093
- method: 'POST',
1094
- headers: { 'Content-Type': 'application/json' },
1095
- body: JSON.stringify(data)
1096
- });
1097
- const result = await res.json();
1098
-
1099
- if (result.success) {
1100
- let html = '<div style="background: var(--bg-secondary); border-radius: 8px; padding: 1rem; max-height: 400px; overflow-y: auto;">';
1101
- result.trends.forEach(t => {
1102
- html += `<div style="padding: 0.5rem 0; border-bottom: 1px solid var(--border);">
1103
- <span style="color: var(--accent); font-weight: bold;">#${t.rank}</span> ${t.topic}
1104
- </div>`;
1105
- });
1106
- html += '</div>';
1107
- results.innerHTML = html;
1108
- } else {
1109
- results.innerHTML = '<p style="color: var(--error);">❌ Error: ' + (result.detail || 'Failed') + '</p>';
1110
- }
1111
- } catch (err) {
1112
- results.innerHTML = '<p style="color: var(--error);">❌ Error: ' + err.message + '</p>';
1113
- }
1114
- });
1115
-
1116
- // Keyword Research Form
1117
- document.getElementById('keywordForm').addEventListener('submit', async (e) => {
1118
- e.preventDefault();
1119
- const results = document.getElementById('keywordResults');
1120
- results.innerHTML = '<p>⏳ Analyzing keyword...</p>';
1121
-
1122
- const data = {
1123
- keyword: document.getElementById('researchKeyword').value,
1124
- region: document.getElementById('researchRegion').value,
1125
- timeframe: document.getElementById('researchTimeframe').value,
1126
- category: document.getElementById('researchCategory').value,
1127
- search_type: document.getElementById('researchType').value
1128
- };
1129
-
1130
- try {
1131
- const res = await fetch('/api/trends/keyword-research', {
1132
- method: 'POST',
1133
- headers: { 'Content-Type': 'application/json' },
1134
- body: JSON.stringify(data)
1135
- });
1136
- const result = await res.json();
1137
-
1138
- if (result.success) {
1139
- let html = '<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;">';
1140
-
1141
- // Related Topics
1142
- html += '<div style="background: var(--bg-secondary); border-radius: 8px; padding: 1rem;">';
1143
- html += '<h4 style="margin-bottom: 0.5rem;">πŸ“Œ Related Topics</h4>';
1144
- if (result.related_topics.top && result.related_topics.top.length > 0) {
1145
- result.related_topics.top.slice(0, 10).forEach(t => {
1146
- html += `<div style="padding: 0.25rem 0; font-size: 0.9rem;">${t.topic} <span style="color: var(--text-secondary);">(${t.value})</span></div>`;
1147
- });
1148
- } else {
1149
- html += '<p style="color: var(--text-secondary); font-size: 0.9rem;">No data</p>';
1150
- }
1151
- html += '</div>';
1152
-
1153
- // Related Queries
1154
- html += '<div style="background: var(--bg-secondary); border-radius: 8px; padding: 1rem;">';
1155
- html += '<h4 style="margin-bottom: 0.5rem;">πŸ” Related Queries</h4>';
1156
- if (result.related_queries.top && result.related_queries.top.length > 0) {
1157
- result.related_queries.top.slice(0, 10).forEach(q => {
1158
- html += `<div style="padding: 0.25rem 0; font-size: 0.9rem;">${q.query} <span style="color: var(--text-secondary);">(${q.value})</span></div>`;
1159
- });
1160
- } else {
1161
- html += '<p style="color: var(--text-secondary); font-size: 0.9rem;">No data</p>';
1162
- }
1163
- html += '</div>';
1164
-
1165
- html += '</div>';
1166
- results.innerHTML = html;
1167
- } else {
1168
- results.innerHTML = '<p style="color: var(--error);">❌ Error: ' + (result.detail || 'Failed') + '</p>';
1169
- }
1170
- } catch (err) {
1171
- results.innerHTML = '<p style="color: var(--error);">❌ Error: ' + err.message + '</p>';
1172
- }
1173
- });
1174
-
1175
- // ==========================================
1176
- // QUIZ REEL MODULE
1177
- // ==========================================
1178
-
1179
- let quizCount = 1;
1180
-
1181
- // Add Quiz button
1182
- document.getElementById('addQuizBtn').addEventListener('click', () => {
1183
- quizCount++;
1184
- const container = document.getElementById('quizContainer');
1185
- const quizHtml = `
1186
- <div class="quiz-item" style="background: var(--bg-secondary); padding: 1.5rem; border-radius: 12px; margin-bottom: 1rem;">
1187
- <h4 style="color: var(--accent); margin-bottom: 1rem;">Quiz ${quizCount}</h4>
1188
- <div class="form-group">
1189
- <label>Hook (Category)</label>
1190
- <input type="text" class="quiz-hook" placeholder="e.g., IQ TEST, MATH QUIZ">
1191
- </div>
1192
- <div class="form-group">
1193
- <label>Question *</label>
1194
- <input type="text" class="quiz-question" placeholder="Enter your question" required>
1195
- </div>
1196
- <div class="form-row">
1197
- <div class="form-group">
1198
- <label>Option A *</label>
1199
- <input type="text" class="quiz-option-a" placeholder="Answer A" required>
1200
- </div>
1201
- <div class="form-group">
1202
- <label>Option B *</label>
1203
- <input type="text" class="quiz-option-b" placeholder="Answer B" required>
1204
- </div>
1205
- <div class="form-group">
1206
- <label>Option C *</label>
1207
- <input type="text" class="quiz-option-c" placeholder="Answer C" required>
1208
- </div>
1209
- </div>
1210
- <div class="form-row">
1211
- <div class="form-group">
1212
- <label>Correct Answer *</label>
1213
- <select class="quiz-correct" required>
1214
- <option value="A">A</option>
1215
- <option value="B">B</option>
1216
- <option value="C">C</option>
1217
- </select>
1218
- </div>
1219
- </div>
1220
- <div class="form-group">
1221
- <label>Explain (shown after answer)</label>
1222
- <input type="text" class="quiz-explain" placeholder="Brief explanation">
1223
- </div>
1224
- </div>
1225
- `;
1226
- container.insertAdjacentHTML('beforeend', quizHtml);
1227
- });
1228
-
1229
- // Remove Quiz button
1230
- document.getElementById('removeQuizBtn').addEventListener('click', () => {
1231
- const container = document.getElementById('quizContainer');
1232
- const items = container.querySelectorAll('.quiz-item');
1233
- if (items.length > 1) {
1234
- items[items.length - 1].remove();
1235
- quizCount--;
1236
- }
1237
- });
1238
-
1239
- // Quiz status polling
1240
- async function pollQuizStatus(jobId, statusDiv) {
1241
- const checkStatus = async () => {
1242
- try {
1243
- const res = await fetch(`/api/quiz/${jobId}/status`);
1244
- const status = await res.json();
1245
-
1246
- if (status.status === 'ready') {
1247
- statusDiv.className = 'status success';
1248
- statusDiv.innerHTML = `βœ… Video Ready! <a href="${status.video_url}" target="_blank" style="color: var(--accent);">Download Video</a>`;
1249
- return;
1250
- } else if (status.status === 'failed') {
1251
- statusDiv.className = 'status error';
1252
- statusDiv.innerHTML = `❌ Failed: ${status.error}`;
1253
- return;
1254
- } else {
1255
- statusDiv.innerHTML = `⏳ ${status.status}... ${status.progress}%`;
1256
- setTimeout(checkStatus, 2000);
1257
- }
1258
- } catch (err) {
1259
- statusDiv.className = 'status error';
1260
- statusDiv.innerHTML = `❌ Error: ${err.message}`;
1261
- }
1262
- };
1263
- checkStatus();
1264
- }
1265
-
1266
- // Quiz Form submission
1267
- document.getElementById('quizForm').addEventListener('submit', async (e) => {
1268
- e.preventDefault();
1269
- const status = document.getElementById('quizStatus');
1270
- status.className = 'status';
1271
- status.classList.remove('hidden');
1272
- status.innerHTML = '⏳ Starting quiz video generation...';
1273
-
1274
- // Collect all quizzes
1275
- const quizItems = document.querySelectorAll('.quiz-item');
1276
- const quizzes = [];
1277
-
1278
- quizItems.forEach(item => {
1279
- quizzes.push({
1280
- hook: item.querySelector('.quiz-hook').value || '',
1281
- question: item.querySelector('.quiz-question').value,
1282
- options: {
1283
- A: item.querySelector('.quiz-option-a').value,
1284
- B: item.querySelector('.quiz-option-b').value,
1285
- C: item.querySelector('.quiz-option-c').value
1286
- },
1287
- correct: item.querySelector('.quiz-correct').value,
1288
- explain: item.querySelector('.quiz-explain').value || ''
1289
- });
1290
- });
1291
-
1292
- const data = {
1293
- quizzes: quizzes,
1294
- voice: 'af_heart'
1295
- };
1296
-
1297
- try {
1298
- const res = await fetch('/api/quiz/generate', {
1299
- method: 'POST',
1300
- headers: { 'Content-Type': 'application/json' },
1301
- body: JSON.stringify(data)
1302
- });
1303
- const result = await res.json();
1304
-
1305
- if (result.job_id) {
1306
- status.innerHTML = `⏳ Job started: ${result.job_id} (${quizzes.length} quizzes)`;
1307
- pollQuizStatus(result.job_id, status);
1308
- } else {
1309
- status.className = 'status error';
1310
- status.innerHTML = `❌ Error: ${result.detail || 'Failed to start'}`;
1311
- }
1312
- } catch (err) {
1313
- status.className = 'status error';
1314
- status.innerHTML = '❌ Error: ' + err.message;
1315
- }
1316
- });
1317
-
1318
- // ==========================================
1319
- // GEMINI CHATBOT TEST
1320
- // ==========================================
1321
- // Wait for DOM elements to be available
1322
- setTimeout(() => {
1323
- const chatBtn = document.getElementById('chatBtn');
1324
- const chatModal = document.getElementById('chatModal');
1325
- const chatClose = document.getElementById('chatClose');
1326
- const chatForm = document.getElementById('chatForm');
1327
- const chatInput = document.getElementById('chatInput');
1328
- const chatMessages = document.getElementById('chatMessages');
1329
-
1330
- if (!chatBtn) return;
1331
-
1332
- chatBtn.addEventListener('click', () => {
1333
- chatModal.classList.toggle('hidden');
1334
- });
1335
-
1336
- chatClose.addEventListener('click', () => {
1337
- chatModal.classList.add('hidden');
1338
- });
1339
-
1340
- chatForm.addEventListener('submit', async (e) => {
1341
- e.preventDefault();
1342
- const message = chatInput.value.trim();
1343
- if (!message) return;
1344
-
1345
- // Add user message
1346
- addMessage(message, 'user');
1347
- chatInput.value = '';
1348
-
1349
- // Add loading
1350
- const loadingId = addMessage('⏳ Thinking...', 'bot');
1351
-
1352
- try {
1353
- const res = await fetch('/api/chat', {
1354
- method: 'POST',
1355
- headers: { 'Content-Type': 'application/json' },
1356
- body: JSON.stringify({ message })
1357
- });
1358
- const data = await res.json();
1359
-
1360
- // Remove loading
1361
- document.getElementById(loadingId).remove();
1362
-
1363
- if (data.reply) {
1364
- addMessage(data.reply, 'bot');
1365
- } else if (data.error) {
1366
- addMessage('❌ ' + data.error, 'bot');
1367
- }
1368
- } catch (err) {
1369
- document.getElementById(loadingId).remove();
1370
- addMessage('❌ Error: ' + err.message, 'bot');
1371
- }
1372
- });
1373
-
1374
- function addMessage(text, type) {
1375
- const id = 'msg-' + Date.now();
1376
- const div = document.createElement('div');
1377
- div.id = id;
1378
- div.className = 'chat-message ' + type;
1379
- div.textContent = text;
1380
- chatMessages.appendChild(div);
1381
- chatMessages.scrollTop = chatMessages.scrollHeight;
1382
- return id;
1383
- }
1384
- }, 100); // End of setTimeout
1385
-
1386
- // ==========================================
1387
- // TEXT STORY MODULE
1388
- // ==========================================
1389
-
1390
- // Add message row
1391
- document.getElementById('tsAddMessage').addEventListener('click', () => {
1392
- const container = document.getElementById('tsMessagesContainer');
1393
- const count = container.querySelectorAll('.ts-message-item').length + 1;
1394
- const html = `
1395
- <div class="ts-message-item" style="background: var(--bg-secondary); padding: 1rem; border-radius: 8px; margin-bottom: 0.5rem;">
1396
- <div class="form-row" style="align-items: flex-end;">
1397
- <div class="form-group" style="flex: 0 0 100px;">
1398
- <label>Sender</label>
1399
- <select class="ts-sender">
1400
- <option value="B">B (Other)</option>
1401
- <option value="A">A (You)</option>
1402
- </select>
1403
- </div>
1404
- <div class="form-group" style="flex: 1;">
1405
- <label>Message ${count}</label>
1406
- <input type="text" class="ts-text" placeholder="Type message..." required>
1407
- </div>
1408
- <button type="button" class="btn btn-secondary ts-remove" style="height: 42px;">βœ•</button>
1409
- </div>
1410
- </div>
1411
- `;
1412
- container.insertAdjacentHTML('beforeend', html);
1413
- });
1414
-
1415
- // Remove message row
1416
- document.getElementById('tsMessagesContainer').addEventListener('click', (e) => {
1417
- if (e.target.classList.contains('ts-remove')) {
1418
- const items = document.querySelectorAll('.ts-message-item');
1419
- if (items.length > 1) {
1420
- e.target.closest('.ts-message-item').remove();
1421
- }
1422
- }
1423
- });
1424
-
1425
- // Toggle Manual/AI mode
1426
- function toggleTsMode() {
1427
- const mode = document.querySelector('input[name="tsMode"]:checked').value;
1428
- document.getElementById('tsAiSection').style.display = mode === 'ai' ? 'block' : 'none';
1429
- document.getElementById('tsManualSection').style.display = mode === 'manual' ? 'block' : 'none';
1430
- }
1431
-
1432
- // Form submit
1433
- document.getElementById('textStoryForm').addEventListener('submit', async (e) => {
1434
- e.preventDefault();
1435
- const status = document.getElementById('textStoryStatus');
1436
- status.className = 'status processing';
1437
- status.classList.remove('hidden');
1438
-
1439
- const mode = document.querySelector('input[name="tsMode"]:checked').value;
1440
- let messages = [];
1441
-
1442
- if (mode === 'ai') {
1443
- // AI Mode - Generate conversation first
1444
- const prompt = document.getElementById('tsAiPrompt').value.trim();
1445
- if (!prompt) {
1446
- status.className = 'status error';
1447
- status.innerHTML = '❌ Please enter a prompt for AI!';
1448
- return;
1449
- }
1450
-
1451
- status.innerHTML = 'πŸ€– AI generating conversation...';
1452
-
1453
- try {
1454
- const aiRes = await fetch('/api/text-story/ai-generate', {
1455
- method: 'POST',
1456
- headers: { 'Content-Type': 'application/json' },
1457
- body: JSON.stringify({
1458
- prompt: prompt,
1459
- person_a_name: document.getElementById('tsPersonA').value || 'You',
1460
- person_b_name: document.getElementById('tsPersonB').value || 'My Ex',
1461
- message_count: parseInt(document.getElementById('tsAiMsgCount').value),
1462
- tone: document.getElementById('tsAiTone').value
1463
- })
1464
- });
1465
- const aiData = await aiRes.json();
1466
-
1467
- if (aiData.messages) {
1468
- messages = aiData.messages;
1469
- status.innerHTML = `πŸ€– Generated ${messages.length} messages. Now creating video...`;
1470
- } else {
1471
- status.className = 'status error';
1472
- status.innerHTML = '❌ AI failed: ' + (aiData.detail || 'Unknown error');
1473
- return;
1474
- }
1475
- } catch (err) {
1476
- status.className = 'status error';
1477
- status.innerHTML = '❌ AI Error: ' + err.message;
1478
- return;
1479
- }
1480
- } else {
1481
- // Manual Mode - Collect messages from form
1482
- const messageItems = document.querySelectorAll('.ts-message-item');
1483
- messageItems.forEach(item => {
1484
- const sender = item.querySelector('.ts-sender').value;
1485
- const text = item.querySelector('.ts-text').value.trim();
1486
- if (text) {
1487
- messages.push({ sender, text });
1488
- }
1489
- });
1490
-
1491
- if (messages.length < 2) {
1492
- status.className = 'status error';
1493
- status.innerHTML = '❌ Need at least 2 messages!';
1494
- return;
1495
- }
1496
- }
1497
-
1498
- status.innerHTML = '⏳ Starting video generation...';
1499
-
1500
- const data = {
1501
- person_a_name: document.getElementById('tsPersonA').value || 'You',
1502
- person_b_name: document.getElementById('tsPersonB').value || 'My Ex',
1503
- person_b_avatar: document.getElementById('tsAvatar').value || null,
1504
- messages: messages,
1505
- ending_text: document.getElementById('tsEnding').value || null,
1506
- voice_a: document.getElementById('tsVoiceA').value,
1507
- voice_b: document.getElementById('tsVoiceB').value
1508
- };
1509
-
1510
- try {
1511
- const res = await fetch('/api/text-story/generate', {
1512
- method: 'POST',
1513
- headers: { 'Content-Type': 'application/json' },
1514
- body: JSON.stringify(data)
1515
- });
1516
- const result = await res.json();
1517
-
1518
- if (result.job_id) {
1519
- status.innerHTML = `⏳ Job started: ${result.job_id}`;
1520
- pollTextStoryStatus(result.job_id);
1521
- } else {
1522
- status.className = 'status error';
1523
- status.innerHTML = `❌ Error: ${result.detail || 'Failed to start'}`;
1524
- }
1525
- } catch (err) {
1526
- status.className = 'status error';
1527
- status.innerHTML = '❌ Error: ' + err.message;
1528
- }
1529
- });
1530
-
1531
- // Poll status
1532
- async function pollTextStoryStatus(jobId) {
1533
- const status = document.getElementById('textStoryStatus');
1534
- const poll = async () => {
1535
- try {
1536
- const res = await fetch(`/api/text-story/${jobId}/status`);
1537
- const data = await res.json();
1538
-
1539
- if (data.status === 'ready') {
1540
- status.className = 'status success';
1541
- status.innerHTML = `βœ… Video ready! <a href="${data.video_url}" target="_blank" class="btn btn-primary" style="margin-left: 1rem;">πŸ“₯ Download</a>`;
1542
- } else if (data.status === 'failed') {
1543
- status.className = 'status error';
1544
- status.innerHTML = '❌ Failed: ' + (data.error || 'Unknown error');
1545
- } else {
1546
- const step = data.current_step || 'Processing';
1547
- status.innerHTML = `⏳ ${step} (${data.progress}%)`;
1548
- setTimeout(poll, 2000);
1549
- }
1550
- } catch (err) {
1551
- setTimeout(poll, 3000);
1552
- }
1553
- };
1554
- poll();
1555
- }
1556
-
1557
- // ============================================
1558
- // MEDIA UPLOADER
1559
- // ============================================
1560
-
1561
- // Load HF folders on page load
1562
- async function loadHFFolders() {
1563
- const select = document.getElementById('mediaFolder');
1564
- try {
1565
- const res = await fetch('/api/utils/hf-folders');
1566
- if (res.ok) {
1567
- const data = await res.json();
1568
- select.innerHTML = '<option value="">Select folder...</option>';
1569
- data.folders.forEach(folder => {
1570
- select.innerHTML += `<option value="${folder}">${folder}</option>`;
1571
- });
1572
- } else {
1573
- select.innerHTML = '<option value="">Failed to load folders</option>';
1574
- }
1575
- } catch (err) {
1576
- select.innerHTML = '<option value="">Error loading folders</option>';
1577
- }
1578
- }
1579
-
1580
- // Toggle category input based on format
1581
- document.querySelectorAll('input[name="mediaFormat"]').forEach(radio => {
1582
- radio.addEventListener('change', (e) => {
1583
- const categorySection = document.getElementById('audioCategorySection');
1584
- categorySection.style.display = e.target.value === 'mp3' ? 'block' : 'none';
1585
- });
1586
- });
1587
-
1588
- // Create new folder
1589
- document.getElementById('createFolderBtn').addEventListener('click', async () => {
1590
- const name = prompt('Enter new folder name:');
1591
- if (!name) return;
1592
-
1593
- try {
1594
- const res = await fetch('/api/utils/hf-folders', {
1595
- method: 'POST',
1596
- headers: { 'Content-Type': 'application/json' },
1597
- body: JSON.stringify({ name: name.trim() })
1598
- });
1599
-
1600
- if (res.ok) {
1601
- alert('βœ… Folder created: ' + name);
1602
- loadHFFolders();
1603
- } else {
1604
- const err = await res.json();
1605
- alert('❌ Failed: ' + (err.detail || 'Unknown error'));
1606
- }
1607
- } catch (err) {
1608
- alert('❌ Error: ' + err.message);
1609
- }
1610
- });
1611
-
1612
- // Media Uploader form submission
1613
- document.getElementById('mediaUploaderForm').addEventListener('submit', async (e) => {
1614
- e.preventDefault();
1615
-
1616
- const status = document.getElementById('mediaUploaderStatus');
1617
- const resultsDiv = document.getElementById('mediaResults');
1618
- const resultsList = document.getElementById('mediaResultsList');
1619
-
1620
- status.className = 'status processing';
1621
- status.innerHTML = '⏳ Downloading and uploading... This may take a while...';
1622
- status.classList.remove('hidden');
1623
- resultsDiv.style.display = 'none';
1624
-
1625
- const format = document.querySelector('input[name="mediaFormat"]:checked').value;
1626
- const data = {
1627
- url: document.getElementById('mediaUrl').value,
1628
- folder: document.getElementById('mediaFolder').value,
1629
- format: format
1630
- };
1631
-
1632
- if (format === 'mp3') {
1633
- data.category = document.getElementById('audioCategory').value || 'music';
1634
- }
1635
-
1636
- try {
1637
- const res = await fetch('/api/utils/youtube-upload', {
1638
- method: 'POST',
1639
- headers: { 'Content-Type': 'application/json' },
1640
- body: JSON.stringify(data)
1641
- });
1642
-
1643
- const result = await res.json();
1644
-
1645
- if (res.ok && result.uploaded && result.uploaded.length > 0) {
1646
- status.className = 'status success';
1647
- status.innerHTML = `βœ… Uploaded ${result.uploaded.length} file(s)!`;
1648
-
1649
- // Show results
1650
- resultsList.innerHTML = '';
1651
- result.uploaded.forEach(file => {
1652
- resultsList.innerHTML += `
1653
- <div style="padding: 0.5rem; background: var(--bg-secondary); border-radius: 8px; margin-bottom: 0.5rem;">
1654
- <strong>${file.filename || file.id}</strong><br>
1655
- <small>${file.title}</small><br>
1656
- <a href="${file.url}" target="_blank" style="color: var(--accent);">πŸ”— View on HF</a>
1657
- </div>
1658
- `;
1659
- });
1660
- resultsDiv.style.display = 'block';
1661
-
1662
- // Refresh folders if new ones created
1663
- loadHFFolders();
1664
- } else {
1665
- status.className = 'status error';
1666
- status.innerHTML = '❌ ' + (result.error || result.detail || 'Upload failed');
1667
- }
1668
- } catch (err) {
1669
- status.className = 'status error';
1670
- status.innerHTML = '❌ Error: ' + err.message;
1671
- }
1672
- });
1673
-
1674
- // Load folders on page load
1675
- loadHFFolders();
1676
- </script>
1677
-
1678
  <!-- Chat Widget Button -->
1679
  <button id="chatBtn"
1680
  style="position:fixed; bottom:30px; right:30px; width:60px; height:60px; border-radius:50%; background:linear-gradient(135deg,#6366f1,#a855f7); border:none; font-size:28px; cursor:pointer; box-shadow:0 4px 20px rgba(99,102,241,0.5); z-index:9999;">πŸ’¬</button>
@@ -1692,114 +496,8 @@
1692
  </form>
1693
  </div>
1694
 
1695
- <style>
1696
- .chat-float-btn {
1697
- position: fixed;
1698
- bottom: 30px;
1699
- right: 30px;
1700
- width: 60px;
1701
- height: 60px;
1702
- border-radius: 50%;
1703
- background: linear-gradient(135deg, #6366f1, #a855f7);
1704
- border: none;
1705
- font-size: 28px;
1706
- cursor: pointer;
1707
- box-shadow: 0 4px 20px rgba(99, 102, 241, 0.5);
1708
- z-index: 9999;
1709
- transition: transform 0.2s;
1710
- }
1711
-
1712
- .chat-float-btn:hover {
1713
- transform: scale(1.1);
1714
- }
1715
-
1716
- .chat-modal {
1717
- position: fixed;
1718
- bottom: 100px;
1719
- right: 30px;
1720
- width: 380px;
1721
- height: 500px;
1722
- background: var(--bg-card);
1723
- border-radius: 16px;
1724
- box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
1725
- display: flex;
1726
- flex-direction: column;
1727
- z-index: 9998;
1728
- overflow: hidden;
1729
- }
1730
-
1731
- .chat-modal.hidden {
1732
- display: none;
1733
- }
1734
-
1735
- .chat-header {
1736
- padding: 15px 20px;
1737
- background: linear-gradient(135deg, #6366f1, #a855f7);
1738
- display: flex;
1739
- justify-content: space-between;
1740
- align-items: center;
1741
- font-weight: 600;
1742
- }
1743
-
1744
- .chat-header button {
1745
- background: none;
1746
- border: none;
1747
- color: white;
1748
- font-size: 18px;
1749
- cursor: pointer;
1750
- }
1751
-
1752
- .chat-body {
1753
- flex: 1;
1754
- padding: 15px;
1755
- overflow-y: auto;
1756
- display: flex;
1757
- flex-direction: column;
1758
- gap: 10px;
1759
- }
1760
-
1761
- .chat-message {
1762
- padding: 10px 14px;
1763
- border-radius: 12px;
1764
- max-width: 85%;
1765
- word-wrap: break-word;
1766
- }
1767
-
1768
- .chat-message.user {
1769
- background: var(--accent);
1770
- align-self: flex-end;
1771
- }
1772
-
1773
- .chat-message.bot {
1774
- background: var(--bg-secondary);
1775
- align-self: flex-start;
1776
- }
1777
-
1778
- .chat-input-area {
1779
- display: flex;
1780
- padding: 10px;
1781
- gap: 10px;
1782
- border-top: 1px solid var(--border);
1783
- }
1784
-
1785
- .chat-input-area input {
1786
- flex: 1;
1787
- padding: 12px;
1788
- border-radius: 8px;
1789
- border: none;
1790
- background: var(--bg-secondary);
1791
- color: var(--text-primary);
1792
- }
1793
-
1794
- .chat-input-area button {
1795
- padding: 12px 20px;
1796
- border-radius: 8px;
1797
- border: none;
1798
- background: var(--accent);
1799
- color: white;
1800
- cursor: pointer;
1801
- }
1802
- </style>
1803
  </body>
1804
 
1805
  </html>
 
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
  <title>NCAkit - Neural Content Automation</title>
8
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
9
+ <link rel="stylesheet" href="/static/css/styles.css">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  </head>
11
 
12
  <body>
 
18
 
19
  <!-- Tabs -->
20
  <div class="tabs">
21
+ <button class="tab-btn active" data-tab="story">🎬 Story Reels</button>
22
+ <button class="tab-btn" data-tab="video">πŸ“Ή Video Creator</button>
23
+ <button class="tab-btn" data-tab="fact">🧠 Fact Image</button>
24
+ <button class="tab-btn" data-tab="trends">πŸ“Š Trends</button>
25
+ <button class="tab-btn" data-tab="quiz">🎯 Quiz Reel</button>
26
+ <button class="tab-btn" data-tab="textstory">πŸ“± Text Story</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  </div>
28
 
29
  <!-- Story Reels Tab -->
 
38
  <div class="form-group">
39
  <label>Voice Script *</label>
40
  <textarea id="storyScript" rows="4"
41
+ placeholder="e.g., Have you ever wondered why some people seem to attract success effortlessly? The secret lies in their mindset..."
42
  required></textarea>
 
 
43
  </div>
44
 
45
  <div class="form-row">
 
50
  <option value="anime">Anime</option>
51
  <option value="cartoon">Cartoon</option>
52
  <option value="realistic">Realistic</option>
 
 
53
  </select>
54
  </div>
55
  <div class="form-group">
 
111
 
112
  <button type="button" id="addScene" class="btn btn-secondary" style="margin-bottom: 1rem;">+ Add
113
  Scene</button>
 
114
  <button type="submit" class="btn btn-primary">🎬 Create Video</button>
115
  </form>
116
 
 
123
  <div class="card">
124
  <h2>🧠 Fact Image Video</h2>
125
  <p style="color: var(--text-secondary); margin-bottom: 1.5rem;">
126
+ Generate a clean image β†’ Add fact text overlay β†’ Create short video
127
  </p>
128
 
129
  <form id="factForm">
 
154
  <option value="rgba(0, 0, 0, 0.45)">Black (45%)</option>
155
  <option value="rgba(0, 0, 0, 0.6)">Black (60%)</option>
156
  <option value="rgba(0, 31, 63, 0.75)">Navy Blue</option>
 
 
157
  <option value="rgba(48, 25, 52, 0.75)">Deep Purple</option>
 
 
 
 
158
  </select>
159
  </div>
160
  <div class="form-group">
 
170
  <div class="form-group">
171
  <label>Image Prompt *</label>
172
  <textarea id="factImagePrompt" rows="2"
173
+ placeholder="e.g., calm forest with soft sunlight filtering through trees"
174
  required></textarea>
 
 
175
  </div>
176
 
177
  <div class="form-group">
178
  <label>Fact Heading (Optional)</label>
179
  <input type="text" id="factHeading" placeholder="e.g., Psychological Hack" maxlength="50">
 
 
180
  </div>
181
 
182
  <div class="form-group">
 
184
  <textarea id="factText" rows="3"
185
  placeholder="e.g., People who daydream more tend to have higher creative intelligence."
186
  required></textarea>
 
 
187
  </div>
188
 
189
  <button type="submit" class="btn btn-primary">🎬 Generate Fact Video</button>
 
202
  Analyze Google Trends data for content planning
203
  </p>
204
 
 
205
  <div style="margin-bottom: 2rem;">
206
  <h3 style="margin-bottom: 1rem;">πŸ”₯ Trending Now</h3>
207
  <form id="trendingForm">
 
213
  <option value="bangladesh">Bangladesh</option>
214
  <option value="india">India</option>
215
  <option value="united_kingdom">United Kingdom</option>
 
 
 
 
216
  </select>
217
  </div>
218
  <div class="form-group">
 
221
  <option value="10">10</option>
222
  <option value="20" selected>20</option>
223
  <option value="30">30</option>
 
224
  </select>
225
  </div>
226
  </div>
227
+ <button type="submit" class="btn btn-primary">Get Trending</button>
228
  </form>
229
  <div id="trendingResults" style="margin-top: 1rem;"></div>
230
  </div>
231
 
232
  <hr style="border-color: var(--border); margin: 2rem 0;">
233
 
 
234
  <div>
235
  <h3 style="margin-bottom: 1rem;">πŸ” Keyword Research</h3>
236
  <form id="keywordForm">
 
246
  <option value="US">United States</option>
247
  <option value="BD">Bangladesh</option>
248
  <option value="IN">India</option>
 
249
  </select>
250
  </div>
251
  <div class="form-group">
 
253
  <select id="researchTimeframe">
254
  <option value="now_1d">Last 24 hours</option>
255
  <option value="now_7d">Last 7 days</option>
 
 
256
  <option value="today_12m" selected>Last 12 months</option>
257
  </select>
258
  </div>
 
265
  <option value="computers_electronics">Computers & Electronics</option>
266
  <option value="games">Games</option>
267
  <option value="arts_entertainment">Arts & Entertainment</option>
 
 
 
 
268
  </select>
269
  </div>
270
  <div class="form-group">
 
273
  <option value="web">Web Search</option>
274
  <option value="youtube">YouTube Search</option>
275
  <option value="news">News Search</option>
 
276
  </select>
277
  </div>
278
  </div>
279
+ <button type="submit" class="btn btn-primary">Analyze Keyword</button>
280
  </form>
281
  <div id="keywordResults" style="margin-top: 1rem;"></div>
282
  </div>
 
 
283
  </div>
284
  </div>
285
 
 
293
 
294
  <form id="quizForm">
295
  <div id="quizContainer">
 
296
  <div class="quiz-item"
297
  style="background: var(--bg-secondary); padding: 1.5rem; border-radius: 12px; margin-bottom: 1rem;">
298
  <h4 style="color: var(--accent); margin-bottom: 1rem;">Quiz 1</h4>
 
331
  </div>
332
  <div class="form-group">
333
  <label>Explain (shown after answer)</label>
334
+ <input type="text" class="quiz-explain" placeholder="Brief explanation">
335
  </div>
336
  </div>
337
  </div>
338
 
339
  <div style="display: flex; gap: 1rem; margin-bottom: 1.5rem;">
340
+ <button type="button" id="addQuizBtn" class="btn btn-secondary" style="flex: 1;">βž• Add Another
341
+ Quiz</button>
 
342
  <button type="button" id="removeQuizBtn" class="btn btn-secondary"
343
+ style="flex: 1; background: #dc2626;">βž– Remove Last Quiz</button>
 
 
344
  </div>
345
 
346
  <button type="submit" class="btn btn-primary" style="width: 100%;">🎯 Generate Quiz Video</button>
 
358
  Create viral iMessage-style fake conversation videos with AI voice
359
  </p>
360
 
 
361
  <div class="form-group" style="margin-bottom: 1.5rem;">
362
  <label>Mode</label>
363
  <div style="display: flex; gap: 1rem;">
 
373
  </div>
374
 
375
  <form id="textStoryForm">
 
376
  <div id="tsAiSection" style="display: none;">
377
  <div class="form-group"
378
  style="background: linear-gradient(135deg, rgba(99,102,241,0.1), rgba(168,85,247,0.1)); padding: 1.5rem; border-radius: 12px; margin-bottom: 1rem;">
379
  <label>πŸ€– AI Prompt - Describe the conversation</label>
380
  <textarea id="tsAiPrompt" rows="3"
381
+ placeholder="e.g., A breakup conversation where the ex wants to get back together but gets rejected."></textarea>
 
 
 
382
  </div>
 
383
  <div class="form-row">
384
  <div class="form-group">
385
  <label>Number of Messages</label>
 
397
  <option value="funny">Funny / Comedy</option>
398
  <option value="shocking">Shocking / Twist</option>
399
  <option value="romantic">Romantic</option>
 
400
  </select>
401
  </div>
402
  </div>
403
  </div>
404
 
 
405
  <div class="form-row">
406
  <div class="form-group">
407
  <label>Person A Name (You - Right/Blue)</label>
 
418
  <input type="text" id="tsAvatar" maxlength="2" placeholder="M" style="width: 80px;">
419
  </div>
420
 
 
421
  <div id="tsManualSection">
422
  <div id="tsMessagesContainer">
423
  <div class="ts-message-item"
 
479
  </div>
480
  </div>
481
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
482
  <!-- Chat Widget Button -->
483
  <button id="chatBtn"
484
  style="position:fixed; bottom:30px; right:30px; width:60px; height:60px; border-radius:50%; background:linear-gradient(135deg,#6366f1,#a855f7); border:none; font-size:28px; cursor:pointer; box-shadow:0 4px 20px rgba(99,102,241,0.5); z-index:9999;">πŸ’¬</button>
 
496
  </form>
497
  </div>
498
 
499
+ <!-- External JavaScript (defer for faster page load) -->
500
+ <script src="/static/js/main.js" defer></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
  </body>
502
 
503
  </html>
static/js/main.js ADDED
@@ -0,0 +1,682 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* NCAkit - Main JavaScript */
2
+
3
+ // Wait for DOM to be ready
4
+ document.addEventListener('DOMContentLoaded', function() {
5
+
6
+ // ============================================
7
+ // TAB SWITCHING
8
+ // ============================================
9
+ document.querySelectorAll('.tab-btn').forEach(btn => {
10
+ btn.addEventListener('click', () => {
11
+ document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
12
+ document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
13
+ btn.classList.add('active');
14
+ document.getElementById(btn.dataset.tab + '-tab').classList.add('active');
15
+ });
16
+ });
17
+
18
+ // ============================================
19
+ // STORY REELS MODULE
20
+ // ============================================
21
+ document.getElementById('storyForm').addEventListener('submit', async (e) => {
22
+ e.preventDefault();
23
+ const status = document.getElementById('storyStatus');
24
+ status.className = 'status processing';
25
+ status.innerHTML = '⏳ Starting generation...';
26
+ status.classList.remove('hidden');
27
+
28
+ const data = {
29
+ script: document.getElementById('storyScript').value,
30
+ image_style: document.getElementById('storyStyle').value,
31
+ voice: document.getElementById('storyVoice').value
32
+ };
33
+
34
+ try {
35
+ const res = await fetch('/api/story/story-reel', {
36
+ method: 'POST',
37
+ headers: { 'Content-Type': 'application/json' },
38
+ body: JSON.stringify(data)
39
+ });
40
+ const result = await res.json();
41
+
42
+ if (result.job_id) {
43
+ status.innerHTML = `βœ… Job started! ID: ${result.job_id}<br>Checking status...`;
44
+ pollStatus(result.job_id, 'story');
45
+ }
46
+ } catch (err) {
47
+ status.className = 'status error';
48
+ status.innerHTML = '❌ Error: ' + err.message;
49
+ }
50
+ });
51
+
52
+ // Generic poll status for story/video
53
+ async function pollStatus(jobId, type) {
54
+ const status = document.getElementById(type + 'Status');
55
+ const endpoint = type === 'story' ? '/api/story/story-reel/' : '/api/video/short-video/';
56
+
57
+ const check = async () => {
58
+ try {
59
+ const res = await fetch(endpoint + jobId + '/status');
60
+ const data = await res.json();
61
+
62
+ const progress = data.progress || 0;
63
+ status.innerHTML = `
64
+ <div>Status: <strong>${data.status}</strong></div>
65
+ <div class="progress-bar"><div class="progress-fill" style="width: ${progress}%"></div></div>
66
+ `;
67
+
68
+ if (data.status === 'ready') {
69
+ status.className = 'status success';
70
+ const downloadUrl = type === 'story' ? `/api/story/story-reel/${jobId}` : `/api/video/short-video/${jobId}`;
71
+ status.innerHTML += `<br><a href="${downloadUrl}" class="btn btn-primary" style="margin-top: 1rem; display: inline-block;">πŸ“₯ Download Video</a>`;
72
+ } else if (data.status === 'failed') {
73
+ status.className = 'status error';
74
+ status.innerHTML = '❌ Failed: ' + (data.error || 'Unknown error');
75
+ } else {
76
+ setTimeout(check, 2000);
77
+ }
78
+ } catch (err) {
79
+ setTimeout(check, 3000);
80
+ }
81
+ };
82
+ check();
83
+ }
84
+
85
+ // ============================================
86
+ // VIDEO CREATOR MODULE
87
+ // ============================================
88
+
89
+ // Add scene
90
+ document.getElementById('addScene').addEventListener('click', () => {
91
+ const container = document.getElementById('scenesContainer');
92
+ const count = container.querySelectorAll('.scene-text').length + 1;
93
+ container.innerHTML += `
94
+ <div class="form-group">
95
+ <label>Scene ${count} - Text</label>
96
+ <textarea class="scene-text" rows="2" placeholder="Enter narration..." required></textarea>
97
+ </div>
98
+ <div class="form-group">
99
+ <label>Keywords</label>
100
+ <input type="text" class="scene-keywords" placeholder="nature, forest">
101
+ </div>
102
+ `;
103
+ });
104
+
105
+ // Video Form
106
+ document.getElementById('videoForm').addEventListener('submit', async (e) => {
107
+ e.preventDefault();
108
+ const status = document.getElementById('videoStatus');
109
+ status.className = 'status processing';
110
+ status.innerHTML = '⏳ Creating video...';
111
+ status.classList.remove('hidden');
112
+
113
+ const scenes = [];
114
+ document.querySelectorAll('.scene-text').forEach((textarea, i) => {
115
+ const keywords = document.querySelectorAll('.scene-keywords')[i]?.value || '';
116
+ scenes.push({
117
+ text: textarea.value,
118
+ searchTerms: keywords.split(',').map(k => k.trim()).filter(k => k)
119
+ });
120
+ });
121
+
122
+ const data = {
123
+ scenes: scenes,
124
+ config: {
125
+ voice: document.getElementById('videoVoice').value,
126
+ music: document.getElementById('videoMusic').value || null
127
+ }
128
+ };
129
+
130
+ try {
131
+ const res = await fetch('/api/video/short-video', {
132
+ method: 'POST',
133
+ headers: { 'Content-Type': 'application/json' },
134
+ body: JSON.stringify(data)
135
+ });
136
+ const result = await res.json();
137
+
138
+ if (result.videoId) {
139
+ status.innerHTML = `βœ… Video started! ID: ${result.videoId}`;
140
+ pollStatus(result.videoId, 'video');
141
+ }
142
+ } catch (err) {
143
+ status.className = 'status error';
144
+ status.innerHTML = '❌ Error: ' + err.message;
145
+ }
146
+ });
147
+
148
+ // ============================================
149
+ // FACT IMAGE MODULE
150
+ // ============================================
151
+ document.getElementById('factForm').addEventListener('submit', async (e) => {
152
+ e.preventDefault();
153
+ const status = document.getElementById('factStatus');
154
+ status.className = 'status processing';
155
+ status.innerHTML = '⏳ Generating fact video...';
156
+ status.classList.remove('hidden');
157
+
158
+ const heading = document.getElementById('factHeading').value.trim();
159
+
160
+ const data = {
161
+ model: document.getElementById('factModel').value,
162
+ image_prompt: document.getElementById('factImagePrompt').value,
163
+ fact_text: document.getElementById('factText').value,
164
+ duration: parseInt(document.getElementById('factDuration').value)
165
+ };
166
+
167
+ if (heading) {
168
+ data.fact_heading = heading;
169
+ data.heading_background = {
170
+ enabled: true,
171
+ color: document.getElementById('headingBgColor').value,
172
+ padding: 22,
173
+ corner_radius: parseInt(document.getElementById('headingBgRadius').value)
174
+ };
175
+ }
176
+
177
+ try {
178
+ const res = await fetch('/api/fact-image/', {
179
+ method: 'POST',
180
+ headers: { 'Content-Type': 'application/json' },
181
+ body: JSON.stringify(data)
182
+ });
183
+ const result = await res.json();
184
+
185
+ if (result.job_id) {
186
+ status.innerHTML = `βœ… Job started! ID: ${result.job_id}<br>Checking status...`;
187
+ pollFactStatus(result.job_id);
188
+ }
189
+ } catch (err) {
190
+ status.className = 'status error';
191
+ status.innerHTML = '❌ Error: ' + err.message;
192
+ }
193
+ });
194
+
195
+ async function pollFactStatus(jobId) {
196
+ const status = document.getElementById('factStatus');
197
+
198
+ const check = async () => {
199
+ try {
200
+ const res = await fetch(`/api/fact-image/${jobId}/status`);
201
+ const data = await res.json();
202
+
203
+ const progress = data.progress || 0;
204
+ status.innerHTML = `
205
+ <div>Status: <strong>${data.status}</strong></div>
206
+ <div class="progress-bar"><div class="progress-fill" style="width: ${progress}%"></div></div>
207
+ `;
208
+
209
+ if (data.status === 'ready') {
210
+ status.className = 'status success';
211
+ status.innerHTML += `<br><a href="/api/fact-image/${jobId}" class="btn btn-primary" style="margin-top: 1rem; display: inline-block;">πŸ“₯ Download Video</a>`;
212
+ } else if (data.status === 'failed') {
213
+ status.className = 'status error';
214
+ status.innerHTML = '❌ Failed: ' + (data.error || 'Unknown error');
215
+ } else {
216
+ setTimeout(check, 2000);
217
+ }
218
+ } catch (err) {
219
+ setTimeout(check, 3000);
220
+ }
221
+ };
222
+ check();
223
+ }
224
+
225
+ // ============================================
226
+ // TRENDS MODULE
227
+ // ============================================
228
+ document.getElementById('trendingForm').addEventListener('submit', async (e) => {
229
+ e.preventDefault();
230
+ const results = document.getElementById('trendingResults');
231
+ results.innerHTML = '<p>⏳ Loading trends...</p>';
232
+
233
+ const data = {
234
+ country: document.getElementById('trendCountry').value,
235
+ limit: parseInt(document.getElementById('trendLimit').value)
236
+ };
237
+
238
+ try {
239
+ const res = await fetch('/api/trends/trending-now', {
240
+ method: 'POST',
241
+ headers: { 'Content-Type': 'application/json' },
242
+ body: JSON.stringify(data)
243
+ });
244
+ const result = await res.json();
245
+
246
+ if (result.success) {
247
+ let html = '<div style="background: var(--bg-secondary); border-radius: 8px; padding: 1rem; max-height: 400px; overflow-y: auto;">';
248
+ result.trends.forEach(t => {
249
+ html += `<div style="padding: 0.5rem 0; border-bottom: 1px solid var(--border);">
250
+ <span style="color: var(--accent); font-weight: bold;">#${t.rank}</span> ${t.topic}
251
+ </div>`;
252
+ });
253
+ html += '</div>';
254
+ results.innerHTML = html;
255
+ } else {
256
+ results.innerHTML = '<p style="color: var(--error);">❌ Error: ' + (result.detail || 'Failed') + '</p>';
257
+ }
258
+ } catch (err) {
259
+ results.innerHTML = '<p style="color: var(--error);">❌ Error: ' + err.message + '</p>';
260
+ }
261
+ });
262
+
263
+ document.getElementById('keywordForm').addEventListener('submit', async (e) => {
264
+ e.preventDefault();
265
+ const results = document.getElementById('keywordResults');
266
+ results.innerHTML = '<p>⏳ Analyzing keyword...</p>';
267
+
268
+ const data = {
269
+ keyword: document.getElementById('researchKeyword').value,
270
+ region: document.getElementById('researchRegion').value,
271
+ timeframe: document.getElementById('researchTimeframe').value,
272
+ category: document.getElementById('researchCategory').value,
273
+ search_type: document.getElementById('researchType').value
274
+ };
275
+
276
+ try {
277
+ const res = await fetch('/api/trends/keyword-research', {
278
+ method: 'POST',
279
+ headers: { 'Content-Type': 'application/json' },
280
+ body: JSON.stringify(data)
281
+ });
282
+ const result = await res.json();
283
+
284
+ if (result.success) {
285
+ let html = '<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;">';
286
+
287
+ html += '<div style="background: var(--bg-secondary); border-radius: 8px; padding: 1rem;">';
288
+ html += '<h4 style="margin-bottom: 0.5rem;">πŸ“Œ Related Topics</h4>';
289
+ if (result.related_topics.top && result.related_topics.top.length > 0) {
290
+ result.related_topics.top.slice(0, 10).forEach(t => {
291
+ html += `<div style="padding: 0.25rem 0; font-size: 0.9rem;">${t.topic} <span style="color: var(--text-secondary);">(${t.value})</span></div>`;
292
+ });
293
+ } else {
294
+ html += '<p style="color: var(--text-secondary); font-size: 0.9rem;">No data</p>';
295
+ }
296
+ html += '</div>';
297
+
298
+ html += '<div style="background: var(--bg-secondary); border-radius: 8px; padding: 1rem;">';
299
+ html += '<h4 style="margin-bottom: 0.5rem;">πŸ” Related Queries</h4>';
300
+ if (result.related_queries.top && result.related_queries.top.length > 0) {
301
+ result.related_queries.top.slice(0, 10).forEach(q => {
302
+ html += `<div style="padding: 0.25rem 0; font-size: 0.9rem;">${q.query} <span style="color: var(--text-secondary);">(${q.value})</span></div>`;
303
+ });
304
+ } else {
305
+ html += '<p style="color: var(--text-secondary); font-size: 0.9rem;">No data</p>';
306
+ }
307
+ html += '</div>';
308
+
309
+ html += '</div>';
310
+ results.innerHTML = html;
311
+ } else {
312
+ results.innerHTML = '<p style="color: var(--error);">❌ Error: ' + (result.detail || 'Failed') + '</p>';
313
+ }
314
+ } catch (err) {
315
+ results.innerHTML = '<p style="color: var(--error);">❌ Error: ' + err.message + '</p>';
316
+ }
317
+ });
318
+
319
+ // ============================================
320
+ // QUIZ REEL MODULE
321
+ // ============================================
322
+ let quizCount = 1;
323
+
324
+ document.getElementById('addQuizBtn').addEventListener('click', () => {
325
+ quizCount++;
326
+ const container = document.getElementById('quizContainer');
327
+ const quizHtml = `
328
+ <div class="quiz-item" style="background: var(--bg-secondary); padding: 1.5rem; border-radius: 12px; margin-bottom: 1rem;">
329
+ <h4 style="color: var(--accent); margin-bottom: 1rem;">Quiz ${quizCount}</h4>
330
+ <div class="form-group">
331
+ <label>Hook (Category)</label>
332
+ <input type="text" class="quiz-hook" placeholder="e.g., IQ TEST, MATH QUIZ">
333
+ </div>
334
+ <div class="form-group">
335
+ <label>Question *</label>
336
+ <input type="text" class="quiz-question" placeholder="Enter your question" required>
337
+ </div>
338
+ <div class="form-row">
339
+ <div class="form-group">
340
+ <label>Option A *</label>
341
+ <input type="text" class="quiz-option-a" placeholder="Answer A" required>
342
+ </div>
343
+ <div class="form-group">
344
+ <label>Option B *</label>
345
+ <input type="text" class="quiz-option-b" placeholder="Answer B" required>
346
+ </div>
347
+ <div class="form-group">
348
+ <label>Option C *</label>
349
+ <input type="text" class="quiz-option-c" placeholder="Answer C" required>
350
+ </div>
351
+ </div>
352
+ <div class="form-row">
353
+ <div class="form-group">
354
+ <label>Correct Answer *</label>
355
+ <select class="quiz-correct" required>
356
+ <option value="A">A</option>
357
+ <option value="B">B</option>
358
+ <option value="C">C</option>
359
+ </select>
360
+ </div>
361
+ </div>
362
+ <div class="form-group">
363
+ <label>Explain (shown after answer)</label>
364
+ <input type="text" class="quiz-explain" placeholder="Brief explanation">
365
+ </div>
366
+ </div>
367
+ `;
368
+ container.insertAdjacentHTML('beforeend', quizHtml);
369
+ });
370
+
371
+ document.getElementById('removeQuizBtn').addEventListener('click', () => {
372
+ const container = document.getElementById('quizContainer');
373
+ const items = container.querySelectorAll('.quiz-item');
374
+ if (items.length > 1) {
375
+ items[items.length - 1].remove();
376
+ quizCount--;
377
+ }
378
+ });
379
+
380
+ async function pollQuizStatus(jobId, statusDiv) {
381
+ const checkStatus = async () => {
382
+ try {
383
+ const res = await fetch(`/api/quiz/${jobId}/status`);
384
+ const status = await res.json();
385
+
386
+ if (status.status === 'ready') {
387
+ statusDiv.className = 'status success';
388
+ statusDiv.innerHTML = `βœ… Video Ready! <a href="${status.video_url}" target="_blank" style="color: var(--accent);">Download Video</a>`;
389
+ return;
390
+ } else if (status.status === 'failed') {
391
+ statusDiv.className = 'status error';
392
+ statusDiv.innerHTML = `❌ Failed: ${status.error}`;
393
+ return;
394
+ } else {
395
+ statusDiv.innerHTML = `⏳ ${status.status}... ${status.progress}%`;
396
+ setTimeout(checkStatus, 2000);
397
+ }
398
+ } catch (err) {
399
+ statusDiv.className = 'status error';
400
+ statusDiv.innerHTML = `❌ Error: ${err.message}`;
401
+ }
402
+ };
403
+ checkStatus();
404
+ }
405
+
406
+ document.getElementById('quizForm').addEventListener('submit', async (e) => {
407
+ e.preventDefault();
408
+ const status = document.getElementById('quizStatus');
409
+ status.className = 'status';
410
+ status.classList.remove('hidden');
411
+ status.innerHTML = '⏳ Starting quiz video generation...';
412
+
413
+ const quizItems = document.querySelectorAll('.quiz-item');
414
+ const quizzes = [];
415
+
416
+ quizItems.forEach(item => {
417
+ quizzes.push({
418
+ hook: item.querySelector('.quiz-hook').value || '',
419
+ question: item.querySelector('.quiz-question').value,
420
+ options: {
421
+ A: item.querySelector('.quiz-option-a').value,
422
+ B: item.querySelector('.quiz-option-b').value,
423
+ C: item.querySelector('.quiz-option-c').value
424
+ },
425
+ correct: item.querySelector('.quiz-correct').value,
426
+ explain: item.querySelector('.quiz-explain').value || ''
427
+ });
428
+ });
429
+
430
+ const data = {
431
+ quizzes: quizzes,
432
+ voice: 'af_heart'
433
+ };
434
+
435
+ try {
436
+ const res = await fetch('/api/quiz/generate', {
437
+ method: 'POST',
438
+ headers: { 'Content-Type': 'application/json' },
439
+ body: JSON.stringify(data)
440
+ });
441
+ const result = await res.json();
442
+
443
+ if (result.job_id) {
444
+ status.innerHTML = `⏳ Job started: ${result.job_id} (${quizzes.length} quizzes)`;
445
+ pollQuizStatus(result.job_id, status);
446
+ } else {
447
+ status.className = 'status error';
448
+ status.innerHTML = `❌ Error: ${result.detail || 'Failed to start'}`;
449
+ }
450
+ } catch (err) {
451
+ status.className = 'status error';
452
+ status.innerHTML = '❌ Error: ' + err.message;
453
+ }
454
+ });
455
+
456
+ // ============================================
457
+ // TEXT STORY MODULE
458
+ // ============================================
459
+ document.getElementById('tsAddMessage').addEventListener('click', () => {
460
+ const container = document.getElementById('tsMessagesContainer');
461
+ const count = container.querySelectorAll('.ts-message-item').length + 1;
462
+ const html = `
463
+ <div class="ts-message-item" style="background: var(--bg-secondary); padding: 1rem; border-radius: 8px; margin-bottom: 0.5rem;">
464
+ <div class="form-row" style="align-items: flex-end;">
465
+ <div class="form-group" style="flex: 0 0 100px;">
466
+ <label>Sender</label>
467
+ <select class="ts-sender">
468
+ <option value="B">B (Other)</option>
469
+ <option value="A">A (You)</option>
470
+ </select>
471
+ </div>
472
+ <div class="form-group" style="flex: 1;">
473
+ <label>Message ${count}</label>
474
+ <input type="text" class="ts-text" placeholder="Type message..." required>
475
+ </div>
476
+ <button type="button" class="btn btn-secondary ts-remove" style="height: 42px;">βœ•</button>
477
+ </div>
478
+ </div>
479
+ `;
480
+ container.insertAdjacentHTML('beforeend', html);
481
+ });
482
+
483
+ document.getElementById('tsMessagesContainer').addEventListener('click', (e) => {
484
+ if (e.target.classList.contains('ts-remove')) {
485
+ const items = document.querySelectorAll('.ts-message-item');
486
+ if (items.length > 1) {
487
+ e.target.closest('.ts-message-item').remove();
488
+ }
489
+ }
490
+ });
491
+
492
+ // Toggle Manual/AI mode - make global
493
+ window.toggleTsMode = function() {
494
+ const mode = document.querySelector('input[name="tsMode"]:checked').value;
495
+ document.getElementById('tsAiSection').style.display = mode === 'ai' ? 'block' : 'none';
496
+ document.getElementById('tsManualSection').style.display = mode === 'manual' ? 'block' : 'none';
497
+ };
498
+
499
+ document.getElementById('textStoryForm').addEventListener('submit', async (e) => {
500
+ e.preventDefault();
501
+ const status = document.getElementById('textStoryStatus');
502
+ status.className = 'status processing';
503
+ status.classList.remove('hidden');
504
+
505
+ const mode = document.querySelector('input[name="tsMode"]:checked').value;
506
+ let messages = [];
507
+
508
+ if (mode === 'ai') {
509
+ const prompt = document.getElementById('tsAiPrompt').value.trim();
510
+ if (!prompt) {
511
+ status.className = 'status error';
512
+ status.innerHTML = '❌ Please enter a prompt for AI!';
513
+ return;
514
+ }
515
+
516
+ status.innerHTML = 'πŸ€– AI generating conversation...';
517
+
518
+ try {
519
+ const aiRes = await fetch('/api/text-story/ai-generate', {
520
+ method: 'POST',
521
+ headers: { 'Content-Type': 'application/json' },
522
+ body: JSON.stringify({
523
+ prompt: prompt,
524
+ person_a_name: document.getElementById('tsPersonA').value || 'You',
525
+ person_b_name: document.getElementById('tsPersonB').value || 'My Ex',
526
+ message_count: parseInt(document.getElementById('tsAiMsgCount').value),
527
+ tone: document.getElementById('tsAiTone').value
528
+ })
529
+ });
530
+ const aiData = await aiRes.json();
531
+
532
+ if (aiData.messages) {
533
+ messages = aiData.messages;
534
+ status.innerHTML = `πŸ€– Generated ${messages.length} messages. Now creating video...`;
535
+ } else {
536
+ status.className = 'status error';
537
+ status.innerHTML = '❌ AI failed: ' + (aiData.detail || 'Unknown error');
538
+ return;
539
+ }
540
+ } catch (err) {
541
+ status.className = 'status error';
542
+ status.innerHTML = '❌ AI Error: ' + err.message;
543
+ return;
544
+ }
545
+ } else {
546
+ const messageItems = document.querySelectorAll('.ts-message-item');
547
+ messageItems.forEach(item => {
548
+ const sender = item.querySelector('.ts-sender').value;
549
+ const text = item.querySelector('.ts-text').value.trim();
550
+ if (text) {
551
+ messages.push({ sender, text });
552
+ }
553
+ });
554
+
555
+ if (messages.length < 2) {
556
+ status.className = 'status error';
557
+ status.innerHTML = '❌ Need at least 2 messages!';
558
+ return;
559
+ }
560
+ }
561
+
562
+ status.innerHTML = '⏳ Starting video generation...';
563
+
564
+ const data = {
565
+ person_a_name: document.getElementById('tsPersonA').value || 'You',
566
+ person_b_name: document.getElementById('tsPersonB').value || 'My Ex',
567
+ person_b_avatar: document.getElementById('tsAvatar').value || null,
568
+ messages: messages,
569
+ ending_text: document.getElementById('tsEnding').value || null,
570
+ voice_a: document.getElementById('tsVoiceA').value,
571
+ voice_b: document.getElementById('tsVoiceB').value
572
+ };
573
+
574
+ try {
575
+ const res = await fetch('/api/text-story/generate', {
576
+ method: 'POST',
577
+ headers: { 'Content-Type': 'application/json' },
578
+ body: JSON.stringify(data)
579
+ });
580
+ const result = await res.json();
581
+
582
+ if (result.job_id) {
583
+ status.innerHTML = `⏳ Job started: ${result.job_id}`;
584
+ pollTextStoryStatus(result.job_id);
585
+ } else {
586
+ status.className = 'status error';
587
+ status.innerHTML = `❌ Error: ${result.detail || 'Failed to start'}`;
588
+ }
589
+ } catch (err) {
590
+ status.className = 'status error';
591
+ status.innerHTML = '❌ Error: ' + err.message;
592
+ }
593
+ });
594
+
595
+ async function pollTextStoryStatus(jobId) {
596
+ const status = document.getElementById('textStoryStatus');
597
+ const poll = async () => {
598
+ try {
599
+ const res = await fetch(`/api/text-story/${jobId}/status`);
600
+ const data = await res.json();
601
+
602
+ if (data.status === 'ready') {
603
+ status.className = 'status success';
604
+ status.innerHTML = `βœ… Video ready! <a href="${data.video_url}" target="_blank" class="btn btn-primary" style="margin-left: 1rem;">πŸ“₯ Download</a>`;
605
+ } else if (data.status === 'failed') {
606
+ status.className = 'status error';
607
+ status.innerHTML = '❌ Failed: ' + (data.error || 'Unknown error');
608
+ } else {
609
+ const step = data.current_step || 'Processing';
610
+ status.innerHTML = `⏳ ${step} (${data.progress}%)`;
611
+ setTimeout(poll, 2000);
612
+ }
613
+ } catch (err) {
614
+ setTimeout(poll, 3000);
615
+ }
616
+ };
617
+ poll();
618
+ }
619
+
620
+ // ============================================
621
+ // GEMINI CHATBOT
622
+ // ============================================
623
+ const chatBtn = document.getElementById('chatBtn');
624
+ const chatModal = document.getElementById('chatModal');
625
+ const chatClose = document.getElementById('chatClose');
626
+ const chatForm = document.getElementById('chatForm');
627
+ const chatInput = document.getElementById('chatInput');
628
+ const chatMessages = document.getElementById('chatMessages');
629
+
630
+ if (chatBtn) {
631
+ chatBtn.addEventListener('click', () => {
632
+ chatModal.classList.toggle('hidden');
633
+ });
634
+
635
+ chatClose.addEventListener('click', () => {
636
+ chatModal.classList.add('hidden');
637
+ });
638
+
639
+ chatForm.addEventListener('submit', async (e) => {
640
+ e.preventDefault();
641
+ const message = chatInput.value.trim();
642
+ if (!message) return;
643
+
644
+ addMessage(message, 'user');
645
+ chatInput.value = '';
646
+
647
+ const loadingId = addMessage('⏳ Thinking...', 'bot');
648
+
649
+ try {
650
+ const res = await fetch('/api/chat', {
651
+ method: 'POST',
652
+ headers: { 'Content-Type': 'application/json' },
653
+ body: JSON.stringify({ message })
654
+ });
655
+ const data = await res.json();
656
+
657
+ document.getElementById(loadingId).remove();
658
+
659
+ if (data.reply) {
660
+ addMessage(data.reply, 'bot');
661
+ } else if (data.error) {
662
+ addMessage('❌ ' + data.error, 'bot');
663
+ }
664
+ } catch (err) {
665
+ document.getElementById(loadingId).remove();
666
+ addMessage('❌ Error: ' + err.message, 'bot');
667
+ }
668
+ });
669
+
670
+ function addMessage(text, type) {
671
+ const id = 'msg-' + Date.now();
672
+ const div = document.createElement('div');
673
+ div.id = id;
674
+ div.className = 'chat-message ' + type;
675
+ div.textContent = text;
676
+ chatMessages.appendChild(div);
677
+ chatMessages.scrollTop = chatMessages.scrollHeight;
678
+ return id;
679
+ }
680
+ }
681
+
682
+ }); // End DOMContentLoaded