victorafarias commited on
Commit
f144369
·
1 Parent(s): fe80f26

Evoluções Gerais

Browse files
Files changed (1) hide show
  1. templates/index.html +163 -30
templates/index.html CHANGED
@@ -10,12 +10,12 @@
10
  .convert-btn {
11
  padding: 5px 10px;
12
  font-size: 12px;
13
- background-color: #17a2b8; /* Cor Ciano/Azul claro */
14
  color: white;
15
  border: none;
16
  border-radius: 4px;
17
  cursor: pointer;
18
- margin-left: 5px; /* Espaço entre os botões */
19
  }
20
  .convert-btn:hover {
21
  background-color: #138496;
@@ -33,25 +33,80 @@
33
  }
34
 
35
  .results-container {
36
- display: none; /* Será alterado via JavaScript */
37
  flex-direction: row;
38
  gap: 10px;
39
  margin-bottom: 0;
40
  }
41
 
42
  #final-result-container {
43
- display: none; /* Será alterado via JavaScript */
44
  width: 100%;
45
  margin-top: 20px;
46
  }
47
 
48
- /* Garantir que as 3 colunas principais mantenham o layout original */
49
  .results-container .result-column {
50
  flex: 1;
51
  min-width: 0;
52
  }
53
-
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  </style>
56
  </head>
57
  <body>
@@ -60,6 +115,7 @@
60
  <div class="loader-spinner"></div>
61
  <p id="loader-message">Processando sua solicitação...</p>
62
  <div class="progress-bar-container"><div id="progress-bar" class="progress-bar"></div></div>
 
63
  </div>
64
  </div>
65
  <button id="merge-btn" class="floating-merge-btn" style="display: none;">Processar Merge</button>
@@ -70,7 +126,7 @@
70
  <p id="flow-description">GROK ➔ Claude Sonnet ➔ Gemini</p>
71
  </div>
72
  <div class="controls-container">
73
- <div class="mode-toggle" title="A versão 'Hierárquica' gerará um único no texto que passará por revisão em duas instâncias. Na versão 'Atômica', serão gerados 3 textos, um em cada modelo de IA; e depois um 4º texto será gerado fazendo um texto final consolidado dessas 3 versões.">
74
  <span>Hierárquico</span>
75
  <label class="switch"><input type="checkbox" id="processing-mode-switch"><span class="slider round"></span></label>
76
  <span>Atômico</span>
@@ -89,6 +145,22 @@
89
  <label for="solicitacao_usuario">Digite sua solicitação (ou arraste arquivos aqui):</label>
90
  <textarea name="solicitacao_usuario" id="solicitacao_usuario" rows="8" required></textarea>
91
  <div id="file-list-container"><p>Arquivos Anexados:</p><ul id="file-list"></ul></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  <button type="submit">Processar com IA</button>
93
  </form>
94
  </div>
@@ -153,6 +225,7 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
153
  </div>
154
  </div>
155
  </div>
 
156
  <script>
157
  // --- Variáveis Globais ---
158
  const processingModeSwitch = document.getElementById('processing-mode-switch');
@@ -169,9 +242,11 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
169
  const mergeBtn = document.getElementById('merge-btn');
170
  const finalResultContainer = document.getElementById('final-result-container');
171
  const finalOutput = document.getElementById('final-output');
 
172
  let attachedFiles = [];
173
  let originalUserQuery = "";
174
- let rawTexts = {}; // NOVO: Objeto para guardar os textos brutos
 
175
 
176
  // Log para debug
177
  function debugLog(message) {
@@ -183,8 +258,56 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
183
  realContainer.style.display = this.checked ? 'none' : 'block';
184
  mockContainer.style.display = this.checked ? 'block' : 'none';
185
  });
 
186
  processingModeSwitch.addEventListener('change', function() {
187
- document.getElementById('flow-description').textContent = this.checked ? "GROK | Claude Sonnet | Gemini (Paralelo)" : "GROK ➔ Claude Sonnet ➔ Gemini";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  });
189
 
190
  // --- Lógica de Upload ---
@@ -228,6 +351,7 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
228
  async function handleFormSubmit(event) {
229
  event.preventDefault();
230
  debugLog("=== FORM SUBMIT INICIADO ===");
 
231
 
232
  // Resetar a interface
233
  errorContainer.innerHTML = '';
@@ -239,18 +363,24 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
239
  btn.disabled = false;
240
  btn.innerText = 'Converter para MD';
241
  });
242
- rawTexts = {}; // Limpa os textos brutos
243
  debugLog("Interface resetada");
244
 
245
  // Iniciar o loader
246
  loaderMessage.textContent = 'Iniciando conexão...';
247
  progressBar.style.width = '0%';
248
  loader.style.display = 'flex';
 
 
249
  debugLog("Loader iniciado");
250
 
251
  const formData = new FormData();
252
  formData.append('processing_mode', processingModeSwitch.checked ? 'atomic' : 'hierarchical');
253
 
 
 
 
 
254
  if (modeSwitch.checked) {
255
  formData.append('mode', 'test');
256
  formData.append('mock_text', document.getElementById('mock_text').value);
@@ -272,7 +402,7 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
272
  debugLog("=== FETCH REALIZADO COM SUCESSO, INICIANDO STREAM ===");
273
  const reader = response.body.getReader();
274
  const decoder = new TextDecoder();
275
- let buffer = ''; // Buffer para dados incompletos
276
 
277
  while (true) {
278
  const { done, value } = await reader.read();
@@ -285,9 +415,8 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
285
  buffer += chunk;
286
  debugLog(`Chunk recebido: ${chunk.length} chars`);
287
 
288
- // Processa todas as linhas completas no buffer
289
  let lines = buffer.split('\n\n');
290
- buffer = lines.pop(); // Mantém a última linha (possivelmente incompleta) no buffer
291
 
292
  lines.forEach(line => {
293
  if (line.startsWith('data: ')) {
@@ -307,10 +436,8 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
307
  });
308
  }
309
 
310
- // Processa qualquer dado restante no buffer
311
  if (buffer.trim()) {
312
  debugLog(`Buffer final: ${buffer}`);
313
- // Tenta processar o buffer final como uma linha completa
314
  if (buffer.startsWith('data: ')) {
315
  const jsonData = buffer.substring(6).trim();
316
  if (jsonData) {
@@ -335,12 +462,15 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
335
  // --- Lógica do Botão de Merge ---
336
  mergeBtn.addEventListener('click', async function() {
337
  debugLog("=== MERGE BUTTON CLICADO ===");
 
 
338
  loaderMessage.textContent = 'Processando o merge dos textos...';
339
  progressBar.style.width = '0%';
340
  loader.style.display = 'flex';
 
 
341
  this.style.display = 'none';
342
 
343
- // Sempre pega o texto bruto para o merge
344
  const payload = {
345
  solicitacao_usuario: originalUserQuery,
346
  grok_text: rawTexts['grok-output'] || '',
@@ -360,7 +490,7 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
360
  debugLog("=== MERGE FETCH REALIZADO COM SUCESSO, INICIANDO STREAM ===");
361
  const reader = response.body.getReader();
362
  const decoder = new TextDecoder();
363
- let buffer = ''; // Buffer para dados incompletos do merge
364
 
365
  while (true) {
366
  const { done, value } = await reader.read();
@@ -373,9 +503,8 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
373
  buffer += chunk;
374
  debugLog(`Merge chunk recebido: ${chunk.length} chars`);
375
 
376
- // Processa todas as linhas completas no buffer
377
  let lines = buffer.split('\n\n');
378
- buffer = lines.pop(); // Mantém a última linha (possivelmente incompleta) no buffer
379
 
380
  lines.forEach(line => {
381
  if (line.startsWith('data: ')) {
@@ -385,7 +514,7 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
385
  try {
386
  const data = JSON.parse(jsonData);
387
  debugLog(`Merge dados processados: ${JSON.stringify({progress: data.progress, message: data.message, hasFinalResult: !!data.final_result, error: data.error})}`);
388
- processStreamData(data, true); // true = é merge
389
  } catch (e) {
390
  console.error("Erro ao parsear JSON do merge:", jsonData.substring(0, 500));
391
  console.error("Erro:", e);
@@ -395,7 +524,6 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
395
  });
396
  }
397
 
398
- // Processa qualquer dado restante no buffer do merge
399
  if (buffer.trim()) {
400
  debugLog(`Merge buffer final: ${buffer.substring(0, 200)}...`);
401
  if (buffer.startsWith('data: ')) {
@@ -452,9 +580,8 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
452
  return;
453
  }
454
 
455
- // Armazena o texto bruto e exibe na tela
456
  rawTexts[targetId] = content;
457
- targetBox.innerText = content; // Exibe texto bruto
458
  debugLog(`Conteúdo armazenado e exibido para: ${targetId}`);
459
 
460
  if (targetId === 'final-output') {
@@ -465,6 +592,15 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
465
  }
466
  finalResultContainer.style.display = 'block';
467
  debugLog("Final result container exibido");
 
 
 
 
 
 
 
 
 
468
  } else {
469
  resultsContainer.style.display = 'flex';
470
  debugLog("Results container exibido");
@@ -491,7 +627,7 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
491
  }
492
  }
493
 
494
- // --- Funções de Utilitários (ATUALIZADAS) ---
495
  function showError(message) {
496
  debugLog(`Exibindo erro: ${message}`);
497
  errorContainer.innerHTML = `<div class="error-box"><strong>Erro:</strong> ${message}<span class="close-btn-error" onclick="this.parentElement.style.display='none';" title="Fechar">&times;</span></div>`;
@@ -499,7 +635,6 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
499
 
500
  function copyToClipboard(elementId) {
501
  debugLog(`Copiando conteúdo de: ${elementId}`);
502
- // Sempre copia o texto bruto original do objeto rawTexts
503
  const textToCopy = rawTexts[elementId];
504
  if (textToCopy !== undefined) {
505
  navigator.clipboard.writeText(textToCopy).then(() => {
@@ -512,7 +647,6 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
512
  }
513
  }
514
 
515
- // NOVA FUNÇÃO para converter para Markdown sob demanda
516
  async function convertToMarkdown(elementId) {
517
  debugLog(`Convertendo markdown para: ${elementId}`);
518
  const button = event.target;
@@ -538,18 +672,17 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
538
 
539
  const data = await response.json();
540
  const targetBox = document.getElementById(elementId);
541
- targetBox.innerHTML = data.html; // Insere o HTML convertido
542
- button.innerText = 'Convertido'; // Mantém o botão desabilitado para indicar sucesso
543
  debugLog(`Markdown convertido com sucesso para ${elementId}`);
544
  } catch (error) {
545
  showError('Não foi possível converter o texto.');
546
  console.error('Conversion error:', error);
547
  debugLog(`Erro na conversão: ${error.message}`);
548
- button.innerText = 'Converter para MD'; // Reabilita em caso de erro
549
  button.disabled = false;
550
  }
551
  }
552
  </script>
553
-
554
  </body>
555
  </html>
 
10
  .convert-btn {
11
  padding: 5px 10px;
12
  font-size: 12px;
13
+ background-color: #17a2b8;
14
  color: white;
15
  border: none;
16
  border-radius: 4px;
17
  cursor: pointer;
18
+ margin-left: 5px;
19
  }
20
  .convert-btn:hover {
21
  background-color: #138496;
 
33
  }
34
 
35
  .results-container {
36
+ display: none;
37
  flex-direction: row;
38
  gap: 10px;
39
  margin-bottom: 0;
40
  }
41
 
42
  #final-result-container {
43
+ display: none;
44
  width: 100%;
45
  margin-top: 20px;
46
  }
47
 
 
48
  .results-container .result-column {
49
  flex: 1;
50
  min-width: 0;
51
  }
 
52
 
53
+ /* Estilos para os campos de tamanho do texto */
54
+ .text-size-controls {
55
+ display: flex;
56
+ gap: 15px;
57
+ margin: 10px 0;
58
+ align-items: center;
59
+ background-color: #f8f9fa;
60
+ padding: 15px;
61
+ border-radius: 5px;
62
+ border: 1px solid #e9ecef;
63
+ }
64
+
65
+ .text-size-field {
66
+ display: flex;
67
+ flex-direction: column;
68
+ gap: 5px;
69
+ }
70
+
71
+ .text-size-field label {
72
+ font-size: 12px;
73
+ color: #6c757d;
74
+ font-weight: bold;
75
+ }
76
+
77
+ .text-size-field input {
78
+ width: 80px;
79
+ padding: 5px 8px;
80
+ border: 1px solid #ced4da;
81
+ border-radius: 3px;
82
+ font-size: 14px;
83
+ }
84
+
85
+ /* Estilo para o botão de cancelar */
86
+ .cancel-btn {
87
+ background-color: #dc3545;
88
+ color: white;
89
+ border: none;
90
+ padding: 8px 15px;
91
+ border-radius: 4px;
92
+ cursor: pointer;
93
+ margin-top: 10px;
94
+ font-size: 14px;
95
+ }
96
+
97
+ .cancel-btn:hover {
98
+ background-color: #c82333;
99
+ }
100
+
101
+ .cancel-btn:disabled {
102
+ background-color: #6c757d;
103
+ cursor: not-allowed;
104
+ }
105
+
106
+ /* Ajuste no loader para acomodar o botão de cancelar */
107
+ .loader-content {
108
+ text-align: center;
109
+ }
110
  </style>
111
  </head>
112
  <body>
 
115
  <div class="loader-spinner"></div>
116
  <p id="loader-message">Processando sua solicitação...</p>
117
  <div class="progress-bar-container"><div id="progress-bar" class="progress-bar"></div></div>
118
+ <button id="cancel-btn" class="cancel-btn">Cancelar Processamento</button>
119
  </div>
120
  </div>
121
  <button id="merge-btn" class="floating-merge-btn" style="display: none;">Processar Merge</button>
 
126
  <p id="flow-description">GROK ➔ Claude Sonnet ➔ Gemini</p>
127
  </div>
128
  <div class="controls-container">
129
+ <div class="mode-toggle" title="A versão 'Hierárquica' gerará um único texto que passará por revisão em duas instâncias. Na versão 'Atômica', serão gerados 3 textos, um em cada modelo de IA; e depois um 4º texto será gerado fazendo um texto final consolidado dessas 3 versões.">
130
  <span>Hierárquico</span>
131
  <label class="switch"><input type="checkbox" id="processing-mode-switch"><span class="slider round"></span></label>
132
  <span>Atômico</span>
 
145
  <label for="solicitacao_usuario">Digite sua solicitação (ou arraste arquivos aqui):</label>
146
  <textarea name="solicitacao_usuario" id="solicitacao_usuario" rows="8" required></textarea>
147
  <div id="file-list-container"><p>Arquivos Anexados:</p><ul id="file-list"></ul></div>
148
+
149
+ <!-- NOVOS CAMPOS: Controle de tamanho do texto -->
150
+ <div class="text-size-controls">
151
+ <div class="text-size-field">
152
+ <label for="min_chars">Mín. Caracteres:</label>
153
+ <input type="number" id="min_chars" name="min_chars" value="24000" min="1000" max="100000" step="1000">
154
+ </div>
155
+ <div class="text-size-field">
156
+ <label for="max_chars">Máx. Caracteres:</label>
157
+ <input type="number" id="max_chars" name="max_chars" value="30000" min="1000" max="100000" step="1000">
158
+ </div>
159
+ <div style="font-size: 12px; color: #6c757d; max-width: 300px;">
160
+ <strong>Dica:</strong> Defina o tamanho desejado para os textos gerados pelas IAs. Valores maiores resultam em textos mais detalhados.
161
+ </div>
162
+ </div>
163
+
164
  <button type="submit">Processar com IA</button>
165
  </form>
166
  </div>
 
225
  </div>
226
  </div>
227
  </div>
228
+
229
  <script>
230
  // --- Variáveis Globais ---
231
  const processingModeSwitch = document.getElementById('processing-mode-switch');
 
242
  const mergeBtn = document.getElementById('merge-btn');
243
  const finalResultContainer = document.getElementById('final-result-container');
244
  const finalOutput = document.getElementById('final-output');
245
+ const cancelBtn = document.getElementById('cancel-btn');
246
  let attachedFiles = [];
247
  let originalUserQuery = "";
248
+ let rawTexts = {};
249
+ let currentProcessingType = null; // 'main' ou 'merge'
250
 
251
  // Log para debug
252
  function debugLog(message) {
 
258
  realContainer.style.display = this.checked ? 'none' : 'block';
259
  mockContainer.style.display = this.checked ? 'block' : 'none';
260
  });
261
+
262
  processingModeSwitch.addEventListener('change', function() {
263
+ const isAtomic = this.checked;
264
+ document.getElementById('flow-description').textContent = isAtomic ?
265
+ "GROK | Claude Sonnet | Gemini (Paralelo)" :
266
+ "GROK ➔ Claude Sonnet ➔ Gemini";
267
+ });
268
+
269
+ // --- Lógica do botão de cancelar ---
270
+ cancelBtn.addEventListener('click', async function() {
271
+ debugLog("=== CANCELAR BUTTON CLICADO ===");
272
+
273
+ try {
274
+ const response = await fetch('/cancel', {
275
+ method: 'POST',
276
+ headers: { 'Content-Type': 'application/json' }
277
+ });
278
+
279
+ if (response.ok) {
280
+ debugLog("Cancelamento solicitado com sucesso");
281
+ loaderMessage.textContent = 'Cancelando processamento...';
282
+ cancelBtn.disabled = true;
283
+ cancelBtn.textContent = 'Cancelando...';
284
+ } else {
285
+ debugLog("Erro ao solicitar cancelamento");
286
+ showError("Erro ao cancelar processamento.");
287
+ }
288
+ } catch (error) {
289
+ debugLog(`Erro no cancelamento: ${error.message}`);
290
+ showError("Erro ao cancelar processamento.");
291
+ }
292
+ });
293
+
294
+ // --- Validação dos campos de tamanho ---
295
+ document.getElementById('min_chars').addEventListener('change', function() {
296
+ const minValue = parseInt(this.value);
297
+ const maxValue = parseInt(document.getElementById('max_chars').value);
298
+
299
+ if (minValue >= maxValue) {
300
+ document.getElementById('max_chars').value = minValue + 1000;
301
+ }
302
+ });
303
+
304
+ document.getElementById('max_chars').addEventListener('change', function() {
305
+ const maxValue = parseInt(this.value);
306
+ const minValue = parseInt(document.getElementById('min_chars').value);
307
+
308
+ if (maxValue <= minValue) {
309
+ document.getElementById('min_chars').value = maxValue - 1000;
310
+ }
311
  });
312
 
313
  // --- Lógica de Upload ---
 
351
  async function handleFormSubmit(event) {
352
  event.preventDefault();
353
  debugLog("=== FORM SUBMIT INICIADO ===");
354
+ currentProcessingType = 'main';
355
 
356
  // Resetar a interface
357
  errorContainer.innerHTML = '';
 
363
  btn.disabled = false;
364
  btn.innerText = 'Converter para MD';
365
  });
366
+ rawTexts = {};
367
  debugLog("Interface resetada");
368
 
369
  // Iniciar o loader
370
  loaderMessage.textContent = 'Iniciando conexão...';
371
  progressBar.style.width = '0%';
372
  loader.style.display = 'flex';
373
+ cancelBtn.disabled = false;
374
+ cancelBtn.textContent = 'Cancelar Processamento';
375
  debugLog("Loader iniciado");
376
 
377
  const formData = new FormData();
378
  formData.append('processing_mode', processingModeSwitch.checked ? 'atomic' : 'hierarchical');
379
 
380
+ // Adicionar parâmetros de tamanho
381
+ formData.append('min_chars', document.getElementById('min_chars').value);
382
+ formData.append('max_chars', document.getElementById('max_chars').value);
383
+
384
  if (modeSwitch.checked) {
385
  formData.append('mode', 'test');
386
  formData.append('mock_text', document.getElementById('mock_text').value);
 
402
  debugLog("=== FETCH REALIZADO COM SUCESSO, INICIANDO STREAM ===");
403
  const reader = response.body.getReader();
404
  const decoder = new TextDecoder();
405
+ let buffer = '';
406
 
407
  while (true) {
408
  const { done, value } = await reader.read();
 
415
  buffer += chunk;
416
  debugLog(`Chunk recebido: ${chunk.length} chars`);
417
 
 
418
  let lines = buffer.split('\n\n');
419
+ buffer = lines.pop();
420
 
421
  lines.forEach(line => {
422
  if (line.startsWith('data: ')) {
 
436
  });
437
  }
438
 
 
439
  if (buffer.trim()) {
440
  debugLog(`Buffer final: ${buffer}`);
 
441
  if (buffer.startsWith('data: ')) {
442
  const jsonData = buffer.substring(6).trim();
443
  if (jsonData) {
 
462
  // --- Lógica do Botão de Merge ---
463
  mergeBtn.addEventListener('click', async function() {
464
  debugLog("=== MERGE BUTTON CLICADO ===");
465
+ currentProcessingType = 'merge';
466
+
467
  loaderMessage.textContent = 'Processando o merge dos textos...';
468
  progressBar.style.width = '0%';
469
  loader.style.display = 'flex';
470
+ cancelBtn.disabled = false;
471
+ cancelBtn.textContent = 'Cancelar Processamento';
472
  this.style.display = 'none';
473
 
 
474
  const payload = {
475
  solicitacao_usuario: originalUserQuery,
476
  grok_text: rawTexts['grok-output'] || '',
 
490
  debugLog("=== MERGE FETCH REALIZADO COM SUCESSO, INICIANDO STREAM ===");
491
  const reader = response.body.getReader();
492
  const decoder = new TextDecoder();
493
+ let buffer = '';
494
 
495
  while (true) {
496
  const { done, value } = await reader.read();
 
503
  buffer += chunk;
504
  debugLog(`Merge chunk recebido: ${chunk.length} chars`);
505
 
 
506
  let lines = buffer.split('\n\n');
507
+ buffer = lines.pop();
508
 
509
  lines.forEach(line => {
510
  if (line.startsWith('data: ')) {
 
514
  try {
515
  const data = JSON.parse(jsonData);
516
  debugLog(`Merge dados processados: ${JSON.stringify({progress: data.progress, message: data.message, hasFinalResult: !!data.final_result, error: data.error})}`);
517
+ processStreamData(data, true);
518
  } catch (e) {
519
  console.error("Erro ao parsear JSON do merge:", jsonData.substring(0, 500));
520
  console.error("Erro:", e);
 
524
  });
525
  }
526
 
 
527
  if (buffer.trim()) {
528
  debugLog(`Merge buffer final: ${buffer.substring(0, 200)}...`);
529
  if (buffer.startsWith('data: ')) {
 
580
  return;
581
  }
582
 
 
583
  rawTexts[targetId] = content;
584
+ targetBox.innerText = content;
585
  debugLog(`Conteúdo armazenado e exibido para: ${targetId}`);
586
 
587
  if (targetId === 'final-output') {
 
592
  }
593
  finalResultContainer.style.display = 'block';
594
  debugLog("Final result container exibido");
595
+
596
+ // MELHORIA 4: Scroll automático para o texto final
597
+ setTimeout(() => {
598
+ finalResultContainer.scrollIntoView({
599
+ behavior: 'smooth',
600
+ block: 'start'
601
+ });
602
+ debugLog("Scroll automático para texto final executado");
603
+ }, 500);
604
  } else {
605
  resultsContainer.style.display = 'flex';
606
  debugLog("Results container exibido");
 
627
  }
628
  }
629
 
630
+ // --- Funções de Utilitários ---
631
  function showError(message) {
632
  debugLog(`Exibindo erro: ${message}`);
633
  errorContainer.innerHTML = `<div class="error-box"><strong>Erro:</strong> ${message}<span class="close-btn-error" onclick="this.parentElement.style.display='none';" title="Fechar">&times;</span></div>`;
 
635
 
636
  function copyToClipboard(elementId) {
637
  debugLog(`Copiando conteúdo de: ${elementId}`);
 
638
  const textToCopy = rawTexts[elementId];
639
  if (textToCopy !== undefined) {
640
  navigator.clipboard.writeText(textToCopy).then(() => {
 
647
  }
648
  }
649
 
 
650
  async function convertToMarkdown(elementId) {
651
  debugLog(`Convertendo markdown para: ${elementId}`);
652
  const button = event.target;
 
672
 
673
  const data = await response.json();
674
  const targetBox = document.getElementById(elementId);
675
+ targetBox.innerHTML = data.html;
676
+ button.innerText = 'Convertido';
677
  debugLog(`Markdown convertido com sucesso para ${elementId}`);
678
  } catch (error) {
679
  showError('Não foi possível converter o texto.');
680
  console.error('Conversion error:', error);
681
  debugLog(`Erro na conversão: ${error.message}`);
682
+ button.innerText = 'Converter para MD';
683
  button.disabled = false;
684
  }
685
  }
686
  </script>
 
687
  </body>
688
  </html>