feat(web): adjust handles float above the text with tips marking the boundary
User report: the handles covered the highlighted text. The dots now hang ABOVE the extent's boundary corners (teardrop tip down at the first rect's top-left / last rect's top-right), so the highlighted line stays readable. Same treatment as the app leg: the nearest-char scan in the endpoint drag is band-constrained to the boundary rect's own line (the lifted finger floats ABOVE the text — a whole-table scan re-anchored onto the line above). Cross-line changes go through re-selection as before.
This commit is contained in:
1 parent
1c209408cc
commit
a28ae5c88d
1 file changed
+25
-9
@@ -1747,11 +1747,13 @@ document.addEventListener("alpine:init", () => {
|
|||||||
const first = ctx.rects[0];
|
const first = ctx.rects[0];
|
||||||
const last = ctx.rects[ctx.rects.length - 1];
|
const last = ctx.rects[ctx.rects.length - 1];
|
||||||
if (!first || !last) return;
|
if (!first || !last) return;
|
||||||
// Start dot at the first rect's LEFT-MIDDLE, end dot at the last
|
// Start dot floating ABOVE the first rect's top-left corner, end
|
||||||
// rect's RIGHT-MIDDLE — the range boundary points.
|
// dot above the last rect's top-right corner — the teardrop tip
|
||||||
|
// points down at the boundary so the highlighted text underneath
|
||||||
|
// stays readable (user request: the handle covered the text).
|
||||||
const pts: Array<{ which: "start" | "end"; fx: number; fy: number }> = [
|
const pts: Array<{ which: "start" | "end"; fx: number; fy: number }> = [
|
||||||
{ which: "start", fx: first[0], fy: first[1] + first[3] / 2 },
|
{ which: "start", fx: first[0], fy: first[1] },
|
||||||
{ which: "end", fx: last[0] + last[2], fy: last[1] + last[3] / 2 },
|
{ which: "end", fx: last[0] + last[2], fy: last[1] },
|
||||||
];
|
];
|
||||||
for (const pt of pts) {
|
for (const pt of pts) {
|
||||||
let dot = layer.querySelector<HTMLDivElement>(
|
let dot = layer.querySelector<HTMLDivElement>(
|
||||||
@@ -1790,8 +1792,10 @@ document.addEventListener("alpine:init", () => {
|
|||||||
layer.appendChild(dot);
|
layer.appendChild(dot);
|
||||||
}
|
}
|
||||||
const host = map.toHost(pt.fx, pt.fy);
|
const host = map.toHost(pt.fx, pt.fy);
|
||||||
|
// The teardrop's rotated tip is its bottom point: hang the box
|
||||||
|
// ABOVE the boundary corner.
|
||||||
dot.style.left = `${host.x - 13}px`;
|
dot.style.left = `${host.x - 13}px`;
|
||||||
dot.style.top = `${host.y - 13}px`;
|
dot.style.top = `${host.y - 22}px`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/** Line boxes (page fractions) of a contiguous char-table span:
|
/** Line boxes (page fractions) of a contiguous char-table span:
|
||||||
@@ -1846,15 +1850,27 @@ document.addEventListener("alpine:init", () => {
|
|||||||
if (!ctx) return;
|
if (!ctx) return;
|
||||||
const map = this.pdfPageMapping(ctx.index);
|
const map = this.pdfPageMapping(ctx.index);
|
||||||
if (!map) return;
|
if (!map) return;
|
||||||
const { fx, fy } = map.toFraction(clientX, clientY);
|
const { fx } = map.toFraction(clientX, clientY);
|
||||||
const chars = ctx.chars;
|
const chars = ctx.chars;
|
||||||
if (!chars.length) return;
|
if (!chars.length) return;
|
||||||
|
// The boundary stays on its OWN line: candidates are the chars in
|
||||||
|
// the boundary rect's line band, and the finger's X picks the
|
||||||
|
// glyph (vertical finger movement is ignored — cross-line changes
|
||||||
|
// go through re-selection). A whole-table Euclidean scan here
|
||||||
|
// re-anchored onto the line ABOVE when the finger floated above
|
||||||
|
// the text (lifted handles).
|
||||||
|
const boundary =
|
||||||
|
which === "start" ? ctx.rects[0] : ctx.rects[ctx.rects.length - 1];
|
||||||
|
const bandTop = boundary[1] - 0.002;
|
||||||
|
const bandBottom = boundary[1] + boundary[3] + 0.002;
|
||||||
let best = 0;
|
let best = 0;
|
||||||
let bestD = Infinity;
|
let bestD = Infinity;
|
||||||
for (let i = 0; i < chars.length; i++) {
|
for (let i = 0; i < chars.length; i++) {
|
||||||
const cx = (chars[i].x0 + chars[i].x1) / 2;
|
const c = chars[i];
|
||||||
const cy = (chars[i].y0 + chars[i].y1) / 2;
|
const cy = (c.y0 + c.y1) / 2;
|
||||||
const d = (cx - fx) * (cx - fx) + (cy - fy) * (cy - fy);
|
if (cy < bandTop || cy > bandBottom) continue;
|
||||||
|
const cx = (c.x0 + c.x1) / 2;
|
||||||
|
const d = (cx - fx) * (cx - fx);
|
||||||
if (d < bestD) {
|
if (d < bestD) {
|
||||||
bestD = d;
|
bestD = d;
|
||||||
best = i;
|
best = i;
|
||||||
|
|||||||
Reference in new issue
Block a user