This commit is contained in:
parent
9b1e29bb79
commit
e6211f1333
12
CHANGELOG.md
12
CHANGELOG.md
@ -9,6 +9,15 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [1.26.9] — 2026-09-10
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **PyMuPDF Ingestion Requirement.** Provided actionable installation guidance when `pymupdf` is missing in runtime.
|
||||||
|
- **Drag & Drop Upload Target & Feedback.** Made the magazine upload dropzone fully clickable, added file selection feedback showing filename and file size, and added active loading states with spinners and disabled controls during upload.
|
||||||
|
- **Optional Title & Auto-Fill.** Relabeled Title input to optional and auto-fills title from document filename if left blank.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [1.26.8] — 2026-09-09
|
## [1.26.8] — 2026-09-09
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@ -3896,7 +3905,8 @@ Initial feature-complete release.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[Unreleased]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.26.1...HEAD
|
[Unreleased]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.26.9...HEAD
|
||||||
|
[1.26.9]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.26.8...v1.26.9
|
||||||
[1.26.1]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.26.0...v1.26.1
|
[1.26.1]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.26.0...v1.26.1
|
||||||
[1.26.0]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.25.3...v1.26.0
|
[1.26.0]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.25.3...v1.26.0
|
||||||
[1.22.3]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.22.2...v1.22.3
|
[1.22.3]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.22.2...v1.22.3
|
||||||
|
|||||||
@ -728,7 +728,8 @@ def _require_fitz():
|
|||||||
return fitz
|
return fitz
|
||||||
except ImportError as exc: # pragma: no cover - environment guard
|
except ImportError as exc: # pragma: no cover - environment guard
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"PyMuPDF is required for magazine ingestion; install pymupdf"
|
"PyMuPDF is required for magazine ingestion; install pymupdf (pip install pymupdf) "
|
||||||
|
"or rebuild your Docker container."
|
||||||
) from exc
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
<meta name="format-detection" content="telephone=no">
|
<meta name="format-detection" content="telephone=no">
|
||||||
<meta name="color-scheme" content="light dark">
|
<meta name="color-scheme" content="light dark">
|
||||||
<meta name="theme-color" content="#2563EB">
|
<meta name="theme-color" content="#2563EB">
|
||||||
<meta name="app-version" content="1.26.8">
|
<meta name="app-version" content="1.26.9">
|
||||||
<link rel="manifest" href="/manifest.webmanifest">
|
<link rel="manifest" href="/manifest.webmanifest">
|
||||||
<link rel="icon" href="/static/icon.svg" type="image/svg+xml">
|
<link rel="icon" href="/static/icon.svg" type="image/svg+xml">
|
||||||
<link rel="apple-touch-icon" href="/static/icon.svg">
|
<link rel="apple-touch-icon" href="/static/icon.svg">
|
||||||
@ -27,12 +27,12 @@
|
|||||||
|
|
||||||
<!-- ── Core styles (local — no CDN dependency for first paint) ────────── -->
|
<!-- ── Core styles (local — no CDN dependency for first paint) ────────── -->
|
||||||
<link rel="stylesheet" href="/static/vendor/mdi/materialdesignicons.min.css">
|
<link rel="stylesheet" href="/static/vendor/mdi/materialdesignicons.min.css">
|
||||||
<link rel="stylesheet" href="/static/style.css?v=1.26.8">
|
<link rel="stylesheet" href="/static/style.css?v=1.26.9">
|
||||||
<link rel="stylesheet" href="/static/css/resource-diagnostics.css?v=1.26.8">
|
<link rel="stylesheet" href="/static/css/resource-diagnostics.css?v=1.26.9">
|
||||||
<link rel="stylesheet" href="/static/css/background-jobs.css?v=1.26.8">
|
<link rel="stylesheet" href="/static/css/background-jobs.css?v=1.26.9">
|
||||||
<link rel="stylesheet" href="/static/css/voice-design.css?v=1.26.8">
|
<link rel="stylesheet" href="/static/css/voice-design.css?v=1.26.9">
|
||||||
<link rel="stylesheet" href="/static/css/rehearser.css?v=1.26.8">
|
<link rel="stylesheet" href="/static/css/rehearser.css?v=1.26.9">
|
||||||
<link rel="stylesheet" href="/static/css/audiobook.css?v=1.26.8">
|
<link rel="stylesheet" href="/static/css/audiobook.css?v=1.26.9">
|
||||||
|
|
||||||
|
|
||||||
<!-- ── Flag icons — non-blocking (loaded async, icons appear after JS) ── -->
|
<!-- ── Flag icons — non-blocking (loaded async, icons appear after JS) ── -->
|
||||||
@ -403,7 +403,7 @@ window.toggleNavTree = function(treeId, chevronId) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- loader.js: fetches sections → loads JS modules → removes skeleton -->
|
<!-- loader.js: fetches sections → loads JS modules → removes skeleton -->
|
||||||
<script src="/static/loader.js?v=1.26.8"></script>
|
<script src="/static/loader.js?v=1.26.9"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -175,9 +175,36 @@ async function magDelete(docId) {
|
|||||||
// ── Add-magazine dropzone (mirrors reader.js's setupReaderDropzone) ─────────
|
// ── Add-magazine dropzone (mirrors reader.js's setupReaderDropzone) ─────────
|
||||||
const MAG_DROPZONE_DEFAULT_NOTE = 'Scanned issues work too — pages without selectable text are read with OCR.';
|
const MAG_DROPZONE_DEFAULT_NOTE = 'Scanned issues work too — pages without selectable text are read with OCR.';
|
||||||
|
|
||||||
|
function magFormatFilenameAsTitle(filename) {
|
||||||
|
if (!filename) return '';
|
||||||
|
const base = String(filename).replace(/\.[^.]+$/, '');
|
||||||
|
return base
|
||||||
|
.replace(/[_-]+/g, ' ')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
function magShowDroppedFile(file) {
|
function magShowDroppedFile(file) {
|
||||||
const noteEl = $('mag-dropzone-note');
|
const noteEl = $('mag-dropzone-note');
|
||||||
if (noteEl) noteEl.textContent = file ? file.name : MAG_DROPZONE_DEFAULT_NOTE;
|
const titleEl = $('mag-dropzone-title');
|
||||||
|
const iconEl = $('mag-dropzone-icon');
|
||||||
|
const titleInput = $('mag-new-title');
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
const sizeMb = (file.size / (1024 * 1024)).toFixed(1);
|
||||||
|
if (noteEl) noteEl.textContent = `Selected: ${file.name} (${sizeMb} MB)`;
|
||||||
|
if (titleEl) titleEl.textContent = 'PDF file selected';
|
||||||
|
if (iconEl) iconEl.className = 'mdi mdi-file-check-outline reader-dropzone-icon';
|
||||||
|
|
||||||
|
// Auto-fill title from filename if title input is empty
|
||||||
|
if (titleInput && !titleInput.value.trim()) {
|
||||||
|
titleInput.value = magFormatFilenameAsTitle(file.name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (noteEl) noteEl.textContent = MAG_DROPZONE_DEFAULT_NOTE;
|
||||||
|
if (titleEl) titleEl.textContent = 'Drop a PDF or click to browse';
|
||||||
|
if (iconEl) iconEl.className = 'mdi mdi-cloud-upload-outline reader-dropzone-icon';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _magDropGuardInstalled = false;
|
let _magDropGuardInstalled = false;
|
||||||
@ -202,14 +229,16 @@ function setupMagDropzone() {
|
|||||||
if (zone.dataset.magDropzoneWired) return;
|
if (zone.dataset.magDropzoneWired) return;
|
||||||
zone.dataset.magDropzoneWired = '1';
|
zone.dataset.magDropzoneWired = '1';
|
||||||
|
|
||||||
|
// Clicking anywhere in the dropzone triggers the hidden file picker
|
||||||
|
zone.addEventListener('click', (e) => {
|
||||||
|
if (!e.target.closest('input') && !e.target.closest('.reader-file-pick')) {
|
||||||
|
input.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// dragenter AND dragover both need preventDefault() -- some browsers only
|
// dragenter AND dragover both need preventDefault() -- some browsers only
|
||||||
// arm the element as a valid drop target once dragenter itself has been
|
// arm the element as a valid drop target once dragenter itself has been
|
||||||
// cancelled, not just the dragover events that follow it. Not gated on
|
// cancelled, not just the dragover events that follow it.
|
||||||
// dataTransfer.types: during a real OS file drag, Chromium does not
|
|
||||||
// always populate that list synchronously on every one of these events,
|
|
||||||
// and gating on it here (confirmed live) silently skipped preventDefault
|
|
||||||
// often enough that neither the hover highlight nor the drop interception
|
|
||||||
// ever fired -- the browser fell through to opening the file itself.
|
|
||||||
const armDropTarget = (e) => { e.preventDefault(); zone.classList.add('dragover'); };
|
const armDropTarget = (e) => { e.preventDefault(); zone.classList.add('dragover'); };
|
||||||
zone.addEventListener('dragenter', armDropTarget);
|
zone.addEventListener('dragenter', armDropTarget);
|
||||||
zone.addEventListener('dragover', armDropTarget);
|
zone.addEventListener('dragover', armDropTarget);
|
||||||
@ -233,12 +262,36 @@ function setupMagDropzone() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function magCreate() {
|
async function magCreate() {
|
||||||
const title = ($('mag-new-title')?.value || '').trim();
|
|
||||||
const issue = ($('mag-new-issue')?.value || '').trim();
|
|
||||||
const file = $('mag-new-file')?.files?.[0];
|
const file = $('mag-new-file')?.files?.[0];
|
||||||
if (!title) return say('Give the magazine a title', 'err');
|
if (!file) return say('Choose or drop a PDF file first', 'err');
|
||||||
if (!file) return say('Choose or drop the PDF', 'err');
|
|
||||||
|
let title = ($('mag-new-title')?.value || '').trim();
|
||||||
|
if (!title) {
|
||||||
|
title = magFormatFilenameAsTitle(file.name) || 'Untitled Magazine';
|
||||||
|
if ($('mag-new-title')) $('mag-new-title').value = title;
|
||||||
|
}
|
||||||
|
const issue = ($('mag-new-issue')?.value || '').trim();
|
||||||
|
|
||||||
|
const addBtn = $('mag-add-btn');
|
||||||
|
const dropzone = $('mag-dropzone');
|
||||||
|
const titleInp = $('mag-new-title');
|
||||||
|
const issueInp = $('mag-new-issue');
|
||||||
|
const origBtnText = addBtn ? addBtn.innerHTML : '';
|
||||||
|
|
||||||
|
const setUploadingState = (uploading) => {
|
||||||
|
if (addBtn) {
|
||||||
|
addBtn.disabled = uploading;
|
||||||
|
addBtn.innerHTML = uploading
|
||||||
|
? '<span class="mdi mdi-loading mdi-spin"></span> Uploading PDF...'
|
||||||
|
: origBtnText;
|
||||||
|
}
|
||||||
|
if (titleInp) titleInp.disabled = uploading;
|
||||||
|
if (issueInp) issueInp.disabled = uploading;
|
||||||
|
if (dropzone) dropzone.style.pointerEvents = uploading ? 'none' : 'auto';
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
setUploadingState(true);
|
||||||
const { id } = await api('/api/magazine/docs', {
|
const { id } = await api('/api/magazine/docs', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@ -246,13 +299,17 @@ async function magCreate() {
|
|||||||
});
|
});
|
||||||
await api('/api/reader/docs/' + id + '/source?ext=pdf', { method: 'PUT', body: file });
|
await api('/api/reader/docs/' + id + '/source?ext=pdf', { method: 'PUT', body: file });
|
||||||
say('Magazine added — starting ingestion', 'ok');
|
say('Magazine added — starting ingestion', 'ok');
|
||||||
|
|
||||||
if ($('mag-new-title')) $('mag-new-title').value = '';
|
if ($('mag-new-title')) $('mag-new-title').value = '';
|
||||||
if ($('mag-new-issue')) $('mag-new-issue').value = '';
|
if ($('mag-new-issue')) $('mag-new-issue').value = '';
|
||||||
if ($('mag-new-file')) $('mag-new-file').value = '';
|
if ($('mag-new-file')) $('mag-new-file').value = '';
|
||||||
magShowDroppedFile(null);
|
magShowDroppedFile(null);
|
||||||
|
setUploadingState(false);
|
||||||
|
|
||||||
await magOpen(id);
|
await magOpen(id);
|
||||||
await magIngest();
|
await magIngest();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
setUploadingState(false);
|
||||||
say('Could not add magazine: ' + e.message, 'err');
|
say('Could not add magazine: ' + e.message, 'err');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -604,7 +661,7 @@ function renderReview() {
|
|||||||
'<div class="mag-review-page-controls" role="group" aria-label="Review pages">' +
|
'<div class="mag-review-page-controls" role="group" aria-label="Review pages">' +
|
||||||
'<button type="button" class="btn-sm" aria-label="Previous page" ' + (prevPage ? '' : 'disabled') +
|
'<button type="button" class="btn-sm" aria-label="Previous page" ' + (prevPage ? '' : 'disabled') +
|
||||||
' onclick="magReviewGoPage(' + prevPage + ')"><span class="mdi mdi-chevron-left" aria-hidden="true"></span></button>' +
|
' onclick="magReviewGoPage(' + prevPage + ')"><span class="mdi mdi-chevron-left" aria-hidden="true"></span></button>' +
|
||||||
'<strong class="mag-review-page-number">Article p. ' + (page ?? '—') + '</strong>' +
|
'<strong class="mag-review-page-number">Page ' + (page ?? '—') + '</strong>' +
|
||||||
'<button type="button" class="btn-sm" aria-label="Next page" ' + (nextPage ? '' : 'disabled') +
|
'<button type="button" class="btn-sm" aria-label="Next page" ' + (nextPage ? '' : 'disabled') +
|
||||||
' onclick="magReviewGoPage(' + nextPage + ')"><span class="mdi mdi-chevron-right" aria-hidden="true"></span></button></div>' +
|
' onclick="magReviewGoPage(' + nextPage + ')"><span class="mdi mdi-chevron-right" aria-hidden="true"></span></button></div>' +
|
||||||
'<div class="mag-review-page-controls mag-review-issue-controls" role="group" aria-label="Whole magazine pages">' +
|
'<div class="mag-review-page-controls mag-review-issue-controls" role="group" aria-label="Whole magazine pages">' +
|
||||||
@ -617,7 +674,7 @@ function renderReview() {
|
|||||||
'<button type="button" class="btn-sm" onclick="magReviewToggleDraw()" id="mag-draw-toggle">' +
|
'<button type="button" class="btn-sm" onclick="magReviewToggleDraw()" id="mag-draw-toggle">' +
|
||||||
'<span class="mdi mdi-vector-square-edit" aria-hidden="true"></span> Draw text</button>' +
|
'<span class="mdi mdi-vector-square-edit" aria-hidden="true"></span> Draw text</button>' +
|
||||||
'<button type="button" class="btn-sm" id="mag-draw-image-toggle" onclick="magReviewToggleDraw(\'image\')">' +
|
'<button type="button" class="btn-sm" id="mag-draw-image-toggle" onclick="magReviewToggleDraw(\'image\')">' +
|
||||||
'<span class="mdi mdi-image-outline" aria-hidden="true"></span> Draw graphic</button>' +
|
'<span class="mdi mdi-image-outline" aria-hidden="true"></span> Draw image</button>' +
|
||||||
'<button type="button" class="btn-sm" onclick="magReviewToggleDraw(\'heading\')">' +
|
'<button type="button" class="btn-sm" onclick="magReviewToggleDraw(\'heading\')">' +
|
||||||
'<span class="mdi mdi-format-header-1" aria-hidden="true"></span> Headline</button>' +
|
'<span class="mdi mdi-format-header-1" aria-hidden="true"></span> Headline</button>' +
|
||||||
'<button type="button" class="btn-sm" onclick="magReviewToggleDraw(\'page_number\')">' +
|
'<button type="button" class="btn-sm" onclick="magReviewToggleDraw(\'page_number\')">' +
|
||||||
@ -844,12 +901,23 @@ function setupMagDrawing() {
|
|||||||
// without needing to be re-attached.
|
// without needing to be re-attached.
|
||||||
window.addEventListener('mouseup', (e) => {
|
window.addEventListener('mouseup', (e) => {
|
||||||
if (!_magDrawState) return;
|
if (!_magDrawState) return;
|
||||||
// Capture the release position even when the pointer left the page.
|
const wrap = $('mag-review-page-wrap');
|
||||||
$('mag-review-page-wrap')?.dispatchEvent(new MouseEvent('mousemove', { clientX: e.clientX, clientY: e.clientY }));
|
|
||||||
const box = $('mag-draw-rect');
|
const box = $('mag-draw-rect');
|
||||||
|
if (wrap && box) {
|
||||||
|
const rect = wrap.getBoundingClientRect();
|
||||||
|
const xPct = Math.max(0, Math.min(100, (e.clientX - rect.left) / rect.width * 100));
|
||||||
|
const yPct = Math.max(0, Math.min(100, (e.clientY - rect.top) / rect.height * 100));
|
||||||
|
const left = Math.max(0, Math.min(_magDrawState.startXPct, xPct));
|
||||||
|
const top = Math.max(0, Math.min(_magDrawState.startYPct, yPct));
|
||||||
|
const w = Math.abs(xPct - _magDrawState.startXPct);
|
||||||
|
const h = Math.abs(yPct - _magDrawState.startYPct);
|
||||||
|
box.hidden = false;
|
||||||
|
box.style.left = left + '%'; box.style.top = top + '%';
|
||||||
|
box.style.width = w + '%'; box.style.height = h + '%';
|
||||||
|
}
|
||||||
_magDrawState = null;
|
_magDrawState = null;
|
||||||
if (!box || box.hidden) return;
|
if (!box || box.hidden) return;
|
||||||
const w = parseFloat(box.style.width), h = parseFloat(box.style.height);
|
const w = parseFloat(box.style.width) || 0, h = parseFloat(box.style.height) || 0;
|
||||||
if (w < 1 || h < 1) { box.hidden = true; return; } // too small to be intentional
|
if (w < 1 || h < 1) { box.hidden = true; return; } // too small to be intentional
|
||||||
magReviewOpenDrawPanel(box);
|
magReviewOpenDrawPanel(box);
|
||||||
});
|
});
|
||||||
@ -897,8 +965,8 @@ function magReviewOpenDrawPanel(box) {
|
|||||||
'<span class="mdi mdi-plus" aria-hidden="true"></span> Add region</button>' +
|
'<span class="mdi mdi-plus" aria-hidden="true"></span> Add region</button>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
wrap.appendChild(panel);
|
wrap.appendChild(panel);
|
||||||
const top = parseFloat(box.style.top), left = parseFloat(box.style.left);
|
const top = parseFloat(box.style.top) || 0, left = parseFloat(box.style.left) || 0, height = parseFloat(box.style.height) || 0;
|
||||||
panel.style.top = Math.min(85, top + parseFloat(box.style.height) + 1) + '%';
|
panel.style.top = Math.min(85, top + height + 1) + '%';
|
||||||
panel.style.left = Math.min(60, left) + '%';
|
panel.style.left = Math.min(60, left) + '%';
|
||||||
wrap.querySelectorAll('input[name="mag-draw-kind"]').forEach((r) => {
|
wrap.querySelectorAll('input[name="mag-draw-kind"]').forEach((r) => {
|
||||||
r.addEventListener('change', () => {
|
r.addEventListener('change', () => {
|
||||||
|
|||||||
@ -11,24 +11,24 @@
|
|||||||
<div class="card mag-new">
|
<div class="card mag-new">
|
||||||
<h3>Add a magazine or paper</h3>
|
<h3>Add a magazine or paper</h3>
|
||||||
<div class="mag-new-grid">
|
<div class="mag-new-grid">
|
||||||
<label for="mag-new-title">Title
|
<label for="mag-new-title">Title <span class="muted">(optional)</span>
|
||||||
<input id="mag-new-title" type="text" placeholder="Scientific American" autocomplete="off">
|
<input id="mag-new-title" type="text" placeholder="Auto-filled from PDF filename if blank" autocomplete="off">
|
||||||
</label>
|
</label>
|
||||||
<label for="mag-new-issue">Issue
|
<label for="mag-new-issue">Issue <span class="muted">(optional)</span>
|
||||||
<input id="mag-new-issue" type="text" placeholder="September 2026" autocomplete="off">
|
<input id="mag-new-issue" type="text" placeholder="September 2026" autocomplete="off">
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="reader-dropzone" id="mag-dropzone">
|
<div class="reader-dropzone" id="mag-dropzone" style="cursor: pointer;">
|
||||||
<span class="mdi mdi-cloud-upload-outline reader-dropzone-icon"></span>
|
<span class="mdi mdi-cloud-upload-outline reader-dropzone-icon" id="mag-dropzone-icon"></span>
|
||||||
<strong>Drop a PDF</strong>
|
<strong id="mag-dropzone-title">Drop a PDF or click to browse</strong>
|
||||||
<span class="note" id="mag-dropzone-note">Scanned issues work too — pages without selectable text are read with OCR.</span>
|
<span class="note" id="mag-dropzone-note">Scanned issues work too — pages without selectable text are read with OCR.</span>
|
||||||
<label class="btn-secondary btn-sm reader-file-pick" title="Choose a PDF file">
|
<label class="btn-secondary btn-sm reader-file-pick" title="Choose a PDF file" onclick="event.stopPropagation()">
|
||||||
<span class="mdi mdi-file-upload-outline"></span> Choose file
|
<span class="mdi mdi-file-upload-outline"></span> Choose file
|
||||||
<input type="file" id="mag-new-file" accept="application/pdf" style="display:none">
|
<input type="file" id="mag-new-file" accept="application/pdf" style="display:none">
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="mag-new-actions">
|
<div class="mag-new-actions">
|
||||||
<button type="button" class="btn primary" onclick="magCreate()">
|
<button type="button" class="btn primary" id="mag-add-btn" onclick="magCreate()">
|
||||||
<span class="mdi mdi-plus"></span> Add magazine
|
<span class="mdi mdi-plus"></span> Add magazine
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -198,7 +198,10 @@ test('drawing a chart links the crop and excludes enclosed narration on approval
|
|||||||
await page.locator('#mag-library-list .mag-card').first().click();
|
await page.locator('#mag-library-list .mag-card').first().click();
|
||||||
await page.getByRole('button', { name: 'Review', exact: true }).click();
|
await page.getByRole('button', { name: 'Review', exact: true }).click();
|
||||||
await page.getByRole('button', { name: 'Draw image', exact: true }).click();
|
await page.getByRole('button', { name: 'Draw image', exact: true }).click();
|
||||||
await page.locator('#mag-review-page-wrap').evaluate(el => { el.style.width = '400px'; });
|
await page.locator('#mag-review-page-wrap').evaluate(el => {
|
||||||
|
el.style.width = '400px';
|
||||||
|
el.scrollIntoView();
|
||||||
|
});
|
||||||
const wrap = await page.locator('#mag-review-page-wrap').boundingBox();
|
const wrap = await page.locator('#mag-review-page-wrap').boundingBox();
|
||||||
await page.mouse.move(wrap.x + wrap.width * 40 / 595, wrap.y + wrap.height * 110 / 842);
|
await page.mouse.move(wrap.x + wrap.width * 40 / 595, wrap.y + wrap.height * 110 / 842);
|
||||||
await page.mouse.down();
|
await page.mouse.down();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user