aldohenrique commited on
Commit
44035aa
·
verified ·
1 Parent(s): 39248e1

Update interface.py

Browse files
Files changed (1) hide show
  1. interface.py +203 -568
interface.py CHANGED
@@ -8,211 +8,111 @@ from ai_logic import (
8
  inicializar_sistema
9
  )
10
 
 
11
  css_customizado = """
12
- /* Reset e configurações básicas */
13
- * {
14
- margin: 0;
15
- padding: 0;
16
- box-sizing: border-box;
17
- }
18
-
19
- body {
20
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
21
- background: var(--bg-primary);
22
- color: var(--text-primary);
23
- line-height: 1.5;
24
- overflow: hidden;
25
- }
26
-
27
- /* Variáveis para tema claro e escuro */
28
  :root {
29
- --bg-primary: #ffffff;
30
- --bg-secondary: #f4f4f5;
31
- --bg-input: #f9fafb;
32
- --text-primary: #111827;
33
- --text-secondary: #6b7280;
34
- --accent: #3b82f6;
35
- --accent-hover: #2563eb;
36
- --border: #e5e7eb;
37
- --message-user-bg: #e0f2fe;
38
- --message-bot-bg: #f3f4f6;
39
- }
40
-
41
- @media (prefers-color-scheme: dark) {
42
- :root {
43
- --bg-primary: #0f0f23;
44
- --bg-secondary: #1f1f38;
45
- --bg-input: #2d2d4a;
46
- --text-primary: #ffffff;
47
- --text-secondary: #9ca3af;
48
- --accent: #60a5fa;
49
- --accent-hover: #3b82f6;
50
- --border: #2d2d4a;
51
- --message-user-bg: #1e40af;
52
- --message-bot-bg: #1f2937;
53
- }
54
- }
55
-
56
- .gradio-container {
57
- background: var(--bg-primary) !important;
58
- min-height: 100vh !important;
59
- max-width: 100% !important;
60
- padding: 0 !important;
61
- margin: 0 !important;
62
- overflow: hidden !important;
63
  }
64
-
65
- /* Layout principal */
66
  .main-container {
67
  display: flex;
68
  height: 100vh;
69
- max-height: 100vh;
70
- background: var(--bg-primary);
71
- overflow: hidden;
72
  }
73
-
74
- /* Sidebar */
75
  .sidebar {
76
  width: 280px;
77
- min-width: 280px;
78
- background: var(--bg-secondary);
79
- border-right: 1px solid var(--border);
80
  padding: 16px;
81
- overflow-y: auto;
82
- height: 100vh;
83
- flex-shrink: 0;
84
- }
85
-
86
- .sidebar-header {
87
  display: flex;
88
- align-items: center;
89
- justify-content: space-between;
90
- padding: 12px 0;
91
- margin-bottom: 16px;
92
- border-bottom: 1px solid var(--border);
93
- }
94
-
95
- .new-chat-btn {
96
- background: var(--accent) !important;
97
- color: #ffffff !important;
98
- border: none !important;
99
- border-radius: 8px !important;
100
- padding: 8px 16px !important;
101
- font-size: 14px !important;
102
- font-weight: 500 !important;
103
- cursor: pointer !important;
104
- transition: background 0.2s ease !important;
105
- }
106
-
107
- .new-chat-btn:hover {
108
- background: var(--accent-hover) !important;
109
  }
110
-
111
- .sidebar-section h3 {
112
  color: var(--text-secondary);
113
  font-size: 12px;
114
- font-weight: 600;
115
  text-transform: uppercase;
 
116
  letter-spacing: 0.5px;
117
- margin-bottom: 12px;
118
  }
119
-
120
- /* Área principal */
 
 
 
 
 
 
 
 
 
 
121
  .chat-main {
122
  flex: 1;
123
  display: flex;
124
  flex-direction: column;
125
- background: var(--bg-primary);
126
  height: 100vh;
127
- overflow: hidden;
128
- }
129
-
130
- .chat-header {
131
- background: var(--bg-secondary);
132
- border-bottom: 1px solid var(--border);
133
- padding: 12px 24px;
134
- display: flex;
135
- align-items: center;
136
- justify-content: space-between;
137
- flex-shrink: 0;
138
- height: 60px;
139
  }
140
-
141
- .chat-title {
142
- color: var(--text-primary);
143
- font-size: 16px;
144
- font-weight: 600;
145
- }
146
-
147
- .chat-title a {
148
- color: var(--accent);
149
- text-decoration: none;
150
- }
151
-
152
- .chat-title a:hover {
153
- text-decoration: underline;
154
- }
155
-
156
- /* Área de mensagens */
157
  .messages-container {
158
- flex: 1;
159
- min-height: 0;
160
- overflow: hidden;
161
- position: relative;
162
- }
163
-
164
- .chatbot {
165
- flex: 1;
166
- min-height: 0;
167
- height: 100%;
168
- overflow: hidden;
169
- background: transparent !important;
170
- border: none !important;
171
- padding: 0 !important;
172
- margin: 0 !important;
173
- }
174
-
175
- .chatbot > div {
176
- height: 100%;
177
- overflow-y: auto;
178
- padding: 16px 24px;
179
- scroll-behavior: smooth;
180
  }
 
 
 
 
181
 
182
- /* Área de input */
183
  .input-area {
184
- background: var(--bg-primary);
185
  padding: 16px 24px;
186
- border-top: 1px solid var(--border);
187
- flex-shrink: 0;
188
- height: 100px;
189
- max-height: 100px;
190
  }
191
-
192
  .input-container {
193
  max-width: 800px;
194
  margin: 0 auto;
195
  display: flex;
196
- align-items: center;
197
- }
198
-
199
- .input-wrapper {
200
- background: var(--bg-input);
201
- border: 1px solid var(--border);
202
- border-radius: 12px;
203
- padding: 12px 16px;
204
- display: flex;
205
- align-items: center;
206
  gap: 12px;
207
- width: 100%;
 
 
 
208
  transition: border-color 0.2s ease;
209
  }
210
-
211
- .input-wrapper:focus-within {
212
- border-color: var(--accent);
213
- box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
214
- }
215
-
216
  #entrada_usuario textarea {
217
  background: transparent !important;
218
  border: none !important;
@@ -220,468 +120,200 @@ body {
220
  font-size: 16px !important;
221
  line-height: 1.5 !important;
222
  resize: none !important;
223
- outline: none !important;
224
- min-height: 24px !important;
225
- max-height: 48px !important;
226
- height: 24px !important;
227
- padding: 0 !important;
228
- margin: 0 !important;
229
  overflow-y: auto !important;
230
- width: 100% !important;
231
- }
232
-
233
- #entrada_usuario textarea::placeholder {
234
- color: var(--text-secondary) !important;
235
  }
236
-
237
  .send-button {
238
- width: 36px;
239
- height: 36px;
240
- background: var(--accent) !important;
241
- border: none !important;
242
- border-radius: 8px !important;
243
- color: #ffffff !important;
244
- cursor: pointer !important;
245
- display: flex;
246
- align-items: center;
247
- justify-content: center;
248
- transition: background 0.2s ease;
249
- }
250
-
251
- .send-button:hover:not(:disabled) {
252
- background: var(--accent-hover) !important;
253
- }
254
-
255
- .send-button:disabled {
256
- background: var(--text-secondary) !important;
257
- cursor: not-allowed !important;
258
- }
259
-
260
- /* Estilo das mensagens */
261
- .message {
262
- padding: 12px 0;
263
- border-bottom: 1px solid var(--border);
264
- animation: fadeIn 0.3s ease-out;
265
- }
266
-
267
- .message-content {
268
- max-width: 800px;
269
- margin: 0 auto;
270
- padding: 0 24px;
271
- display: flex;
272
- gap: 12px;
273
- align-items: flex-start;
274
- }
275
-
276
- .message-avatar {
277
- width: 36px;
278
- height: 36px;
279
- border-radius: 50%;
280
- flex-shrink: 0;
281
- display: flex;
282
- align-items: center;
283
- justify-content: center;
284
- font-size: 18px;
285
- font-weight: 600;
286
- }
287
-
288
- .user-avatar {
289
- background: var(--accent);
290
- color: #ffffff;
291
- }
292
-
293
- .bot-avatar {
294
- background: #6b7280;
295
- color: #ffffff;
296
- }
297
-
298
- .message-text {
299
- flex: 1;
300
- line-height: 1.6;
301
- font-size: 16px;
302
- color: var(--text-primary);
303
- }
304
-
305
- .message.user {
306
- background: var(--message-user-bg);
307
- }
308
-
309
- .message.assistant {
310
- background: var(--message-bot-bg);
311
- }
312
-
313
- /* Estilo para código */
314
- .message pre {
315
- background: var(--bg-secondary) !important;
316
- border: 1px solid var(--border) !important;
317
- border-radius: 8px !important;
318
- padding: 12px !important;
319
- margin: 12px 0 !important;
320
- overflow-x: auto !important;
321
- font-family: 'JetBrains Mono', monospace !important;
322
- font-size: 14px !important;
323
- }
324
-
325
- .message code {
326
- background: rgba(0, 0, 0, 0.05) !important;
327
- padding: 2px 6px !important;
328
- border-radius: 4px !important;
329
- font-family: 'JetBrains Mono', monospace !important;
330
- }
331
-
332
- .message pre code {
333
- background: transparent !important;
334
- padding: 0 !important;
335
- }
336
-
337
- /* Links */
338
- .message a {
339
- color: var(--accent) !important;
340
- text-decoration: none !important;
341
- }
342
-
343
- .message a:hover {
344
- text-decoration: underline !important;
345
- }
346
-
347
- /* Listas */
348
- .message ul, .message ol {
349
- margin: 12px 0 !important;
350
- padding-left: 24px !important;
351
- }
352
-
353
- .message li {
354
- margin: 6px 0 !important;
355
- }
356
-
357
- /* Tabelas */
358
- .message table {
359
- border-collapse: collapse !important;
360
- width: 100% !important;
361
- margin: 16px 0 !important;
362
- background: var(--bg-secondary) !important;
363
- border-radius: 8px !important;
364
- }
365
-
366
- .message th, .message td {
367
- border: 1px solid var(--border) !important;
368
- padding: 12px !important;
369
- text-align: left !important;
370
- }
371
-
372
- .message th {
373
- background: rgba(0, 0, 0, 0.05) !important;
374
- font-weight: 600 !important;
375
- }
376
-
377
- /* Scrollbar */
378
- ::-webkit-scrollbar {
379
- width: 6px;
380
- }
381
-
382
- ::-webkit-scrollbar-track {
383
- background: var(--bg-secondary);
384
- }
385
-
386
- ::-webkit-scrollbar-thumb {
387
- background: var(--text-secondary);
388
- border-radius: 3px;
389
- }
390
-
391
- ::-webkit-scrollbar-thumb:hover {
392
- background: var(--accent);
393
- }
394
 
395
  /* Responsividade */
396
  @media (max-width: 768px) {
397
- .main-container {
398
- flex-direction: column;
399
- }
400
-
401
  .sidebar {
402
  width: 100%;
403
- height: 180px;
404
- max-height: 180px;
405
  border-right: none;
406
- border-bottom: 1px solid var(--border);
407
- }
408
-
409
- .chat-main {
410
- height: calc(100vh - 180px);
411
  }
412
-
413
- .messages-container {
414
- max-height: calc(100vh - 280px);
415
- }
416
-
417
- .input-area {
418
- padding: 12px 16px;
419
- height: 80px;
420
- max-height: 80px;
421
- }
422
-
423
- .message-content {
424
- padding: 0 16px;
425
- gap: 10px;
426
- }
427
-
428
- .message-avatar {
429
- width: 32px;
430
- height: 32px;
431
- font-size: 16px;
432
- }
433
-
434
- .message-text {
435
- font-size: 15px;
436
- }
437
- }
438
-
439
- /* Animações */
440
- @keyframes fadeIn {
441
- from { opacity: 0; transform: translateY(8px); }
442
- to { opacity: 1; transform: translateY(0); }
443
- }
444
-
445
- /* Loading */
446
- .loading-indicator {
447
- display: flex;
448
- align-items: center;
449
- gap: 6px;
450
- color: var(--text-secondary);
451
- font-style: italic;
452
- }
453
-
454
- .loading-dots span {
455
- width: 5px;
456
- height: 5px;
457
- background: var(--text-secondary);
458
- border-radius: 50%;
459
- animation: pulse 1.2s ease-in-out infinite;
460
- }
461
-
462
- .loading-dots span:nth-child(1) { animation-delay: -0.24s; }
463
- .loading-dots span:nth-child(2) { animation-delay: -0.12s; }
464
- .loading-dots span:nth-child(3) { animation-delay: 0s; }
465
-
466
- @keyframes pulse {
467
- 0%, 80%, 100% { opacity: 0.3; }
468
- 40% { opacity: 1; }
469
- }
470
-
471
- /* Gradio components */
472
- .gr-textbox, .gr-dropdown {
473
- background: transparent !important;
474
- border: none !important;
475
- color: var(--text-primary) !important;
476
- }
477
-
478
- .gr-dropdown {
479
- background: var(--bg-input) !important;
480
- border: 1px solid var(--border) !important;
481
- border-radius: 8px !important;
482
- }
483
-
484
- .gr-accordion {
485
- background: transparent !important;
486
- border: 1px solid var(--border) !important;
487
- border-radius: 8px !important;
488
- }
489
-
490
- .gr-accordion summary {
491
- background: var(--bg-input) !important;
492
- color: var(--text-primary) !important;
493
- padding: 12px !important;
494
- border-radius: 8px !important;
495
- }
496
-
497
- .gr-accordion[open] summary {
498
- border-bottom: 1px solid var(--border) !important;
499
- border-radius: 8px 8px 0 0 !important;
500
- }
501
-
502
- .gr-accordion .gr-panel {
503
- background: var(--bg-secondary) !important;
504
- padding: 12px !important;
505
- border-radius: 0 0 8px 8px !important;
506
- }
507
  """
508
 
509
  def criar_interface():
510
- with gr.Blocks(title="Dr. Aldo Henrique - iAldo AI Assistant", theme=gr.themes.Base(), css=css_customizado) as interface:
511
- session_id_state = gr.State(str(uuid.uuid4()))
512
 
513
  with gr.Row(elem_classes="main-container"):
514
- # Sidebar
515
- with gr.Column(elem_classes="sidebar", scale=0, min_width=280):
516
- gr.HTML("""
517
- <div class="sidebar-header">
518
- <h2 style="color: var(--accent); font-size: 20px; font-weight: 600;">iAldo AI</h2>
519
- <button class="new-chat-btn">Novo Chat</button>
520
- </div>
521
- """)
522
-
523
  with gr.Group():
524
- gr.HTML('<h3>🧠 Modelo de IA</h3>')
 
 
 
 
525
  modelo_select = gr.Dropdown(
526
  choices=list(MODELS.keys()),
527
- value="Llama 3.2 3B",
528
- label="",
529
- show_label=False,
530
- elem_classes="model-selector",
531
  container=False
532
  )
533
 
534
- with gr.Accordion("🔧 Status API", open=False, elem_classes="sidebar-section"):
535
- status_api = gr.Textbox(label="", interactive=False, lines=4, show_label=False,
536
- value="✅ Modelos carregados com sucesso!")
537
-
538
- with gr.Accordion("ℹ️ Sobre", open=False, elem_classes="sidebar-section"):
539
  gr.Markdown("""
540
- **Dr. Aldo Henrique**
541
-
542
- Especialista em C/C++, Java
543
- • Desenvolvimento Web & IA
544
- • Conteúdo: aldohenrique.com.br
545
-
546
- **Dicas:**
547
- • Faça perguntas específicas
548
- • Use o RAG para consultar o blog
549
- • Experimente diferentes modelos
550
  """)
551
 
552
- # Área principal
553
- with gr.Column(elem_classes="chat-main", scale=1):
554
- gr.HTML("""
555
- <div class="chat-header">
556
- <div class="chat-title">
557
- 🤖 Conversando com <a href="https://aldohenrique.com.br/" target="_blank">Prof. Dr. Aldo Henrique</a>
558
- </div>
559
- </div>
560
- """)
561
-
562
  with gr.Column(elem_classes="messages-container"):
563
  chatbot = gr.Chatbot(
564
- label="",
 
565
  elem_id="chat",
566
  show_label=False,
567
  container=False,
568
- elem_classes="chatbot",
569
  avatar_images=("https://cdn-icons-png.flaticon.com/256/9055/9055398.png",
570
  "https://cdn.iconscout.com/icon/premium/png-256-thumb/robo-97-415007.png"),
571
  bubble_full_width=False,
572
  height="100%"
573
  )
574
 
 
575
  with gr.Column(elem_classes="input-area"):
576
  with gr.Row(elem_classes="input-container"):
577
- with gr.Column(elem_classes="input-wrapper", scale=1):
578
- user_input = gr.Textbox(
579
- show_label=False,
580
- placeholder="Digite sua mensagem aqui...",
581
- lines=1,
582
- elem_id="entrada_usuario",
583
- max_lines=2,
584
- container=False,
585
- scale=1
586
- )
587
- enviar_btn = gr.Button("➤", variant="primary", size="sm", elem_classes="send-button")
588
-
589
  def responder(chat_history, user_msg, modelo, session_id):
590
  if not user_msg.strip():
591
  return chat_history, ""
592
-
593
- chat_history = chat_history + [[user_msg, "🤔 Dr. Aldo está pensando..."]]
594
- yield chat_history, ""
595
 
596
  try:
597
- resposta_final = responder_como_aldo(session_id, user_msg, modelo)
598
- chat_history[-1][1] = resposta_final
599
- yield chat_history, ""
600
 
 
 
 
 
 
 
 
601
  except Exception as e:
602
  chat_history[-1][1] = f"❌ Desculpe, ocorreu um erro: {str(e)}"
603
  yield chat_history, ""
604
 
605
- enviar_btn.click(
 
 
 
 
 
606
  fn=responder,
607
  inputs=[chatbot, user_input, modelo_select, session_id_state],
608
- outputs=[chatbot, user_input],
609
- show_progress=False
610
  )
611
-
612
- user_input.submit(
613
  fn=responder,
614
  inputs=[chatbot, user_input, modelo_select, session_id_state],
615
- outputs=[chatbot, user_input],
616
- show_progress=False
 
 
 
 
617
  )
618
 
619
- gr.HTML("""
620
- <script>
621
- (function() {
622
- let initialized = false;
623
-
624
- function initLayout() {
625
- if (initialized) return;
626
- initialized = true;
627
-
628
- const mainContainer = document.querySelector('.main-container');
629
- if (mainContainer) {
630
- mainContainer.style.height = '100vh';
631
- mainContainer.style.maxHeight = '100vh';
632
- mainContainer.style.overflow = 'hidden';
633
  }
 
 
 
 
 
634
 
635
- const chatMain = document.querySelector('.chat-main');
636
- if (chatMain) {
637
- chatMain.style.height = '100vh';
638
- chatMain.style.maxHeight = '100vh';
639
- chatMain.style.display = 'flex';
640
- chatMain.style.flexDirection = 'column';
641
  }
642
-
643
- const messagesContainer = document.querySelector('.messages-container');
644
- if (messagesContainer) {
645
- messagesContainer.style.flex = '1';
646
- messagesContainer.style.minHeight = '0';
647
- messagesContainer.style.overflow = 'hidden';
 
 
 
 
 
 
 
 
 
 
 
 
648
  }
649
 
650
- const textarea = document.querySelector("#entrada_usuario textarea");
651
- if (textarea) {
652
- textarea.style.height = "24px";
653
- textarea.style.minHeight = "24px";
654
- textarea.style.maxHeight = "48px";
655
- textarea.style.resize = "none";
656
- textarea.style.overflowY = "hidden";
657
-
658
- function autoResize() {
659
- textarea.style.height = "24px";
660
- const newHeight = Math.min(textarea.scrollHeight, 48);
661
- textarea.style.height = Math.max(newHeight, 24) + "px";
662
- textarea.style.overflowY = textarea.scrollHeight > 48 ? "auto" : "hidden";
663
- }
664
-
665
- textarea.addEventListener('input', autoResize);
666
- textarea.addEventListener('paste', () => setTimeout(autoResize, 0));
667
- setTimeout(() => textarea.focus(), 300);
668
- }
669
- }
670
-
671
- if (document.readyState === 'loading') {
672
- document.addEventListener('DOMContentLoaded', initLayout);
673
- } else {
674
- setTimeout(initLayout, 100);
675
  }
676
-
677
- setTimeout(initLayout, 1000);
678
- })();
679
- </script>
680
- """)
681
 
682
  return interface
683
 
 
684
  def inicializar_sistema_sync():
 
685
  try:
686
  import asyncio
687
  try:
@@ -700,12 +332,15 @@ def inicializar_sistema_sync():
700
  return False, {}
701
 
702
  def configurar_interface():
 
 
 
703
  try:
704
  status, available_models = inicializar_sistema()
705
  if status:
706
  return criar_interface()
707
  else:
708
- error_msg = f"Sistema não inicializado: menos de 3 modelos disponíveis. Modelos: {', '.join(available_models.keys()) if available_models else 'Nenhum'}"
709
  raise RuntimeError(error_msg)
710
  except Exception as e:
711
  print(f"Erro na configuração da interface: {e}")
 
8
  inicializar_sistema
9
  )
10
 
11
+ # CSS Aprimorado: mais limpo, focado em Flexbox e responsividade.
12
  css_customizado = """
13
+ /* Reset Básico e Tema Escuro */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  :root {
15
+ --bg-primary: #0F172A; /* Azul escuro principal */
16
+ --bg-secondary: #1E293B; /* Azul um pouco mais claro */
17
+ --bg-tertiary: #334155; /* Componentes de input, etc. */
18
+ --border-color: #475569;
19
+ --text-primary: #F1F5F9; /* Branco suave */
20
+ --text-secondary: #94A3B8; /* Cinza claro */
21
+ --accent-color: #2563EB; /* Azul vibrante */
22
+ --accent-hover: #1D4ED8;
23
+ }
24
+ * { box-sizing: border-box; margin: 0; padding: 0; }
25
+ body, .gradio-container {
26
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
27
+ background-color: var(--bg-primary) !important;
28
+ color: var(--text-primary) !important;
29
+ height: 100vh;
30
+ overflow: hidden; /* Impede o scroll no body */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  }
32
+ /* Layout Principal com Flexbox */
 
33
  .main-container {
34
  display: flex;
35
  height: 100vh;
36
+ width: 100%;
 
 
37
  }
38
+ /* --- Sidebar --- */
 
39
  .sidebar {
40
  width: 280px;
41
+ background-color: var(--bg-secondary);
42
+ border-right: 1px solid var(--border-color);
 
43
  padding: 16px;
 
 
 
 
 
 
44
  display: flex;
45
+ flex-direction: column;
46
+ gap: 24px;
47
+ height: 100%;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
+ .sidebar-header { text-align: center; margin-bottom: 16px; font-size: 24px; font-weight: 600; }
50
+ .sidebar-section-title {
51
  color: var(--text-secondary);
52
  font-size: 12px;
53
+ font-weight: 500;
54
  text-transform: uppercase;
55
+ margin-bottom: 8px;
56
  letter-spacing: 0.5px;
 
57
  }
58
+ .new-chat-btn {
59
+ width: 100%;
60
+ background-color: var(--accent-color) !important;
61
+ color: white !important;
62
+ border: none !important;
63
+ border-radius: 8px !important;
64
+ padding: 10px !important;
65
+ font-weight: 500 !important;
66
+ transition: background-color 0.2s ease;
67
+ }
68
+ .new-chat-btn:hover { background-color: var(--accent-hover) !important; }
69
+ /* --- Área do Chat --- */
70
  .chat-main {
71
  flex: 1;
72
  display: flex;
73
  flex-direction: column;
 
74
  height: 100vh;
75
+ overflow: hidden; /* Impede overflow */
 
 
 
 
 
 
 
 
 
 
 
76
  }
77
+ /* Container de Mensagens (com scroll) */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  .messages-container {
79
+ flex: 1; /* Ocupa todo o espaço vertical disponível */
80
+ overflow-y: auto; /* Apenas este container terá scroll */
81
+ padding: 24px;
82
+ background-color: var(--bg-primary);
83
+ }
84
+ #chat { min-height: 100%; } /* Garante que o chatbot preencha o container */
85
+ #chat .message-bubble {
86
+ background-color: transparent !important;
87
+ box-shadow: none !important;
88
+ border-radius: 12px !important;
89
+ padding: 12px !important;
90
+ max-width: 90% !important;
 
 
 
 
 
 
 
 
 
 
91
  }
92
+ #chat .message-bubble.user { background-color: var(--bg-secondary) !important; }
93
+ #chat .message-bubble.bot { background-color: transparent !important; } /* Apenas texto */
94
+ #chat .prose { color: var(--text-primary) !important; font-size: 16px; line-height: 1.6; }
95
+ #chat .avatar-container img { border-radius: 50% !important; }
96
 
97
+ /* Área de Input */
98
  .input-area {
 
99
  padding: 16px 24px;
100
+ border-top: 1px solid var(--border-color);
101
+ background-color: var(--bg-primary);
 
 
102
  }
 
103
  .input-container {
104
  max-width: 800px;
105
  margin: 0 auto;
106
  display: flex;
107
+ align-items: flex-end;
 
 
 
 
 
 
 
 
 
108
  gap: 12px;
109
+ background-color: var(--bg-secondary);
110
+ border: 1px solid var(--border-color);
111
+ border-radius: 16px;
112
+ padding: 8px 8px 8px 16px;
113
  transition: border-color 0.2s ease;
114
  }
115
+ .input-container:focus-within { border-color: var(--accent-color); }
 
 
 
 
 
116
  #entrada_usuario textarea {
117
  background: transparent !important;
118
  border: none !important;
 
120
  font-size: 16px !important;
121
  line-height: 1.5 !important;
122
  resize: none !important;
123
+ padding: 8px 0 !important;
124
+ max-height: 200px !important; /* Limita o crescimento */
 
 
 
 
125
  overflow-y: auto !important;
 
 
 
 
 
126
  }
127
+ #entrada_usuario textarea::placeholder { color: var(--text-secondary) !important; }
128
  .send-button {
129
+ background: var(--accent-color) !important;
130
+ color: white !important;
131
+ border-radius: 10px !important;
132
+ width: 40px !important;
133
+ height: 40px !important;
134
+ min-width: 40px !important;
135
+ transition: background-color 0.2s ease;
136
+ align-self: flex-end; /* Alinha o botão na parte inferior */
137
+ }
138
+ .send-button:hover:not(:disabled) { background: var(--accent-hover) !important; }
139
+ .send-button:disabled { background: var(--bg-tertiary) !important; cursor: not-allowed !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
  /* Responsividade */
142
  @media (max-width: 768px) {
143
+ .main-container { flex-direction: column; }
 
 
 
144
  .sidebar {
145
  width: 100%;
146
+ height: auto;
 
147
  border-right: none;
148
+ border-bottom: 1px solid var(--border-color);
149
+ flex-direction: row; /* Itens da sidebar em linha */
150
+ flex-wrap: wrap; /* Quebra de linha se não couber */
151
+ align-items: center;
 
152
  }
153
+ .sidebar-header, .sidebar-section { flex: 1; }
154
+ .chat-main { height: calc(100% - auto); } /* Ajusta a altura */
155
+ .messages-container { padding: 16px; }
156
+ .input-area { padding: 12px; }
157
+ #chat .message-bubble { max-width: 95% !important; }
158
+ }
159
+ /* Ocultar elementos desnecessários do Gradio */
160
+ footer { display: none !important; }
161
+ .gr-button-lg { display: none !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  """
163
 
164
  def criar_interface():
165
+ with gr.Blocks(theme=gr.themes.Base(), css=css_customizado, title="iAldo AI Assistant") as interface:
166
+ session_id_state = gr.State(lambda: str(uuid.uuid4()))
167
 
168
  with gr.Row(elem_classes="main-container"):
169
+ # --- Sidebar (Painel de Controle) ---
170
+ with gr.Column(scale=0, min_width=280, elem_classes="sidebar"):
 
 
 
 
 
 
 
171
  with gr.Group():
172
+ gr.HTML("<div class='sidebar-header'>iAldo AI</div>")
173
+ novo_chat_btn = gr.Button("➕ Novo Chat", elem_classes="new-chat-btn")
174
+
175
+ with gr.Group(elem_classes="sidebar-section"):
176
+ gr.HTML("<p class='sidebar-section-title'>🧠 Modelo de IA</p>")
177
  modelo_select = gr.Dropdown(
178
  choices=list(MODELS.keys()),
179
+ value=DEFAULT_MODEL,
180
+ label=None, show_label=False,
 
 
181
  container=False
182
  )
183
 
184
+ with gr.Accordion("ℹ️ Sobre", open=False):
 
 
 
 
185
  gr.Markdown("""
186
+ **Assistente de IA do Prof. Dr. Aldo Henrique.**
187
+ - Especialista em C/C++, Java, Web & IA.
188
+ - [aldohenrique.com.br](https://aldohenrique.com.br)
 
 
 
 
 
 
 
189
  """)
190
 
191
+ # --- Área Principal do Chat ---
192
+ with gr.Column(scale=4, elem_classes="chat-main"):
193
+ # Container das mensagens (com scroll)
 
 
 
 
 
 
 
194
  with gr.Column(elem_classes="messages-container"):
195
  chatbot = gr.Chatbot(
196
+ [],
197
+ label="iAldo",
198
  elem_id="chat",
199
  show_label=False,
200
  container=False,
 
201
  avatar_images=("https://cdn-icons-png.flaticon.com/256/9055/9055398.png",
202
  "https://cdn.iconscout.com/icon/premium/png-256-thumb/robo-97-415007.png"),
203
  bubble_full_width=False,
204
  height="100%"
205
  )
206
 
207
+ # Container do input
208
  with gr.Column(elem_classes="input-area"):
209
  with gr.Row(elem_classes="input-container"):
210
+ user_input = gr.Textbox(
211
+ show_label=False,
212
+ placeholder="Pergunte algo ao Dr. Aldo...",
213
+ elem_id="entrada_usuario",
214
+ container=False,
215
+ scale=10 # Dá mais espaço para o textbox
216
+ )
217
+ enviar_btn = gr.Button("➤", variant="primary", elem_classes="send-button", scale=1, min_width=40)
218
+
219
+ # --- Lógica de Backend (sem alterações) ---
 
 
220
  def responder(chat_history, user_msg, modelo, session_id):
221
  if not user_msg.strip():
222
  return chat_history, ""
223
+
224
+ chat_history.append([user_msg, None])
225
+ yield chat_history, "" # Limpa o input imediatamente
226
 
227
  try:
228
+ resposta_stream = responder_como_aldo(session_id, user_msg, modelo)
 
 
229
 
230
+ # Resposta em streaming para melhor UX
231
+ resposta_completa = ""
232
+ for chunk in resposta_stream:
233
+ resposta_completa += chunk
234
+ chat_history[-1][1] = resposta_completa
235
+ yield chat_history, ""
236
+
237
  except Exception as e:
238
  chat_history[-1][1] = f"❌ Desculpe, ocorreu um erro: {str(e)}"
239
  yield chat_history, ""
240
 
241
+ # Função para limpar o chat
242
+ def limpar_chat():
243
+ return [], str(uuid.uuid4())
244
+
245
+ # --- Eventos ---
246
+ user_input.submit(
247
  fn=responder,
248
  inputs=[chatbot, user_input, modelo_select, session_id_state],
249
+ outputs=[chatbot, user_input]
 
250
  )
251
+ enviar_btn.click(
 
252
  fn=responder,
253
  inputs=[chatbot, user_input, modelo_select, session_id_state],
254
+ outputs=[chatbot, user_input]
255
+ )
256
+ novo_chat_btn.click(
257
+ fn=limpar_chat,
258
+ inputs=[],
259
+ outputs=[chatbot, session_id_state]
260
  )
261
 
262
+ # --- JavaScript para Melhorar a UX ---
263
+ interface.load(
264
+ None,
265
+ None,
266
+ None,
267
+ js="""
268
+ () => {
269
+ // Função para rolar o chat para o final
270
+ function scrollChatToBottom() {
271
+ const chatContainer = document.querySelector('.messages-container');
272
+ if (chatContainer) {
273
+ chatContainer.scrollTop = chatContainer.scrollHeight;
274
+ }
 
275
  }
276
+
277
+ // Observador para rolar automaticamente quando novas mensagens aparecem
278
+ const observer = new MutationObserver((mutations) => {
279
+ scrollChatToBottom();
280
+ });
281
 
282
+ const chatElement = document.getElementById('chat');
283
+ if(chatElement) {
284
+ observer.observe(chatElement, { childList: true, subtree: true });
 
 
 
285
  }
286
+
287
+ // Lida com o envio via "Enter" e o crescimento dinâmico da textarea
288
+ const userInput = document.getElementById('entrada_usuario').querySelector('textarea');
289
+ if (userInput) {
290
+ userInput.addEventListener('keydown', (e) => {
291
+ if (e.key === 'Enter' && !e.shiftKey) {
292
+ e.preventDefault(); // Impede a quebra de linha
293
+ // Clica no botão de enviar do Gradio (que pode estar oculto)
294
+ const sendButton = document.querySelector('.send-button');
295
+ if (sendButton) sendButton.click();
296
+ }
297
+ });
298
+
299
+ // Faz a textarea crescer com o conteúdo
300
+ userInput.addEventListener('input', () => {
301
+ userInput.style.height = 'auto'; // Reseta a altura
302
+ userInput.style.height = (userInput.scrollHeight) + 'px';
303
+ });
304
  }
305
 
306
+ // Garante a rolagem inicial
307
+ scrollChatToBottom();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
  }
309
+ """
310
+ )
 
 
 
311
 
312
  return interface
313
 
314
+ # --- Código de inicialização (sem alterações) ---
315
  def inicializar_sistema_sync():
316
+ """Função síncrona para compatibilidade"""
317
  try:
318
  import asyncio
319
  try:
 
332
  return False, {}
333
 
334
  def configurar_interface():
335
+ """
336
+ Configura a interface Gradio apenas se o sistema for inicializado com sucesso.
337
+ """
338
  try:
339
  status, available_models = inicializar_sistema()
340
  if status:
341
  return criar_interface()
342
  else:
343
+ error_msg = f"Sistema não inicializado: Modelos indisponíveis. Modelos: {', '.join(available_models.keys()) if available_models else 'Nenhum'}"
344
  raise RuntimeError(error_msg)
345
  except Exception as e:
346
  print(f"Erro na configuração da interface: {e}")