ui: align obfuscator with Nubes branding
Update the interface palette, typography and layout to match Nubes while keeping the file workflow unchanged. Bundle Ubuntu locally and bump the app version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
+1
-1
@@ -22,7 +22,7 @@ if _sys_path_root not in sys.path:
|
||||
sys.path.insert(0, _sys_path_root)
|
||||
|
||||
# Версия приложения (меняется при изменениях)
|
||||
VERSION = "1.1.0"
|
||||
VERSION = "1.1.1"
|
||||
|
||||
|
||||
def setup_logging():
|
||||
|
||||
Binary file not shown.
+195
-201
@@ -10,16 +10,24 @@
|
||||
<title>Obfuscator — обфускация документов</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'Ubuntu';
|
||||
src: url('/static/fonts/Ubuntu.woff2') format('woff2');
|
||||
font-weight: 300 700;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
:root {
|
||||
--brand-primary: #2563eb;
|
||||
--brand-gray: #d1d5db;
|
||||
--brand-grey-light: #f3f4f6;
|
||||
--bg: #f9fafb;
|
||||
--navy: #001c34;
|
||||
--brand-primary: #0d6efd;
|
||||
--brand-gray: #dee2e6;
|
||||
--brand-grey-light: #f8f9fa;
|
||||
--bg: #f8f9fa;
|
||||
--card: #ffffff;
|
||||
--text: #1a1a1a;
|
||||
--muted: #6b7280;
|
||||
--red: #ef4444;
|
||||
--green: #22c55e;
|
||||
--text: #001c34;
|
||||
--muted: #6c757d;
|
||||
--red: #dc3545;
|
||||
--green: #198754;
|
||||
}
|
||||
|
||||
* {
|
||||
@@ -29,95 +37,103 @@
|
||||
}
|
||||
|
||||
body {
|
||||
font: 14px system-ui, sans-serif;
|
||||
font: 14px/1.5 'Ubuntu', system-ui, sans-serif;
|
||||
color: var(--text);
|
||||
background: var(--bg);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.topbar {
|
||||
background: var(--card);
|
||||
border-bottom: 1px solid var(--brand-gray);
|
||||
padding: 0 16px;
|
||||
height: 48px;
|
||||
background: var(--navy);
|
||||
color: #fff;
|
||||
padding: 0 24px;
|
||||
height: 56px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.topbar img {
|
||||
height: 18px;
|
||||
height: 20px;
|
||||
filter: brightness(0) invert(1);
|
||||
}
|
||||
|
||||
.topbar .title {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.topbar .ver {
|
||||
font-weight: 400;
|
||||
color: var(--muted);
|
||||
color: #a4acb3;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.topbar .help {
|
||||
margin-left: auto;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
user-select: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 5px 14px;
|
||||
border-radius: 6px;
|
||||
background: #1d4ed8;
|
||||
padding: 6px 16px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, .4);
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: .06em;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .25);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.topbar .help:hover {
|
||||
background: #1e40af;
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: 820px;
|
||||
margin: 20px auto;
|
||||
padding: 0 12px;
|
||||
max-width: 960px;
|
||||
margin: 40px auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 28px;
|
||||
font-weight: 500;
|
||||
color: var(--navy);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.page-sub {
|
||||
color: var(--muted);
|
||||
margin: 8px 0 24px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--card);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--brand-gray);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .04);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: var(--brand-grey-light);
|
||||
padding: 10px 12px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
border-bottom: 1px solid var(--brand-gray);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 12px;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.sub {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--muted);
|
||||
margin-bottom: 10px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.limits {
|
||||
background: var(--brand-grey-light);
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 13px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.limits ul {
|
||||
padding-left: 18px;
|
||||
}
|
||||
|
||||
/* FilePicker styling */
|
||||
.file-picker {
|
||||
#filePicker .file-picker,
|
||||
#filePicker .fp-table {
|
||||
font-family: inherit;
|
||||
color: var(--text);
|
||||
}
|
||||
@@ -129,70 +145,77 @@
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.file-picker .fp-toolbar button {
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
#filePicker .fp-toolbar button {
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
padding: 0 18px;
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--brand-gray);
|
||||
border: 1px solid var(--brand-primary);
|
||||
background: var(--card);
|
||||
color: var(--text);
|
||||
color: var(--brand-primary);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
gap: 6px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.file-picker .fp-toolbar button#fp-files-btn {
|
||||
background: #eff6ff;
|
||||
border-color: var(--brand-primary);
|
||||
color: var(--brand-primary);
|
||||
#filePicker .fp-toolbar button:hover {
|
||||
background: #e7f1ff;
|
||||
}
|
||||
|
||||
.file-picker .fp-toolbar button#fp-folder-btn {
|
||||
background: var(--card);
|
||||
}
|
||||
|
||||
.file-picker .fp-toolbar button#fp-clear-btn {
|
||||
#filePicker .fp-toolbar button#fp-clear-btn {
|
||||
color: var(--muted);
|
||||
border-color: transparent;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.file-picker .fp-toolbar button#fp-clear-btn:hover {
|
||||
#filePicker .fp-toolbar button#fp-clear-btn:hover {
|
||||
background: transparent;
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
.file-picker .fp-table-wrap {
|
||||
#filePicker .fp-table-wrap {
|
||||
border: 1px solid var(--brand-gray);
|
||||
border-radius: 8px;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.file-picker .fp-table {
|
||||
#filePicker .fp-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 13px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.file-picker .fp-table th {
|
||||
#filePicker .fp-table th {
|
||||
background: var(--brand-grey-light);
|
||||
text-transform: uppercase;
|
||||
padding: 6px 8px;
|
||||
border-right: 1px solid var(--brand-gray);
|
||||
padding: 10px 16px;
|
||||
border-bottom: 1px solid var(--brand-gray);
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
letter-spacing: 0;
|
||||
text-transform: none;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.file-picker .fp-table td {
|
||||
padding: 5px 8px;
|
||||
border-right: 1px solid var(--brand-gray);
|
||||
#filePicker .fp-table td {
|
||||
padding: 10px 16px;
|
||||
border-bottom: 1px solid var(--brand-gray);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
#filePicker .fp-table tr:last-child td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
#filePicker .fp-table td[colspan] {
|
||||
color: var(--muted);
|
||||
text-align: center;
|
||||
padding: 32px 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.file-picker .tree-row:hover td {
|
||||
@@ -224,7 +247,7 @@
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.file-picker .fp-status {
|
||||
#filePicker .fp-status {
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
margin: 4px 0;
|
||||
@@ -258,19 +281,21 @@
|
||||
}
|
||||
|
||||
.footer-bar {
|
||||
margin-top: 10px;
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.btn {
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
padding: 0 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--brand-gray);
|
||||
@@ -278,7 +303,7 @@
|
||||
color: var(--text);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@@ -288,148 +313,116 @@
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
opacity: .9;
|
||||
background: #0b5ed7;
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
opacity: .4;
|
||||
background: #e9ecef;
|
||||
border-color: #e9ecef;
|
||||
color: #a4acb3;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin-top: 10px;
|
||||
padding: 8px 12px;
|
||||
margin-top: 16px;
|
||||
padding: 10px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
font-size: 14px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.status.progress {
|
||||
background: #eff6ff;
|
||||
border: 1px solid var(--brand-primary);
|
||||
color: var(--brand-primary);
|
||||
background: #e7f1ff;
|
||||
color: #0a58ca;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.status.done {
|
||||
background: #f0fdf4;
|
||||
border: 1px solid var(--green);
|
||||
color: #16a34a;
|
||||
background: #e8f5ee;
|
||||
color: #146c43;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.status.error {
|
||||
background: #fef2f2;
|
||||
border: 1px solid var(--red);
|
||||
color: #991b1b;
|
||||
background: #fdecee;
|
||||
color: #b02a37;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dl-btns {
|
||||
display: none;
|
||||
gap: 8px;
|
||||
margin-top: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.dl-btns.show {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.stats-block {
|
||||
.stats-block,
|
||||
.live-block {
|
||||
display: none;
|
||||
margin-top: 12px;
|
||||
padding: 16px;
|
||||
background: linear-gradient(135deg, #f0fdf4, #ecfdf5);
|
||||
border: 2px solid var(--green);
|
||||
margin-top: 16px;
|
||||
padding: 16px 20px;
|
||||
background: var(--brand-grey-light);
|
||||
border: 1px solid var(--brand-gray);
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.stats-block.show {
|
||||
.stats-block.show,
|
||||
.live-block.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.stats-title {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #14532d;
|
||||
margin-bottom: 10px;
|
||||
.stats-title,
|
||||
.live-title {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.stats-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
flex: 1;
|
||||
min-width: 130px;
|
||||
background: #fff;
|
||||
border: 1px solid #bbf7d0;
|
||||
border-radius: 10px;
|
||||
padding: 10px 12px;
|
||||
text-align: center;
|
||||
border: 1px solid var(--brand-gray);
|
||||
border-radius: 8px;
|
||||
padding: 10px 14px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .04em;
|
||||
font-size: 13px;
|
||||
color: var(--muted);
|
||||
margin-bottom: 4px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 24px;
|
||||
font-weight: 800;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.live-block {
|
||||
display: none;
|
||||
margin-top: 12px;
|
||||
padding: 16px;
|
||||
background: linear-gradient(135deg, #eff6ff, #dbeafe);
|
||||
border: 2px solid var(--brand-primary);
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.live-block.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.live-title {
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
color: #1e40af;
|
||||
margin-bottom: 6px;
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.live-file {
|
||||
font-size: 15px;
|
||||
color: #1e3a8a;
|
||||
margin-bottom: 10px;
|
||||
color: var(--muted);
|
||||
margin-bottom: 8px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.live-timer {
|
||||
font-size: 40px;
|
||||
font-weight: 800;
|
||||
color: #1d4ed8;
|
||||
font-size: 32px;
|
||||
font-weight: 500;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.live-llm {
|
||||
display: none;
|
||||
margin-top: 8px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
background: #fef3c7;
|
||||
border: 1px solid #f59e0b;
|
||||
color: #92400e;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
width: fit-content;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.live-llm.show {
|
||||
@@ -438,9 +431,7 @@
|
||||
|
||||
.live-eta {
|
||||
margin-top: 8px;
|
||||
font-size: 14px;
|
||||
color: #1d4ed8;
|
||||
font-weight: 600;
|
||||
color: var(--muted);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@@ -453,43 +444,41 @@
|
||||
<span class="help" onclick="document.getElementById('helpModal').style.display='flex'">HELP</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h1 class="page-title">Обфускация документов</h1>
|
||||
<p class="page-sub">Загрузите документы — получите ZIP с обезличенными копиями. CSV с таблицей замен скачивается отдельно.</p>
|
||||
<div class="card">
|
||||
<div class="card-header">Обфускация документов</div>
|
||||
<div class="card-body">
|
||||
<p class="sub">
|
||||
Загрузите документы (.docx, .doc, .pdf, .txt, .md, .zip) — получите ZIP с обезличенными копиями.<br>
|
||||
CSV с таблицей замен скачивается отдельно.<br>
|
||||
<span style="color:#c0392b;">Ограничения: один файл не более 50 МБ, суммарно не более 500 МБ. Файлы сверх
|
||||
лимитов помечаются статусом «пропущен» и не участвуют в обфускации. Пустые, повреждённые файлы и сканы без
|
||||
текстового слоя получают статус «не извлечён» — они тоже не попадают в результат.</span><br>
|
||||
<span style="color:#7d3c98;">⏱ Время обработки каждого файла — ориентировочное (обновляется по факту).</span>
|
||||
</p>
|
||||
<div class="limits">
|
||||
<ul>
|
||||
<li>Форматы: .docx, .doc, .pdf, .txt, .md, .zip. Не более 50 МБ на файл и 500 МБ суммарно — файлы сверх лимитов получают статус «пропущен».</li>
|
||||
<li>Пустые, повреждённые файлы и сканы без текстового слоя получают статус «не извлечён» и не попадают в результат.</li>
|
||||
<li>Время обработки каждого файла ориентировочное и обновляется по факту.</li>
|
||||
<li>Фильтр по маскам: положите в корень папки файл <code>obscure.lst</code>. Подробнее — кнопка HELP.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="filePicker"></div>
|
||||
<p class="sub" style="margin-top:6px;font-size:12px;">
|
||||
Фильтр по маскам: положите в корень папки файл <code>obscure.lst</code>. Подробнее — кнопка HELP.
|
||||
</p>
|
||||
|
||||
<div class="footer-bar">
|
||||
<span id="fileCount">0 файлов</span>
|
||||
<button class="btn" id="cancelBtn" style="display:none" onclick="confirmCancel()">⏹ Прервать</button>
|
||||
<button class="btn" id="newSessionBtn" style="display:none" onclick="resetAll()">🔄 Новая сессия</button>
|
||||
<button class="btn btn-primary" id="uploadBtn" disabled onclick="uploadFiles()">🛡️ Обфусцировать</button>
|
||||
<button class="btn" id="cancelBtn" style="display:none" onclick="confirmCancel()">Прервать</button>
|
||||
<button class="btn" id="newSessionBtn" style="display:none" onclick="resetAll()">Новая сессия</button>
|
||||
<button class="btn btn-primary" id="uploadBtn" disabled onclick="uploadFiles()">Обфусцировать</button>
|
||||
</div>
|
||||
<div class="status" id="status"></div>
|
||||
<div class="dl-btns" id="dlBtns">
|
||||
<button class="btn btn-primary" id="dlZip" onclick="downloadZip()">📦 Скачать ZIP</button>
|
||||
<button class="btn" id="dlCsv" onclick="downloadCsv()">📋 Скачать CSV</button>
|
||||
<button class="btn btn-primary" id="dlZip" onclick="downloadZip()">Скачать ZIP</button>
|
||||
<button class="btn" id="dlCsv" onclick="downloadCsv()">Скачать CSV</button>
|
||||
</div>
|
||||
<div class="live-block" id="liveBlock">
|
||||
<div class="live-title">⏱ Обработка…</div>
|
||||
<div class="live-title">Обработка…</div>
|
||||
<div class="live-file" id="liveFile">—</div>
|
||||
<div class="live-timer" id="liveTimer">0.0 с</div>
|
||||
<div class="live-llm" id="liveLlm">🤖 ИИ обрабатывает… <span id="liveLlmTime">0.0 с</span></div>
|
||||
<div class="live-llm" id="liveLlm">ИИ обрабатывает… <span id="liveLlmTime">0.0 с</span></div>
|
||||
<div class="live-eta" id="liveEta"></div>
|
||||
</div>
|
||||
<div class="stats-block" id="statsBlock">
|
||||
<div class="stats-title">📊 Итоги обработки</div>
|
||||
<div class="stats-title">Итоги обработки</div>
|
||||
<div class="stats-row">
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">Общее время</div>
|
||||
@@ -533,8 +522,8 @@
|
||||
mount: '#filePicker',
|
||||
allowedExt: DOC_EXTS,
|
||||
labels: {
|
||||
pickFiles: '📄 Выбрать файлы',
|
||||
pickFolder: '📁 Выбрать папку',
|
||||
pickFiles: 'Выбрать файлы',
|
||||
pickFolder: 'Выбрать папку',
|
||||
clear: 'Очистить',
|
||||
empty: 'Нет выбранных файлов',
|
||||
statusReady: 'готов',
|
||||
@@ -574,7 +563,7 @@
|
||||
}
|
||||
if (currentSid) {
|
||||
fetch('/api/cancel/' + currentSid, { method: 'POST' }).catch(() => { });
|
||||
st.textContent = '⏹ Остановка… завершаем текущий файл';
|
||||
st.textContent = 'Остановка… завершаем текущий файл';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -660,7 +649,7 @@
|
||||
if (!res.ok) {
|
||||
if (res.aborted) {
|
||||
st.className = 'status';
|
||||
st.textContent = '⏹ Загрузка отменена пользователем.';
|
||||
st.textContent = 'Загрузка отменена пользователем.';
|
||||
} else {
|
||||
st.className = 'status error';
|
||||
st.textContent = res.error || 'Ошибка загрузки';
|
||||
@@ -676,7 +665,7 @@
|
||||
|
||||
if (TEST_UPLOAD_ONLY) {
|
||||
st.className = 'status done';
|
||||
st.textContent = '✅ Загружено ' + uploadedCount + ' файлов (тест: обработка пропущена). Сессия: ' + currentSid;
|
||||
st.textContent = 'Загружено ' + uploadedCount + ' файлов (тест: обработка пропущена). Сессия: ' + currentSid;
|
||||
setBusy(false);
|
||||
cb.style.display = 'none';
|
||||
ub.disabled = false;
|
||||
@@ -738,7 +727,7 @@
|
||||
curRows.forEach(r => r.classList.remove('row-current'));
|
||||
curRows[idx].classList.add('row-current');
|
||||
const td = curRows[idx].querySelectorAll('td')[2];
|
||||
if (td) td.innerHTML = '<span style="color:#2563eb">⚙️</span>';
|
||||
if (td) td.innerHTML = '<span style="color:var(--brand-primary)">…</span>';
|
||||
}
|
||||
currentLiveFile = 'Файл ' + (d.idx + 1) + '/' + total + ': ' + d.name;
|
||||
liveFileEl.textContent = currentLiveFile;
|
||||
@@ -757,7 +746,7 @@
|
||||
curRows.forEach(r => r.classList.remove('row-current'));
|
||||
curRows[idx].classList.add('row-current');
|
||||
const td = curRows[idx].querySelectorAll('td')[2];
|
||||
if (td) td.innerHTML = '<span style="color:#2563eb">⚙️ анализ…</span>';
|
||||
if (td) td.innerHTML = '<span style="color:var(--brand-primary)">анализ…</span>';
|
||||
}
|
||||
liveFileEl.textContent = 'Файл ' + (d.idx + 1) + '/' + total + ': ' + d.name;
|
||||
});
|
||||
@@ -773,7 +762,7 @@
|
||||
if (curRows[idx]) {
|
||||
curRows[idx].classList.remove('row-current');
|
||||
const td = curRows[idx].querySelectorAll('td')[2];
|
||||
if (td) td.innerHTML = '✅ готов';
|
||||
if (td) td.innerHTML = '<span style="color:var(--green)">готов</span>';
|
||||
}
|
||||
});
|
||||
activeES.addEventListener('llm', function (e) {
|
||||
@@ -781,7 +770,7 @@
|
||||
if (d.active) {
|
||||
liveLlmTime.textContent = d.elapsed + ' с';
|
||||
liveLlm.classList.add('show');
|
||||
liveFileEl.textContent = '🤖 ИИ анализирует все файлы…';
|
||||
liveFileEl.textContent = 'ИИ анализирует все файлы…';
|
||||
if (typeof d.eta_sec === 'number' && d.eta_sec >= 0) liveEta.textContent = '⏳ осталось ~' + fmtSec(d.eta_sec);
|
||||
else liveEta.textContent = '';
|
||||
} else {
|
||||
@@ -805,7 +794,7 @@
|
||||
: ((performance.now() - (fileTimers[idx] || performance.now())) / 1000).toFixed(1);
|
||||
if (curRows[idx]) {
|
||||
const td = curRows[idx].querySelectorAll('td')[2];
|
||||
if (td) td.innerHTML = '✅ ' + sec + 'с';
|
||||
if (td) td.innerHTML = '<span style="color:var(--green)">' + sec + 'с</span>';
|
||||
}
|
||||
});
|
||||
activeES.addEventListener('cancelled', function (e) {
|
||||
@@ -813,7 +802,7 @@
|
||||
finishProcUI();
|
||||
const totalSec = ((performance.now() - t0) / 1000).toFixed(1);
|
||||
st.className = 'status done';
|
||||
st.textContent = '⏹ Остановлено. Сохранено ' + d.saved + ' из ' + d.total + ' файлов + таблица замен (общее ' + totalSec + 'с)';
|
||||
st.textContent = 'Остановлено. Сохранено ' + d.saved + ' из ' + d.total + ' файлов + таблица замен (общее ' + totalSec + 'с)';
|
||||
const sb = document.getElementById('statsBlock');
|
||||
document.getElementById('stTotalTime').textContent = totalSec + ' с';
|
||||
if (d.llm_sec > 0) {
|
||||
@@ -835,7 +824,7 @@
|
||||
const d = JSON.parse(e.data);
|
||||
const totalSec = ((performance.now() - t0) / 1000).toFixed(1);
|
||||
st.className = 'status done';
|
||||
st.textContent = '✅ Обработано ' + d.total + ' файлов: общее ' + totalSec + 'с, из них ИИ ' + (d.llm_sec > 0 ? d.llm_sec + 'с' : '0с');
|
||||
st.textContent = 'Обработано ' + d.total + ' файлов: общее ' + totalSec + 'с, из них ИИ ' + (d.llm_sec > 0 ? d.llm_sec + 'с' : '0с');
|
||||
const sb = document.getElementById('statsBlock');
|
||||
document.getElementById('stTotalTime').textContent = totalSec + ' с';
|
||||
if (d.llm_sec > 0) {
|
||||
@@ -957,7 +946,8 @@
|
||||
</p>
|
||||
<ul style="font-size:13px;color:var(--muted);line-height:1.7;padding-left:20px;margin:8px 0">
|
||||
<li>Файл ищется только в корне папки; имя без учёта регистра: <code>obscure.lst</code>,
|
||||
<code>OBSCURE.LST</code>.</li>
|
||||
<code>OBSCURE.LST</code>.
|
||||
</li>
|
||||
<li>Если файла нет — обрабатываются все поддерживаемые документы (как раньше).</li>
|
||||
<li>Сам <code>obscure.lst</code> не загружается и не обфусцируется.</li>
|
||||
<li>Другие <code>.lst</code> игнорируются; вложенный <code>subdir/obscure.lst</code> фильтр не включает.</li>
|
||||
@@ -993,21 +983,25 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:4px 8px;border-bottom:1px solid var(--brand-gray);white-space:nowrap"><code>[a-z]</code>,
|
||||
<code>[abc]</code></td>
|
||||
<code>[abc]</code>
|
||||
</td>
|
||||
<td style="padding:4px 8px;border-bottom:1px solid var(--brand-gray);color:var(--muted)">класс или диапазон
|
||||
символов</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:4px 8px;border-bottom:1px solid var(--brand-gray);white-space:nowrap"><code>[!a]</code>,
|
||||
<code>[^a]</code></td>
|
||||
<code>[^a]</code>
|
||||
</td>
|
||||
<td style="padding:4px 8px;border-bottom:1px solid var(--brand-gray);color:var(--muted)">любой символ, кроме
|
||||
указанных</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:4px 8px;border-bottom:1px solid var(--brand-gray);white-space:nowrap">
|
||||
<code>reports/</code></td>
|
||||
<code>reports/</code>
|
||||
</td>
|
||||
<td style="padding:4px 8px;border-bottom:1px solid var(--brand-gray);color:var(--muted)">то же, что
|
||||
<code>reports/**</code> — всё поддерево</td>
|
||||
<code>reports/**</code> — всё поддерево
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -1043,7 +1037,7 @@ reports/name*.docx
|
||||
<div
|
||||
style="background:var(--card);border-radius:12px;padding:24px;max-width:440px;box-shadow:0 4px 24px rgba(0,0,0,.15)"
|
||||
onclick="event.stopPropagation()">
|
||||
<h3 style="margin-bottom:12px">⏹ Остановить обработку?</h3>
|
||||
<h3 style="margin-bottom:12px">Остановить обработку?</h3>
|
||||
<p style="font-size:13px;color:var(--muted);line-height:1.6" id="cancelModalText">
|
||||
Будут сохранены: полностью обработанные файлы и таблица замен.
|
||||
Необработанные файлы в результат не попадут.
|
||||
|
||||
Reference in New Issue
Block a user