diff --git a/app/templates/documents/detail.html b/app/templates/documents/detail.html
index de782ca..d3339f4 100644
--- a/app/templates/documents/detail.html
+++ b/app/templates/documents/detail.html
@@ -5,6 +5,10 @@
box-shadow: 0 0 0 4px rgba(34, 197, 94, 0.18) !important;
font-weight: 800 !important;
}
+
+body.layout-multi-select-active #layout-word-popover {
+ display: none !important;
+}
{% extends "base.html" %}
@@ -1926,6 +1930,9 @@ document.addEventListener("DOMContentLoaded", () => {
+
+
+
@@ -2504,6 +2511,8 @@ const HANDLE_SIZE_PX = 14;
const page = pages[0];
let words = JSON.parse(JSON.stringify(page.words || []));
let selectedId = null;
+ let selectedIds = new Set();
+ let multiSelectMode = false;
let tool = "select";
let zoom = 1;
let dragState = null;
@@ -3070,7 +3079,7 @@ function refreshSelectionUI(opts = {}) {
const y2 = bbox[3] * scaleY;
const w = Math.max(1, x2 - x1);
const h = Math.max(1, y2 - y1);
- const selected = String(word.id) === String(selectedId);
+ const selected = selectedIds.has(String(word.id)) || String(word.id) === String(selectedId);
if (showText) {
drawReplicaText(word, bbox, x1, y1, x2, y2, w, h, scaleX, scaleY);
@@ -3558,6 +3567,7 @@ function refreshSelectionUI(opts = {}) {
if (popoverEl) popoverEl.style.display = "none";
canvas.style.cursor = "default";
refreshSelectionUI({ forceText: true });
+ if (multiSelectMode) hidePopover();
renderCanvas();
setStatus("No selection");
return;
@@ -3727,6 +3737,28 @@ function refreshSelectionUI(opts = {}) {
document.getElementById("layout-nudge-down")?.addEventListener("click", () => nudge(0, 1));
document.getElementById("layout-tool-select")?.addEventListener("click", () => setTool("select"));
+ document.getElementById("layout-select-all")?.addEventListener("click", () => {
+ selectedIds = new Set(words.map(w => String(w.id)));
+ selectedId = Array.from(selectedIds).at(-1) || null;
+ refreshSelectionUI({ forceText: true });
+ if (multiSelectMode) hidePopover();
+ renderCanvas();
+ setStatus("Selected all: " + selectedIds.size);
+ });
+ document.getElementById("layout-clear-selection")?.addEventListener("click", () => {
+ selectedIds.clear();
+ selectedId = null;
+ refreshSelectionUI({ forceText: true });
+ renderCanvas();
+ setStatus("Selection cleared");
+ });
+ document.getElementById("layout-multi-select")?.addEventListener("click", () => {
+ multiSelectMode = !multiSelectMode;
+ document.getElementById("layout-multi-select")?.classList.toggle("active", multiSelectMode);
+ document.body.classList.toggle("layout-multi-select-active", multiSelectMode);
+ if (multiSelectMode) hidePopover();
+ setStatus(multiSelectMode ? "Multi-select on" : "Multi-select off");
+ });
document.getElementById("layout-tool-pan")?.addEventListener("click", () => setTool("pan"));
document.getElementById("layout-tool-add")?.addEventListener("click", () => setTool("add"));