diff --git a/public/pdf.html b/public/pdf.html
index f76241f..10eb724 100644
--- a/public/pdf.html
+++ b/public/pdf.html
@@ -203,6 +203,21 @@
.page-thumb-check { position:absolute; top:4px; right:4px; width:18px; height:18px; border-radius:50%; border:2px solid rgba(255,255,255,.5); background:rgba(0,0,0,.3); display:none; align-items:center; justify-content:center; }
.page-thumb.selected .page-thumb-check { display:flex; background:var(--accent); border-color:var(--accent); }
.page-thumb-check svg { width:12px; height:12px; }
+ .page-thumb.rotate-90 img { transform: rotate(90deg); }
+ .page-thumb.rotate-180 img { transform: rotate(180deg); }
+ .page-thumb.rotate-270 img { transform: rotate(270deg); }
+ .page-thumb.dragging { opacity: 0.4; border-color: var(--accent); }
+ .page-thumb.drag-over { border-color: var(--accent); transform: scale(1.05); }
+ .page-thumb.marked-delete { border-color: #ef4444; opacity: 0.6; }
+ .page-thumb.marked-delete .page-thumb-check { display: flex; background: #ef4444; border-color: #ef4444; }
+ .page-thumb.marked-delete .page-thumb-num { background: rgba(239,68,68,.8); }
+ .grid-action-bar { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
+ .grid-action-btn { padding: 8px 16px; border-radius: 8px; font-size: 13px; font-weight: 600; border: none; cursor: pointer; font-family: 'Manrope', sans-serif; transition: all .15s; }
+ .grid-action-btn.danger { background: rgba(239,68,68,.1); color: #ef4444; border: 1px solid rgba(239,68,68,.3); }
+ .grid-action-btn.danger:hover { background: rgba(239,68,68,.2); }
+ .grid-action-btn.primary { background: var(--accent); color: #fff; }
+ .grid-action-btn.primary:hover { background: var(--accent-dim); }
+ .grid-action-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.page-grid-hint { font-size:11px; color:var(--text-muted); margin-top:6px; font-family:'JetBrains Mono',monospace; }
.file-card-remove:hover {
@@ -727,27 +742,36 @@
- Страницы (нажмите для выбора)
+ Нажмите на страницы для поворота
- Загрузите файл для просмотра страниц
+
+
+
+
-
Выберите страницы для удаления
+
Нажмите на страницы для удаления
-
Загрузите файл для просмотра страниц
+
+
+
+
-
Перетаскивайте страницы для изменения порядка
+
Перетаскивайте страницы мышкой для нового порядка
-
Загрузите файл для просмотра страниц
+
+
+
+
@@ -1676,27 +1700,88 @@
if (!grid) return;
if (!fileId || pages < 1) {
grid.innerHTML = '';
- if (hint) hint.textContent = 'Выберите файл';
+ if (hint) hint.textContent = '';
return;
}
- if (hint) hint.textContent = mode === 'delete' ? 'Нажмите на страницы для выбора' : mode === 'rotate' ? 'Нажмите для выбора страниц' : 'Перетаскивайте для изменения порядка';
+ if (hint) hint.textContent = '';
grid.innerHTML = '';
- for (let i = 1; i <= pages; i++) {
+ for (let i = 1; i <= Math.min(pages, 100); i++) {
const thumb = document.createElement('div');
thumb.className = 'page-thumb';
thumb.dataset.page = i;
- thumb.innerHTML = `
${i}
`;
+ thumb.innerHTML = '
' + i + '
';
- if (mode === 'delete' || mode === 'rotate') {
+ if (mode === 'delete') {
thumb.addEventListener('click', () => {
thumb.classList.toggle('selected');
+ thumb.classList.toggle('marked-delete');
+ updateDeleteCount();
+ });
+ } else if (mode === 'rotate') {
+ thumb.addEventListener('click', () => {
+ thumb.classList.toggle('selected');
+ // Visual rotation preview
+ if (thumb.classList.contains('selected')) {
+ const angle = state.rotateAngle || 90;
+ thumb.classList.add('rotate-' + angle);
+ } else {
+ thumb.classList.remove('rotate-90', 'rotate-180', 'rotate-270');
+ }
+ updateRotateCount();
+ });
+ } else if (mode === 'reorder') {
+ // Drag and drop
+ thumb.draggable = true;
+ thumb.addEventListener('dragstart', e => {
+ e.dataTransfer.setData('text/plain', i);
+ thumb.classList.add('dragging');
+ });
+ thumb.addEventListener('dragend', () => thumb.classList.remove('dragging'));
+ thumb.addEventListener('dragover', e => { e.preventDefault(); thumb.classList.add('drag-over'); });
+ thumb.addEventListener('dragleave', () => thumb.classList.remove('drag-over'));
+ thumb.addEventListener('drop', e => {
+ e.preventDefault();
+ thumb.classList.remove('drag-over');
+ const fromPage = e.dataTransfer.getData('text/plain');
+ const fromEl = grid.querySelector('[data-page="' + fromPage + '"]');
+ if (fromEl && fromEl !== thumb) {
+ // Swap positions in DOM
+ const parent = thumb.parentNode;
+ const thumbNext = thumb.nextSibling;
+ if (fromEl.nextSibling === thumb) {
+ parent.insertBefore(thumb, fromEl);
+ } else {
+ parent.insertBefore(fromEl, thumb);
+ parent.insertBefore(thumb, thumbNext);
+ }
+ // Update visual numbers
+ Array.from(grid.children).forEach((el, idx) => {
+ el.querySelector('.page-thumb-num').textContent = idx + 1;
+ });
+ }
});
}
grid.appendChild(thumb);
}
}
+ function updateDeleteCount() {
+ const count = getSelectedPages('deletePageGrid').length;
+ const el = document.getElementById('deleteCount');
+ const btn = document.getElementById('btnDeleteSelected');
+ if (el) el.textContent = count;
+ if (btn) btn.disabled = count === 0;
+ }
+
+ function updateRotateCount() {
+ const count = getSelectedPages('rotatePageGrid').length;
+ const el = document.getElementById('rotateCount');
+ const btn = document.getElementById('btnRotateSelected');
+ if (el) el.textContent = count;
+ if (btn) btn.disabled = count === 0;
+ }
+
function getSelectedPages(gridId) {
const grid = document.getElementById(gridId);
if (!grid) return [];
@@ -1705,6 +1790,60 @@
+
+ // Direct execute for page operations (from grid action buttons)
+ window.executeDeletePages = async function() {
+ const pages = getSelectedPages('deletePageGrid');
+ if (!pages.length) { showError('Выберите страницы'); return; }
+ const fileId = state.selectedFileId;
+ if (!fileId) { showError('Выберите файл'); return; }
+ clearError();
+ try {
+ const res = await fetch('/pdf/delete', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ fileId, pages: pages.join(',') }) });
+ const data = await res.json();
+ if (!res.ok) throw new Error(data.error);
+ showResult(data);
+ } catch(e) { showError(e.message); }
+ };
+
+ window.executeRotatePages = async function() {
+ const pages = getSelectedPages('rotatePageGrid');
+ const fileId = state.selectedFileId;
+ if (!fileId) { showError('Выберите файл'); return; }
+ clearError();
+ try {
+ const res = await fetch('/pdf/rotate', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ fileId, pages: pages.length ? pages.join(',') : 'all', angle: state.rotateAngle || 90 }) });
+ const data = await res.json();
+ if (!res.ok) throw new Error(data.error);
+ showResult(data);
+ } catch(e) { showError(e.message); }
+ };
+
+ window.executeReorderPages = async function() {
+ const grid = document.getElementById('reorderPageGrid');
+ if (!grid) return;
+ const order = Array.from(grid.querySelectorAll('.page-thumb')).map(t => parseInt(t.dataset.page));
+ const fileId = state.selectedFileId;
+ if (!fileId) { showError('Выберите файл'); return; }
+ clearError();
+ try {
+ const res = await fetch('/pdf/reorder', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ fileId, order }) });
+ const data = await res.json();
+ if (!res.ok) throw new Error(data.error);
+ showResult(data);
+ } catch(e) { showError(e.message); }
+ };
+
+ function showResult(data) {
+ if (data.downloadUrl) {
+ document.getElementById('downloadLink').href = data.downloadUrl;
+ document.getElementById('downloadLink').style.display = '';
+ if (data.size) document.getElementById('resultSize').textContent = formatSize(data.size);
+ stepConfig.style.display = 'none';
+ stepResult.style.display = 'block';
+ }
+ }
+
})();