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 last = ctx.rects[ctx.rects.length - 1];
|
||||
if (!first || !last) return;
|
||||
// Start dot at the first rect's LEFT-MIDDLE, end dot at the last
|
||||
// rect's RIGHT-MIDDLE — the range boundary points.
|
||||
// Start dot floating ABOVE the first rect's top-left corner, end
|
||||
// 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 }> = [
|
||||
{ which: "start", fx: first[0], fy: first[1] + first[3] / 2 },
|
||||
{ which: "end", fx: last[0] + last[2], fy: last[1] + last[3] / 2 },
|
||||
{ which: "start", fx: first[0], fy: first[1] },
|
||||
{ which: "end", fx: last[0] + last[2], fy: last[1] },
|
||||
];
|
||||
for (const pt of pts) {
|
||||
let dot = layer.querySelector<HTMLDivElement>(
|
||||
@@ -1790,8 +1792,10 @@ document.addEventListener("alpine:init", () => {
|
||||
layer.appendChild(dot);
|
||||
}
|
||||
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.top = `${host.y - 13}px`;
|
||||
dot.style.top = `${host.y - 22}px`;
|
||||
}
|
||||
},
|
||||
/** Line boxes (page fractions) of a contiguous char-table span:
|
||||
@@ -1846,15 +1850,27 @@ document.addEventListener("alpine:init", () => {
|
||||
if (!ctx) return;
|
||||
const map = this.pdfPageMapping(ctx.index);
|
||||
if (!map) return;
|
||||
const { fx, fy } = map.toFraction(clientX, clientY);
|
||||
const { fx } = map.toFraction(clientX, clientY);
|
||||
const chars = ctx.chars;
|
||||
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 bestD = Infinity;
|
||||
for (let i = 0; i < chars.length; i++) {
|
||||
const cx = (chars[i].x0 + chars[i].x1) / 2;
|
||||
const cy = (chars[i].y0 + chars[i].y1) / 2;
|
||||
const d = (cx - fx) * (cx - fx) + (cy - fy) * (cy - fy);
|
||||
const c = chars[i];
|
||||
const cy = (c.y0 + c.y1) / 2;
|
||||
if (cy < bandTop || cy > bandBottom) continue;
|
||||
const cx = (c.x0 + c.x1) / 2;
|
||||
const d = (cx - fx) * (cx - fx);
|
||||
if (d < bestD) {
|
||||
bestD = d;
|
||||
best = i;
|
||||
|
||||
Reference in new issue
Block a user