This commit is contained in:
parent
03597d37f0
commit
48acbf6e00
10
CHANGELOG.md
10
CHANGELOG.md
@ -9,6 +9,13 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [1.44.0] — 2026-09-22
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **Magazine: Draw region stays armed after adding a box.** Previously every box meant clicking the toolbar button again; it's now a toggle (with an on/off switch icon on the button itself, since the background colour alone was easy to miss after a few boxes in a row) that stays active until you turn it off.
|
||||||
|
- **Magazine: a freshly drawn region is ticked in automatically** when its type is one that's actually narrated (Paragraph, Headline, Subheading, Standfirst, Author) — drawing a region already means it belongs in the article. Furniture-like kinds (Page number, Source/footer, Advertisement, Table, Duplicate, OCR noise, Caption, Pull quote, Sidenote, Text in picture) stay unticked, since those are excluded from narration on purpose.
|
||||||
|
- **Magazine: the region type list is now searchable, with an icon per type.** Filter any of the 16 kinds by typing instead of scrolling a plain dropdown.
|
||||||
|
|
||||||
## [1.43.1] — 2026-09-22
|
## [1.43.1] — 2026-09-22
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
@ -4321,7 +4328,8 @@ Initial feature-complete release.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[Unreleased]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.43.1...HEAD
|
[Unreleased]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.44.0...HEAD
|
||||||
|
[1.44.0]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.43.1...v1.44.0
|
||||||
[1.43.1]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.43.0...v1.43.1
|
[1.43.1]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.43.0...v1.43.1
|
||||||
[1.26.10]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.26.9...v1.26.10
|
[1.26.10]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.26.9...v1.26.10
|
||||||
[1.26.9]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.26.8...v1.26.9
|
[1.26.9]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.26.8...v1.26.9
|
||||||
|
|||||||
@ -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.43.1">
|
<meta name="app-version" content="1.44.0">
|
||||||
<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.43.1">
|
<link rel="stylesheet" href="/static/style.css?v=1.44.0">
|
||||||
<link rel="stylesheet" href="/static/css/resource-diagnostics.css?v=1.43.1">
|
<link rel="stylesheet" href="/static/css/resource-diagnostics.css?v=1.44.0">
|
||||||
<link rel="stylesheet" href="/static/css/background-jobs.css?v=1.43.1">
|
<link rel="stylesheet" href="/static/css/background-jobs.css?v=1.44.0">
|
||||||
<link rel="stylesheet" href="/static/css/voice-design.css?v=1.43.1">
|
<link rel="stylesheet" href="/static/css/voice-design.css?v=1.44.0">
|
||||||
<link rel="stylesheet" href="/static/css/rehearser.css?v=1.43.1">
|
<link rel="stylesheet" href="/static/css/rehearser.css?v=1.44.0">
|
||||||
<link rel="stylesheet" href="/static/css/audiobook.css?v=1.43.1">
|
<link rel="stylesheet" href="/static/css/audiobook.css?v=1.44.0">
|
||||||
|
|
||||||
|
|
||||||
<!-- ── 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.43.1"></script>
|
<script src="/static/loader.js?v=1.44.0"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -919,12 +919,26 @@ function magReviewGoMagazinePage(page) {
|
|||||||
// read" without reading the label. No table detection exists yet (P2), so a
|
// read" without reading the label. No table detection exists yet (P2), so a
|
||||||
// table currently reads as its own text region like any other paragraph.
|
// table currently reads as its own text region like any other paragraph.
|
||||||
// What a region can be changed into from the review list. Values match the server's REGION_TYPES.
|
// What a region can be changed into from the review list. Values match the server's REGION_TYPES.
|
||||||
|
// Mirrors core/magazine.py's NARRATABLE -- the kinds that are actually read
|
||||||
|
// aloud. A freshly drawn region of one of these is ticked in automatically
|
||||||
|
// (see magReviewSubmitDraw); everything else (page number, source, ad,
|
||||||
|
// table, duplicate, OCR noise, caption, pull quote, sidenote) is furniture
|
||||||
|
// or reference material excluded from narration on purpose and stays unticked.
|
||||||
|
const MAG_NARRATABLE_KINDS = new Set(['title', 'heading', 'deck', 'byline', 'body']);
|
||||||
const MAG_REGION_TYPES = [
|
const MAG_REGION_TYPES = [
|
||||||
['body', 'Paragraph'], ['title', 'Headline'], ['heading', 'Subheading'], ['deck', 'Standfirst'],
|
['body', 'Paragraph'], ['title', 'Headline'], ['heading', 'Subheading'], ['deck', 'Standfirst'],
|
||||||
['byline', 'Author'], ['caption', 'Caption'], ['pullquote', 'Pull quote'], ['sidebar', 'Sidenote'],
|
['byline', 'Author'], ['caption', 'Caption'], ['pullquote', 'Pull quote'], ['sidebar', 'Sidenote'],
|
||||||
['page_number', 'Page number'], ['source', 'Source / footer'], ['ad', 'Advertisement'],
|
['page_number', 'Page number'], ['source', 'Source / footer'], ['ad', 'Advertisement'],
|
||||||
['in_picture', 'Text in picture'], ['ocr_noise', 'OCR noise'], ['duplicate', 'Duplicate'], ['table', 'Table'], ['image', 'Image'],
|
['in_picture', 'Text in picture'], ['ocr_noise', 'OCR noise'], ['duplicate', 'Duplicate'], ['table', 'Table'], ['image', 'Image'],
|
||||||
];
|
];
|
||||||
|
const MAG_REGION_TYPE_ICONS = {
|
||||||
|
body: 'mdi-text-box-outline', title: 'mdi-format-title', heading: 'mdi-format-header-2',
|
||||||
|
deck: 'mdi-text-short', byline: 'mdi-account-edit-outline', caption: 'mdi-image-text',
|
||||||
|
pullquote: 'mdi-format-quote-close', sidebar: 'mdi-note-text-outline', page_number: 'mdi-numeric',
|
||||||
|
source: 'mdi-book-open-page-variant-outline', ad: 'mdi-advertisements-outline',
|
||||||
|
in_picture: 'mdi-text-recognition', ocr_noise: 'mdi-alert-circle-outline',
|
||||||
|
duplicate: 'mdi-content-duplicate', table: 'mdi-table', image: 'mdi-image-outline',
|
||||||
|
};
|
||||||
// Furniture kinds that the picker shows under their own name (everything else is a page number).
|
// Furniture kinds that the picker shows under their own name (everything else is a page number).
|
||||||
const MAG_FURNITURE_KINDS = ['source', 'in_picture', 'ocr_noise', 'duplicate'];
|
const MAG_FURNITURE_KINDS = ['source', 'in_picture', 'ocr_noise', 'duplicate'];
|
||||||
|
|
||||||
@ -1172,8 +1186,9 @@ function renderReview() {
|
|||||||
'onclick="magReviewToggleSpread()"><span class="mdi mdi-book-open-page-variant" aria-hidden="true"></span> ' +
|
'onclick="magReviewToggleSpread()"><span class="mdi mdi-book-open-page-variant" aria-hidden="true"></span> ' +
|
||||||
(state.reviewSpread ? 'Single page' : 'Two-page spread') + '</button></div>' +
|
(state.reviewSpread ? 'Single page' : 'Two-page spread') + '</button></div>' +
|
||||||
'<div class="mag-review-draw-controls" role="group" aria-label="Draw regions">' +
|
'<div class="mag-review-draw-controls" role="group" aria-label="Draw regions">' +
|
||||||
'<button type="button" class="btn-sm" onclick="magReviewToggleDraw()" id="mag-draw-toggle">' +
|
'<button type="button" class="btn-sm" onclick="magReviewToggleDraw()" id="mag-draw-toggle" aria-pressed="false">' +
|
||||||
'<span class="mdi mdi-vector-square-edit" aria-hidden="true"></span> Draw region</button>' +
|
'<span class="mdi mdi-vector-square-edit" aria-hidden="true"></span> Draw region' +
|
||||||
|
'<span class="mdi mdi-toggle-switch-off-outline mag-draw-toggle-switch" id="mag-draw-toggle-switch" aria-hidden="true"></span></button>' +
|
||||||
'<button type="button" class="btn-sm" id="mag-edit-toggle" aria-pressed="false" ' +
|
'<button type="button" class="btn-sm" id="mag-edit-toggle" aria-pressed="false" ' +
|
||||||
'title="Drag a region to move or resize it, or delete one that was detected wrongly" ' +
|
'title="Drag a region to move or resize it, or delete one that was detected wrongly" ' +
|
||||||
'onclick="magReviewToggleEdit()"><span class="mdi mdi-vector-polygon" aria-hidden="true"></span> Edit boxes</button>' +
|
'onclick="magReviewToggleEdit()"><span class="mdi mdi-vector-polygon" aria-hidden="true"></span> Edit boxes</button>' +
|
||||||
@ -1675,19 +1690,36 @@ function magReviewHighlightBox(blockId) {
|
|||||||
// cover that). Percent-of-image coordinates while dragging, converted to
|
// cover that). Percent-of-image coordinates while dragging, converted to
|
||||||
// PDF-point page coordinates (matching every other bbox in the layout) only
|
// PDF-point page coordinates (matching every other bbox in the layout) only
|
||||||
// once the user commits the region.
|
// once the user commits the region.
|
||||||
function magReviewToggleDraw(kind = 'text') {
|
// Applies state.drawMode/drawKind to the DOM -- pulled out of
|
||||||
state.drawMode = state.drawMode && state.drawKind === kind ? false : true;
|
// magReviewToggleDraw so magReviewSubmitDraw can re-assert the same "still
|
||||||
state.drawKind = kind;
|
// drawing" visuals after renderReview() rebuilds the toolbar from scratch
|
||||||
if (state.drawMode && state.editMode) magReviewToggleEdit();
|
// (which has no memory of drawMode, since that class was only ever applied
|
||||||
|
// imperatively, never baked into the template).
|
||||||
|
function _magSyncDrawUi() {
|
||||||
const hint = $('mag-draw-hint');
|
const hint = $('mag-draw-hint');
|
||||||
const field = kind === 'title' ? 'title' : kind === 'author' ? 'author' : '';
|
const field = state.drawKind === 'title' ? 'title' : state.drawKind === 'author' ? 'author' : '';
|
||||||
if (hint) hint.textContent = state.drawMode
|
if (hint) hint.textContent = state.drawMode
|
||||||
? (field ? ` Drag around the ${field} text to OCR it into this field. Press Escape to cancel.`
|
? (field ? ` Drag around the ${field} text to OCR it into this field. Press Escape to cancel.`
|
||||||
: ' Drag around the region, then say what it is. Press Escape to cancel.') : '';
|
: ' Drag around the region, then say what it is. Press Escape to cancel.') : '';
|
||||||
const wrap = $('mag-review-page-wrap');
|
const wrap = $('mag-review-page-wrap');
|
||||||
if (wrap) wrap.classList.toggle('is-drawing', state.drawMode);
|
if (wrap) wrap.classList.toggle('is-drawing', state.drawMode);
|
||||||
const btn = $('mag-draw-toggle');
|
const btn = $('mag-draw-toggle');
|
||||||
if (btn) { btn.classList.toggle('active', state.drawMode && kind === 'text'); btn.setAttribute('aria-pressed', String(state.drawMode && kind === 'text')); }
|
if (btn) { btn.classList.toggle('active', state.drawMode); btn.setAttribute('aria-pressed', String(state.drawMode)); }
|
||||||
|
// A background-colour change alone was easy to miss once several regions
|
||||||
|
// in a row had been added -- confirmed live. An explicit on/off switch
|
||||||
|
// icon is a second, unambiguous cue for the same state.
|
||||||
|
const switchIcon = $('mag-draw-toggle-switch');
|
||||||
|
if (switchIcon) {
|
||||||
|
switchIcon.classList.toggle('mdi-toggle-switch', state.drawMode);
|
||||||
|
switchIcon.classList.toggle('mdi-toggle-switch-off-outline', !state.drawMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function magReviewToggleDraw(kind = 'text') {
|
||||||
|
state.drawMode = state.drawMode && state.drawKind === kind ? false : true;
|
||||||
|
state.drawKind = kind;
|
||||||
|
if (state.drawMode && state.editMode) magReviewToggleEdit();
|
||||||
|
_magSyncDrawUi();
|
||||||
magReviewCancelDrawPanel();
|
magReviewCancelDrawPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2163,9 +2195,13 @@ function magReviewOpenDrawPanel(box) {
|
|||||||
'<button type="button" class="btn-sm primary" onclick="magReviewSubmitDraw()"><span class="mdi mdi-text-recognition"></span> Use recognised text</button></div>'
|
'<button type="button" class="btn-sm primary" onclick="magReviewSubmitDraw()"><span class="mdi mdi-text-recognition"></span> Use recognised text</button></div>'
|
||||||
:
|
:
|
||||||
'<div class="mag-draw-panel-row">' +
|
'<div class="mag-draw-panel-row">' +
|
||||||
'<label>Region type <select id="mag-draw-kind">' +
|
'<div class="mag-type-picker" id="mag-draw-kind-picker">' +
|
||||||
drawKindOptions.map(([v, label]) => '<option value="' + v + '">' + label + '</option>').join('') +
|
'<label for="mag-draw-kind-search">Region type</label>' +
|
||||||
'</select></label>' +
|
'<input type="text" id="mag-draw-kind-search" class="mag-type-picker-input" autocomplete="off" ' +
|
||||||
|
'placeholder="Search region type…" aria-expanded="false" role="combobox" aria-autocomplete="list">' +
|
||||||
|
'<input type="hidden" id="mag-draw-kind" value="body">' +
|
||||||
|
'<ul class="mag-type-picker-list" id="mag-draw-kind-list" role="listbox" hidden></ul>' +
|
||||||
|
'</div>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'<label id="mag-draw-exclude" hidden><input id="mag-draw-exclude-check" type="checkbox" checked> Exclude enclosed text from narration</label>' +
|
'<label id="mag-draw-exclude" hidden><input id="mag-draw-exclude-check" type="checkbox" checked> Exclude enclosed text from narration</label>' +
|
||||||
'<textarea id="mag-draw-text" rows="2" placeholder="Type the text, or leave blank and use OCR"></textarea>' +
|
'<textarea id="mag-draw-text" rows="2" placeholder="Type the text, or leave blank and use OCR"></textarea>' +
|
||||||
@ -2178,15 +2214,78 @@ wrap.appendChild(panel);
|
|||||||
const top = parseFloat(box.style.top) || 0, left = parseFloat(box.style.left) || 0, height = parseFloat(box.style.height) || 0;
|
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 + 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) + '%';
|
||||||
const kindSelect = $('mag-draw-kind');
|
if ($('mag-draw-kind-picker')) _magSetupDrawKindPicker(drawKindOptions);
|
||||||
if (kindSelect) {
|
}
|
||||||
kindSelect.addEventListener('change', () => {
|
|
||||||
const isImage = kindSelect.value === 'image';
|
// A 16-option <select> for something picked constantly deserved faster
|
||||||
|
// access than scrolling a native dropdown -- type to filter, arrow keys or
|
||||||
|
// click to choose, icon per type so the list scans visually instead of by
|
||||||
|
// re-reading every label. #mag-draw-kind stays a plain hidden input so
|
||||||
|
// magReviewSubmitDraw's existing $('mag-draw-kind').value read needed no
|
||||||
|
// changes.
|
||||||
|
function _magDrawKindOnPick(value) {
|
||||||
|
const isImage = value === 'image';
|
||||||
const ta = $('mag-draw-text');
|
const ta = $('mag-draw-text');
|
||||||
if (ta) ta.hidden = isImage;
|
if (ta) ta.hidden = isImage;
|
||||||
$('mag-draw-exclude').hidden = !isImage;
|
const exclude = $('mag-draw-exclude');
|
||||||
});
|
if (exclude) exclude.hidden = !isImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _magSetupDrawKindPicker(options) {
|
||||||
|
const search = $('mag-draw-kind-search');
|
||||||
|
const hidden = $('mag-draw-kind');
|
||||||
|
const list = $('mag-draw-kind-list');
|
||||||
|
if (!search || !hidden || !list) return;
|
||||||
|
|
||||||
|
const renderList = (query) => {
|
||||||
|
const q = (query || '').trim().toLowerCase();
|
||||||
|
const matches = q ? options.filter(([, label]) => label.toLowerCase().includes(q)) : options;
|
||||||
|
list.innerHTML = matches.length
|
||||||
|
? matches.map(([v, label]) => '<li role="option" data-value="' + v + '" ' +
|
||||||
|
'class="mag-type-picker-option' + (v === hidden.value ? ' is-selected' : '') + '">' +
|
||||||
|
'<span class="mdi ' + (MAG_REGION_TYPE_ICONS[v] || 'mdi-text-box-outline') + '" aria-hidden="true"></span> ' +
|
||||||
|
esc(label) + '</li>').join('')
|
||||||
|
: '<li class="mag-type-picker-empty muted">No matching region type</li>';
|
||||||
|
};
|
||||||
|
|
||||||
|
const pick = (value, label) => {
|
||||||
|
hidden.value = value;
|
||||||
|
search.value = label;
|
||||||
|
list.hidden = true;
|
||||||
|
search.setAttribute('aria-expanded', 'false');
|
||||||
|
_magDrawKindOnPick(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
renderList(search.value === (options.find(([v]) => v === hidden.value) || [])[1] ? '' : search.value);
|
||||||
|
list.hidden = false;
|
||||||
|
search.setAttribute('aria-expanded', 'true');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start on the default (Paragraph) with its label showing, matching the
|
||||||
|
// hidden input's own default value set in the HTML above.
|
||||||
|
pick(hidden.value, (options.find(([v]) => v === hidden.value) || [])[1] || '');
|
||||||
|
search.addEventListener('focus', open);
|
||||||
|
search.addEventListener('input', () => { list.hidden = false; search.setAttribute('aria-expanded', 'true'); renderList(search.value); });
|
||||||
|
search.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape') { list.hidden = true; search.setAttribute('aria-expanded', 'false'); search.blur(); }
|
||||||
|
else if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
const first = list.querySelector('.mag-type-picker-option');
|
||||||
|
if (first) { const v = first.dataset.value; pick(v, options.find(([ov]) => ov === v)[1]); }
|
||||||
|
} else if (e.key === 'ArrowDown' && list.hidden) { open(); }
|
||||||
|
});
|
||||||
|
list.addEventListener('mousedown', (e) => {
|
||||||
|
// mousedown (not click) fires before the input's blur, so the list is
|
||||||
|
// still in the DOM to read from when the handler below reacts to blur.
|
||||||
|
const li = e.target.closest('.mag-type-picker-option');
|
||||||
|
if (!li) return;
|
||||||
|
e.preventDefault();
|
||||||
|
pick(li.dataset.value, options.find(([v]) => v === li.dataset.value)[1]);
|
||||||
|
});
|
||||||
|
search.addEventListener('blur', () => {
|
||||||
|
setTimeout(() => { list.hidden = true; search.setAttribute('aria-expanded', 'false'); }, 0);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function magReviewSubmitDraw() {
|
async function magReviewSubmitDraw() {
|
||||||
@ -2211,9 +2310,9 @@ async function magReviewSubmitDraw() {
|
|||||||
article.author = $('mag-rev-author').value;
|
article.author = $('mag-rev-author').value;
|
||||||
if (!_magReviewChecked) _magReviewChecked = new Set(article.blocks || []);
|
if (!_magReviewChecked) _magReviewChecked = new Set(article.blocks || []);
|
||||||
magReviewCancelDrawPanel();
|
magReviewCancelDrawPanel();
|
||||||
state.drawMode = false;
|
|
||||||
if (field) {
|
if (field) {
|
||||||
magReviewCancelDrawPanel();
|
state.drawMode = false;
|
||||||
|
_magSyncDrawUi();
|
||||||
say(`Reading selected ${field}…`, '');
|
say(`Reading selected ${field}…`, '');
|
||||||
try {
|
try {
|
||||||
const result = await api('/api/magazine/' + state.doc.id + '/pages/' + state.reviewPage + '/extract', {
|
const result = await api('/api/magazine/' + state.doc.id + '/pages/' + state.reviewPage + '/extract', {
|
||||||
@ -2265,8 +2364,20 @@ async function magReviewSubmitDraw() {
|
|||||||
x + w <= bbox[0] + bbox[2] && y + h <= bbox[1] + bbox[3]) _magReviewChecked.delete(b.id);
|
x + w <= bbox[0] + bbox[2] && y + h <= bbox[1] + bbox[3]) _magReviewChecked.delete(b.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (kind === 'text' || kind === 'heading') added.forEach(r => _magReviewChecked.add(r.id));
|
} else if (MAG_NARRATABLE_KINDS.has(kind)) {
|
||||||
|
// Drawing a region at all means the reader judged it part of the
|
||||||
|
// article -- it should read as included, not force a second click per
|
||||||
|
// box to tick what was just drawn. Furniture-like kinds (page number,
|
||||||
|
// source, advertisement, table, duplicate, OCR noise) stay unchecked,
|
||||||
|
// since those are excluded from narration on purpose.
|
||||||
|
added.forEach(r => _magReviewChecked.add(r.id));
|
||||||
|
}
|
||||||
renderReview();
|
renderReview();
|
||||||
|
// Draw region is a toggle, not a one-shot: stay armed for the next box
|
||||||
|
// instead of forcing another click on the toolbar button every time.
|
||||||
|
// renderReview() just rebuilt the toolbar from scratch with no memory of
|
||||||
|
// drawMode, so the "still drawing" visuals need reapplying here.
|
||||||
|
if (state.drawMode) _magSyncDrawUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep the review's unsaved selection in step with a region change made on the server. Without this,
|
// Keep the review's unsaved selection in step with a region change made on the server. Without this,
|
||||||
|
|||||||
@ -4969,6 +4969,8 @@ input[list="lang-codes"] {
|
|||||||
.mag-review-draw-controls { display: flex; gap: 6px; }
|
.mag-review-draw-controls { display: flex; gap: 6px; }
|
||||||
.mag-review-draw-controls .btn-sm { min-height: 34px; }
|
.mag-review-draw-controls .btn-sm { min-height: 34px; }
|
||||||
.mag-review-draw-controls .active { background: var(--accent); color: var(--on-accent); }
|
.mag-review-draw-controls .active { background: var(--accent); color: var(--on-accent); }
|
||||||
|
.mag-draw-toggle-switch { margin-left: 6px; font-size: 17px; vertical-align: -3px; }
|
||||||
|
.mag-review-draw-controls .active .mag-draw-toggle-switch { color: var(--green, #16a34a); }
|
||||||
#mag-draw-hint:not(:empty) { padding: 8px 12px; margin-bottom: 12px; border-left: 3px solid var(--accent); }
|
#mag-draw-hint:not(:empty) { padding: 8px 12px; margin-bottom: 12px; border-left: 3px solid var(--accent); }
|
||||||
.mag-review-pagenav-spacer { flex: 1; min-width: 0; }
|
.mag-review-pagenav-spacer { flex: 1; min-width: 0; }
|
||||||
|
|
||||||
@ -5019,6 +5021,25 @@ input[list="lang-codes"] {
|
|||||||
padding: 4px 6px; border: 1px solid var(--border); border-radius: 4px; background: var(--surface);
|
padding: 4px 6px; border: 1px solid var(--border); border-radius: 4px; background: var(--surface);
|
||||||
color: var(--text); }
|
color: var(--text); }
|
||||||
|
|
||||||
|
/* Searchable region-type combobox in the draw panel — a 16-option native
|
||||||
|
<select> is slow to scan and slow to reach by scrolling; this filters by
|
||||||
|
typing and shows an icon per type instead. */
|
||||||
|
.mag-type-picker { position: relative; display: flex; align-items: center; gap: 6px; flex: 1; min-width: 0; }
|
||||||
|
.mag-type-picker label { flex-shrink: 0; }
|
||||||
|
.mag-type-picker-input { flex: 1; min-width: 0; font: inherit; font-size: 12px; padding: 4px 6px;
|
||||||
|
border: 1px solid var(--border); border-radius: 4px; background: var(--surface); color: var(--text); }
|
||||||
|
.mag-type-picker-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px rgba(37,99,235,.18); }
|
||||||
|
.mag-type-picker-list { position: absolute; top: 100%; left: 0; right: 0; z-index: 5; margin: 2px 0 0;
|
||||||
|
padding: 4px; list-style: none; max-height: 240px; overflow-y: auto; background: var(--surface);
|
||||||
|
border: 1px solid var(--border); border-radius: var(--radius); box-shadow: 0 6px 18px rgba(0,0,0,.25); }
|
||||||
|
.mag-type-picker-option { display: flex; align-items: center; gap: 8px; padding: 6px 8px; font-size: 13px;
|
||||||
|
border-radius: 5px; cursor: pointer; color: var(--text); }
|
||||||
|
.mag-type-picker-option .mdi { color: var(--subtext); font-size: 16px; flex-shrink: 0; }
|
||||||
|
.mag-type-picker-option:hover { background: var(--panel); }
|
||||||
|
.mag-type-picker-option.is-selected { background: var(--accent); color: var(--on-accent); }
|
||||||
|
.mag-type-picker-option.is-selected .mdi { color: inherit; }
|
||||||
|
.mag-type-picker-empty { padding: 6px 8px; font-size: 13px; }
|
||||||
|
|
||||||
/* Editing an already-detected region: move by dragging the box body, resize
|
/* Editing an already-detected region: move by dragging the box body, resize
|
||||||
from its corner handle, delete with its ×. Handles and the delete button
|
from its corner handle, delete with its ×. Handles and the delete button
|
||||||
only render usably in edit mode -- kept in the DOM at all times (rather
|
only render usably in edit mode -- kept in the DOM at all times (rather
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user