feat: criado repositório jhonny-editor no Gitea
criado repositório jhonny-editor no Gitea adicionado script admin/deploy.command com commit automático atualizado admin/DEV-NOTES.md com template limpo Resumo: - 3 arquivos alterados - 0 novos - 3 modificados - 0 removidos 3 files changed, 83 insertions(+), 21 deletions(-) Arquivos: - code/cep-plugin/index.html - code/cep-plugin/main.js - code/cep-plugin/styles.css
This commit is contained in:
@@ -42,13 +42,13 @@
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<!-- Painel de progresso global: tudo que está rodando aparece aqui, no topo,
|
||||
em vez de ficar escondido no log lá embaixo. -->
|
||||
<!-- O painel é reposicionado para a aba que iniciou a operação. -->
|
||||
<section class="task-dock" id="taskDock" role="status" aria-live="polite" hidden>
|
||||
<div class="task-head">
|
||||
<span class="task-spinner" id="taskSpinner" aria-hidden="true"></span>
|
||||
<strong class="task-title" id="taskTitle">Processando…</strong>
|
||||
<span class="task-elapsed" id="taskElapsed">00:00</span>
|
||||
<button type="button" class="task-copy" id="taskCopy" onclick="taskCopyError()" aria-label="Copiar erro técnico" title="Copiar erro técnico" hidden>⧉</button>
|
||||
</div>
|
||||
<div class="progress-track" id="taskTrack" data-mode="indeterminate">
|
||||
<div class="progress-fill" id="taskFill" style="width:0%"></div>
|
||||
@@ -57,6 +57,7 @@
|
||||
<span class="task-stage" id="taskStage">Preparando…</span>
|
||||
<span class="task-pct" id="taskPct"></span>
|
||||
</div>
|
||||
<pre class="task-error-details" id="taskErrorDetails" hidden></pre>
|
||||
</section>
|
||||
|
||||
<div class="toast" id="toast" role="status" aria-live="polite" hidden>
|
||||
|
||||
+77
-19
@@ -27,8 +27,8 @@ function log(msg, cls) {
|
||||
// ---- Painel de progresso (task dock) ----
|
||||
// Antes, tudo que rodava só aparecia como linhas no log lá embaixo e a pessoa
|
||||
// não sabia o que estava acontecendo. Agora todo processo longo abre um card
|
||||
// fixo no topo com etapa atual, barra e tempo decorrido.
|
||||
var taskState = { active: false, startedAt: 0, timer: null, clearTimer: null, mirror: null };
|
||||
// reposicionado na aba de origem com etapa atual, barra e tempo decorrido.
|
||||
var taskState = { active: false, startedAt: 0, timer: null, clearTimer: null, mirror: null, originTab: "silence", originPanelId: "tabSilence", errorText: "" };
|
||||
|
||||
function taskEl(id) { return document.getElementById(id); }
|
||||
|
||||
@@ -39,11 +39,28 @@ function formatClock(ms) {
|
||||
return mm + ":" + ss;
|
||||
}
|
||||
|
||||
function activeTab() {
|
||||
var panel = document.querySelector(".tab-panel:not([hidden])");
|
||||
return panel ? panel.id.replace(/^tab/, "").toLowerCase() : "silence";
|
||||
}
|
||||
|
||||
function taskMoveToOriginTab() {
|
||||
var dock = taskEl("taskDock");
|
||||
var nome = taskState.originTab;
|
||||
var panel = document.getElementById("tab" + nome.charAt(0).toUpperCase() + nome.slice(1));
|
||||
if (!dock || !panel) return;
|
||||
panel.insertBefore(dock, panel.firstChild);
|
||||
taskState.originPanelId = panel.id;
|
||||
}
|
||||
|
||||
function taskStart(title, stage, mirror) {
|
||||
if (taskState.clearTimer) { clearTimeout(taskState.clearTimer); taskState.clearTimer = null; }
|
||||
taskState.active = true;
|
||||
taskState.startedAt = Date.now();
|
||||
taskState.mirror = mirror || null;
|
||||
taskState.originTab = activeTab();
|
||||
taskState.errorText = "";
|
||||
taskMoveToOriginTab();
|
||||
|
||||
var dock = taskEl("taskDock");
|
||||
dock.removeAttribute("data-result");
|
||||
@@ -54,6 +71,9 @@ function taskStart(title, stage, mirror) {
|
||||
taskEl("taskElapsed").textContent = "00:00";
|
||||
taskEl("taskTrack").setAttribute("data-mode", "indeterminate");
|
||||
taskEl("taskFill").style.width = "0%";
|
||||
taskEl("taskCopy").hidden = true;
|
||||
taskEl("taskErrorDetails").hidden = true;
|
||||
taskEl("taskErrorDetails").textContent = "";
|
||||
hideToast();
|
||||
|
||||
if (taskState.timer) clearInterval(taskState.timer);
|
||||
@@ -80,7 +100,7 @@ function taskUpdate(info) {
|
||||
}
|
||||
}
|
||||
|
||||
function taskEnd(ok, message) {
|
||||
function taskEnd(ok, message, technicalDetails) {
|
||||
if (taskState.timer) { clearInterval(taskState.timer); taskState.timer = null; }
|
||||
taskState.active = false;
|
||||
|
||||
@@ -90,6 +110,13 @@ function taskEnd(ok, message) {
|
||||
taskEl("taskFill").style.width = "100%";
|
||||
taskEl("taskStage").textContent = message || (ok ? "Concluído" : "Falhou");
|
||||
taskEl("taskPct").textContent = ok ? "100%" : "";
|
||||
if (!ok) {
|
||||
taskState.errorText = String(technicalDetails || message || "Falha sem detalhes técnicos.");
|
||||
taskEl("taskCopy").hidden = false;
|
||||
taskEl("taskErrorDetails").textContent = taskState.errorText;
|
||||
taskEl("taskErrorDetails").hidden = false;
|
||||
if (activeTab() !== taskState.originTab) switchTab(taskState.originTab);
|
||||
}
|
||||
|
||||
taskState.mirror = null;
|
||||
// O próprio card já carrega o desfecho e fica na tela por alguns segundos —
|
||||
@@ -99,6 +126,35 @@ function taskEnd(ok, message) {
|
||||
}, ok ? 4000 : 9000);
|
||||
}
|
||||
|
||||
function taskCopyError() {
|
||||
var texto = taskState.errorText;
|
||||
if (!texto) return;
|
||||
try {
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(texto).then(function () { showToast("ok", "Erro técnico copiado."); }).catch(function () { taskCopyErrorFallback(texto); });
|
||||
return;
|
||||
}
|
||||
} catch (_) {}
|
||||
taskCopyErrorFallback(texto);
|
||||
}
|
||||
|
||||
function taskCopyErrorFallback(texto) {
|
||||
try {
|
||||
var copia = childProcess.spawn("pbcopy");
|
||||
copia.stdin.end(texto);
|
||||
showToast("ok", "Erro técnico copiado.");
|
||||
return;
|
||||
} catch (_) {}
|
||||
var area = document.createElement("textarea");
|
||||
area.value = texto;
|
||||
area.style.position = "fixed";
|
||||
area.style.opacity = "0";
|
||||
document.body.appendChild(area);
|
||||
area.select();
|
||||
try { document.execCommand("copy"); showToast("ok", "Erro técnico copiado."); } catch (erro) { showToast("err", "Não foi possível copiar o erro: " + erro.message); }
|
||||
document.body.removeChild(area);
|
||||
}
|
||||
|
||||
function showToast(kind, text) {
|
||||
var el = document.getElementById("toast");
|
||||
if (!el) return;
|
||||
@@ -302,17 +358,18 @@ function retakesAnalisar() {
|
||||
{ cwd: "/Volumes/Merongo/SISTEMAS/GENIAL SISTEMAS/Jhonny/code/engine", stdio: ["ignore", "pipe", "pipe"] });
|
||||
retakesProcesso = child;
|
||||
var resto = "";
|
||||
var erroTecnico = "";
|
||||
child.stdout.on("data", function (pedaco) {
|
||||
resto += pedaco.toString(); var linhas = resto.split("\n"); resto = linhas.pop();
|
||||
linhas.forEach(function (linha) { var evento; try { evento = JSON.parse(linha); } catch (_) { return; } retakesProcessarEvento(evento); });
|
||||
});
|
||||
child.stderr.on("data", function (pedaco) { if (pedaco.toString().trim()) retakesLog(pedaco.toString().trim()); });
|
||||
child.on("error", function (erro) { retakesProcesso = null; setBusy("btnRetakesAnalisar", false); taskEnd(false, "Engine indisponível"); showToast("err", erro.message); });
|
||||
child.stderr.on("data", function (pedaco) { erroTecnico += pedaco.toString(); if (pedaco.toString().trim()) retakesLog(pedaco.toString().trim()); });
|
||||
child.on("error", function (erro) { retakesProcesso = null; setBusy("btnRetakesAnalisar", false); taskEnd(false, "Engine indisponível", erro.stack || erroTecnico || erro.message); showToast("err", erro.message); });
|
||||
child.on("close", function (codigo) {
|
||||
retakesProcesso = null; setBusy("btnRetakesAnalisar", false);
|
||||
document.getElementById("retakesProgressoCard").hidden = true;
|
||||
document.getElementById("retakesStatusGeral").textContent = "Analisado";
|
||||
if (codigo !== 0) taskEnd(false, "Análise falhou");
|
||||
if (codigo !== 0) taskEnd(false, "Análise falhou", erroTecnico || "A engine terminou com código " + codigo + ".");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -461,8 +518,8 @@ function scannerProcess() {
|
||||
lines.forEach(function(line) { try { var event=JSON.parse(line); if (event.evento === "progresso") taskUpdate({stage:event.etapa, detail:event.clipe || "", pct:event.percentual}); if (event.evento === "concluido" && event.arquivo) scannerReportPath=event.arquivo; } catch (_) {} });
|
||||
});
|
||||
child.stderr.on("data",function(chunk){stderr+=chunk.toString();});
|
||||
child.on("error",function(error){setBusy("btnScannerProcess",false);taskEnd(false,"Scanner indisponível");showToast("err",error.message);});
|
||||
child.on("close",function(code){setBusy("btnScannerProcess",false);if(code!==0||!fs.existsSync(scannerReportPath)){taskEnd(false,"Scanner falhou");showToast("err",stderr||"Não foi possível gerar o relatório.");return;}taskEnd(true,"Relatório JSON gerado");document.getElementById("scannerReportPath").textContent=scannerReportPath;document.getElementById("scannerReportCard").hidden=false;showToast("ok","Scanner concluído.");});
|
||||
child.on("error",function(error){setBusy("btnScannerProcess",false);taskEnd(false,"Scanner indisponível",error.stack||error.message);showToast("err",error.message);});
|
||||
child.on("close",function(code){setBusy("btnScannerProcess",false);if(code!==0||!fs.existsSync(scannerReportPath)){taskEnd(false,"Scanner falhou",stderr||"Não foi possível gerar o relatório.");showToast("err",stderr||"Não foi possível gerar o relatório.");return;}taskEnd(true,"Relatório JSON gerado");document.getElementById("scannerReportPath").textContent=scannerReportPath;document.getElementById("scannerReportCard").hidden=false;showToast("ok","Scanner concluído.");});
|
||||
}
|
||||
|
||||
function scannerOpenReport() { if (!scannerReportPath) return; childProcess.spawn("open",[scannerReportPath],{detached:true,stdio:"ignore"}).unref(); }
|
||||
@@ -1450,6 +1507,7 @@ function runStreamed(bin, args, onLine, onDone, options) {
|
||||
var childProcess = nodeRequire("child_process");
|
||||
var child = childProcess.spawn(bin, args, options || {});
|
||||
var buffer = "";
|
||||
var ultimoErro = "";
|
||||
// Linhas "@@PROGRESS {json}" são o canal de progresso dos scripts Python/Node:
|
||||
// vão para a barra no topo em vez de virarem ruído no log.
|
||||
function handleLine(line) {
|
||||
@@ -1471,11 +1529,11 @@ function runStreamed(bin, args, onLine, onDone, options) {
|
||||
}
|
||||
}
|
||||
child.stdout.on("data", function (d) { flushLines(d.toString()); });
|
||||
child.stderr.on("data", function (d) { flushLines(d.toString()); });
|
||||
child.stderr.on("data", function (d) { ultimoErro = d.toString().trim() || ultimoErro; flushLines(d.toString()); });
|
||||
child.on("error", function (err) { onDone(1, "spawn error: " + err.message); });
|
||||
child.on("close", function (code) {
|
||||
if (buffer.trim()) handleLine(buffer);
|
||||
onDone(code, null);
|
||||
onDone(code, ultimoErro || null);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1503,11 +1561,11 @@ function silenceTranscribe() {
|
||||
"--cache-dir", silenceState.outputDir,
|
||||
],
|
||||
function (line) { lastLine = line; silenceLog(line); },
|
||||
function (code) {
|
||||
function (code, lastErr) {
|
||||
setBusy("btnTranscribe", false);
|
||||
if (code !== 0) {
|
||||
setStep(2, "error", "Falhou");
|
||||
taskEnd(false, "A transcrição falhou (código " + code + "). Veja os detalhes técnicos.");
|
||||
taskEnd(false, "A transcrição falhou (código " + code + "). Veja os detalhes técnicos.", lastErr || lastLine);
|
||||
silenceLog("Falha na transcrição (código " + code + ").", "err");
|
||||
return;
|
||||
}
|
||||
@@ -1515,7 +1573,7 @@ function silenceTranscribe() {
|
||||
try { parsed = JSON.parse(lastLine); } catch (e) {}
|
||||
if (!parsed || parsed.ok === false) {
|
||||
setStep(2, "error", "Falhou");
|
||||
taskEnd(false, "A transcrição terminou com uma resposta inválida.");
|
||||
taskEnd(false, "A transcrição terminou com uma resposta inválida.", lastLine);
|
||||
silenceLog("Transcrição terminou mas resposta é inválida: " + lastLine, "err");
|
||||
return;
|
||||
}
|
||||
@@ -1573,7 +1631,7 @@ function silenceDiarizeSpeakers() {
|
||||
if (code !== 0) {
|
||||
setStep(3, "ready", "Opcional");
|
||||
info.textContent = "Falhou — veja os detalhes técnicos.";
|
||||
taskEnd(false, "A detecção de locutores falhou (código " + code + ").");
|
||||
taskEnd(false, "A detecção de locutores falhou (código " + code + ").", lastErr);
|
||||
silenceLog("Falha na diarização (código " + code + (lastErr ? ": " + lastErr : "") + ").", "err");
|
||||
return;
|
||||
}
|
||||
@@ -1628,7 +1686,7 @@ function silenceComputeVoiceFeatures() {
|
||||
if (code !== 0) {
|
||||
setStep(3, "ready", "Opcional");
|
||||
info.textContent = "Falhou — veja os detalhes técnicos.";
|
||||
taskEnd(false, "O cálculo das métricas de voz falhou (código " + code + ").");
|
||||
taskEnd(false, "O cálculo das métricas de voz falhou (código " + code + ").", lastErr);
|
||||
silenceLog("Falha ao calcular métricas de voz (código " + code + (lastErr ? ": " + lastErr : "") + ").", "err");
|
||||
return;
|
||||
}
|
||||
@@ -1784,11 +1842,11 @@ function applyEditorialActions() {
|
||||
nodeBin,
|
||||
[applyScript, "--plan", planPath, "--sequence", silenceState.sequenceName],
|
||||
function (line) { silenceLog(line); },
|
||||
function (code) {
|
||||
function (code, lastErr) {
|
||||
setBusy("btnApplyEditorialActions", false);
|
||||
if (code !== 0) {
|
||||
setStep(5, "error", "Falhou");
|
||||
taskEnd(false, "Falha ao aplicar o plano (código " + code + ").");
|
||||
taskEnd(false, "Falha ao aplicar o plano (código " + code + ").", lastErr);
|
||||
silenceLog("Falha ao aplicar o plano (código " + code + ").", "err");
|
||||
return;
|
||||
}
|
||||
@@ -2374,12 +2432,12 @@ function downloadModel(model, btn, approxMB) {
|
||||
"--approx-mb", String(approxMB || 0),
|
||||
],
|
||||
function (line) { modelsLog(line); },
|
||||
function (code) {
|
||||
function (code, lastErr) {
|
||||
btn.classList.remove("is-busy");
|
||||
if (code !== 0) {
|
||||
if (box) box.hidden = true;
|
||||
modelsLog("Falha ao baixar " + model + " (código " + code + ").", "err");
|
||||
taskEnd(false, "Falha ao baixar o modelo " + model + " (código " + code + ").");
|
||||
taskEnd(false, "Falha ao baixar o modelo " + model + " (código " + code + ").", lastErr);
|
||||
btn.disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -170,6 +170,8 @@ h1, h2, h3 { margin: 0; }
|
||||
.task-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
|
||||
.task-title { flex: 1; min-width: 0; overflow: hidden; font-size: 11.5px; font-weight: 650; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.task-elapsed { flex: 0 0 auto; color: var(--text-muted); font: 10px var(--font-mono); }
|
||||
.task-copy { flex: 0 0 auto; padding: 2px 5px; border: 1px solid var(--border); border-radius: 4px; background: transparent; color: var(--text-secondary); cursor: pointer; font-size: 12px; line-height: 1; }
|
||||
.task-copy:hover { border-color: var(--red); color: var(--text); }
|
||||
|
||||
.task-spinner {
|
||||
width: 12px;
|
||||
@@ -209,6 +211,7 @@ h1, h2, h3 { margin: 0; }
|
||||
.task-foot { display: flex; align-items: baseline; gap: 8px; margin-top: 7px; }
|
||||
.task-stage { flex: 1; min-width: 0; overflow: hidden; color: var(--text-secondary); font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.task-pct { flex: 0 0 auto; color: var(--text); font: 600 10px var(--font-mono); }
|
||||
.task-error-details { max-height: 180px; margin: 10px 0 0; padding: 8px; overflow: auto; border: 1px solid rgba(255, 101, 101, .25); border-radius: 5px; background: rgba(0, 0, 0, .2); color: #ffb0b0; font: 9px/1.45 var(--font-mono); white-space: pre-wrap; user-select: text; }
|
||||
|
||||
/* -------------------------------- Toast -------------------------------- */
|
||||
.toast {
|
||||
|
||||
Reference in New Issue
Block a user