5 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
45e60330b8 |
highlight-touch: แก้กลุ่มบั๊กจอสัมผัสของ useCustomSelection + rectsInSweptPath ตามรายงาน review 12 ข้อ + follow-up 3 ข้อ — ลากแนวนอนบรรทัดเดียวบน tablet ต้องสร้างมาร์กได้ทุกครั้ง และ gesture ต้องไม่ตายถาวรกลางsession — (1) fingerOffsetFor: FINGER_OFFSET_PX=12 คงที่ทำ probe ที่ y−12 หลุดบรรทัด (บรรทัด PDF บน tablet zoom-fit สูงจริง ~14px — วัดจาก viewport 820px) ไปโดน heading จนลากแนวนอนเงียบไม่สร้าง — offset กลายเป็น min(12, ครึ่งความสูงบรรทัด−2) วัดจาก span ของ anchor ตอน pointerdown (บรรทัด ≥28px ยังได้ 12 เท่าเดิม) และถ้าน้อยกว่า 12 จะ resolve anchor ซ้ำที่จุดที่หักจริง — ยืนยันด้วย Playwright emulate iPad + CDP touch จริง (ก่อนแก้ 9/11 หลังแก้ 11/11) (2) A1 rangeBetween ฝั่งหลังใช้ boundary end แทน start — anchor/focus ผ่าน word snap แล้วเป็น range ครอบคำ การอ่าน start ทิ้งคำท้าย (ลาก alpha→gamma ได้แค่ alpha beta) และลากในคำเดียวได้ selection ว่าง (3) A2 anchorPathPt(client คงที่)→anchorRel สัดส่วน 0–1 ของหน้า แปลงกลับทุกเฟรม — พิกัด client โกหกทันทีที่ auto-scroll เลื่อนเนื้อหากลางลาก จนบรรทัดต้นทางหายจากทั้ง preview และค่าที่บันทึก (จำลอง 26 บรรทัดเคยได้ 14) (4) A3 rectsInSweptPath (utils/coordinates.ts) rewrite: snap จุด start/end เข้า center บรรทัดใกล้สุด (snapY) ก่อนตัดสินบรรทัดเดียวกัน/คนละบรรทัด — พิกัดนิ้วที่คนละบรรทัดแต่ dy น้อยกว่า lineTol เคยถูกตีเป็นบรรทัดเดียวจนกรองได้ 0 rect (ลากท้ายบรรทัดบน→ต้นบรรทัดถัดไประยะสั้น) — ใช้ร่วม desktop เหมือนเดิม เคส test เดิม 3 เคสยังผ่าน (5) A4 range.collapsed (ย่อจนว่างจริง) = เคลียร์ lastRef + preview แล้ว return — commit ตรง preview เสมอ แยกจากเคสกรองว่างชั่วคราวที่ยังคง lastRef ตามเจตนาเดิม (6) A5 caretInSpanAt วาง caret ตามสัดส่วน x ใน span แทนต้น span เสมอ (pdf.js มักครอบทั้งบรรทัดใน span เดียว) ใช้ทั้งชั้น ③ elementsFromPoint และ ④ nearest span (7) A6 fromElement ปฏิเสธ text node ที่อยู่นอก .textLayer (กัน DFS หลุดไป text node แรกของหน้า) (8) A7.1 auto-scroll speed cap ระยะเลยขอบที่ margin / A7.2 ลบ seenMovePointerIds debug / A7.4 mouse ปุ่มซ้ายเท่านั้น (hybrid) / A7.5 ส่ง DragState ตรงให้ resolveTextCaretDeep ให้ spanCache ถูกเก็บกลับ (9) B1 ระหว่างลาก retry resolver แค่ 2 ทิศแรก (ครบ 8 ทิศ ~6ms/เฟรม เก็บไว้ pointerdown — fail ระหว่างลากมีทางออกคือคง lastFocus) (10) B2+2.3 spanCache เก็บ fraction 0–1 ของหน้า (แบบเดียวกับ anchorRel — ทน re-fit ความกว้างกลาง gesture เช่น นิ้วที่สอง toggle sidebar) วัดครั้งเดียวต่อ gesture แทน getBoundingClientRect ทั้งหน้าทุกครั้ง (~2.3ms) (11) code review 2.1: pointerup/pointercancel ย้ายจาก <main> เป็นตาข่าย document แบบ capture phase ผูกตั้งแต่ pointerdown จน finish — Chromium กับ touch ถอด pointer capture กลาง drag ได้เอง พอ capture หาด pointerup ยิงนอก <main> จน dragRef ค้าง gesture ถัดไปเงียบหมด (arm เฉพาะตอน active ไม่พอ — tap ที่ไม่ขยับก็ต้องมีตาข่ายรับ ไม่งั้นค้างเหมือนกัน เจอจริงตอนแก้ e2e 9/13) (12) A7.3 ไม่ใช้: ห้ามผูก finish เข้า lostpointercapture — กับ touch ของ Chromium ยิงกลาง drag ได้เอง (implicit capture ของ span + re-hit-test) ผูกแล้ว dragRef ถูกเคลียร์กลางทาง gesture ตายหมด — ยืนยันด้วย CDP touch event log จริง (13) 2.2 แก้ comment fingerOffsetFor ให้ตรงกลไก (เคส probe โดน heading ≥28px ยังใช้ heuristic เดิม) (14) tests: coordinates +7 เคส (รวม 2 บรรทัด dy เล็กที่เคยได้ 0 และ auto-scroll 26 บรรทัด) + ไฟล์ใหม่ tests/hooks/useCustomSelection-dom.test.ts 7 เคส snapCaretToWord/rangeBetween (export เพิ่มเพื่อ test พร้อม comment) ผ่าน pragma @vitest-environment jsdom รวมคำไทยกับ Intl.Segmenter "แมวกินปลา"→"แมว" — เพิ่ม devDependency jsdom (test-only) (15) rules/pdf-viewer.md sync จุดที่พฤติกรรมเปลี่ยนครบ — ผ่าน code review รอบ 2 (ไม่มี bug ระดับต้องแก้) + tsc + vitest 157/157 + e2e tablet 12/13 (เคส F fail ที่การจัดเตรียมเงื่อนไขของ script เอง ไม่ใช่ฟังก์ชัน — A3 ครอบโดย unit tests) (คำขอผู้ใช้ 2026-09-28)
Co-Authored-By: Claude Code <noreply@anthropic.com> |
||
|
|
f7cd4b8312 |
viewer: เปลี่ยนตัวเลื่อนของหน้า annotate จาก window มาเป็น <main> ของเอกสาร — zoom เกินจอแล้วเลื่อนได้ทั้งสองแกน sidebar/header อยู่กับที่ (คำขอผู้ใช้ 2026-09-22: เดิมเลื่อน/ซูมทีเดียวเป็นทั้งหน้าเว็บรวม sidebar ไปด้วย)
- PDFViewer root เป็น app shell flex h-dvh overflow-hidden (dvh กันปัญหา URL bar ของจอสัมผัส) + content column เป็น flex-col + <main> ได้ class pdf-scroll-main min-h-0 flex-1 overflow-auto overscroll-contain = ตัวเลื่อนเดียวของหน้า; aside เปลี่ยน h-screen เป็น h-full (ถอด sticky top-0 ที่เป็น no-op แล้ว เหลือ top-0 สำหรับ max-lg:fixed) - class ใหม่ .pdf-scroll-main ใน globals.css: touch-action pan-x pan-y บนพื้นที่เอกสาร — กัน browser pinch/double-tap zoom ทั้งหน้าเว็บ (อาการ sidebar โต้ตามตอนซูมบน tablet) วางก่อน .pdf-custom-select เพื่อให้โหมดมาร์กข้อความ (touch-action:none) ยังชนะตอนเปิดใช้; ปุ่มซูม +- เป็นทางเลือก single-pointer ตาม WCAG 2.5.1 - retarget scroll ทุกจุดจาก window มาที่ mainRef.current: scrollToBelowHeader รับ main + helper ใหม่ contentTopInScroll(main, clientTop) แทนสูตร r.top + window.scrollY (ลบ viewerHeaderHeight ที่ไม่จำเป็น — header อยู่นอกกรอบ scroll), ตรวจหน้าปัจจุบันย้าย listener มาที่ main, IO ของ virtualization ตั้ง root เป็น main, deep link ?page/?ann, goToPage/goToBookmark/goToAnnotation/currentViewPosition, tool pan ใช้ main.scrollBy (ลากได้ทั้งแนวนอนด้วย) - useCustomSelection auto-scroll: เลื่อน scroll container ของ <main> แทน window + เพิ่มแกนนอน (ลากชิดขอบซ้าย/ขวาเลื่อนต่อ — แก้ zoom แล้วเข้าไม่ถึงครึ่งขวาของหน้าในโหมดมาร์กข้อความบน tablet) และขอบวัดจาก rect ของ main (ใต้ header) แทนขอบจอที่โดน header บัง + hint เปลี่ยนเป็น "ลากชิดขอบจอเพื่อเลื่อนหน้า" - sync กติกา .claude/rules/pdf-viewer.md: เพิ่ม bullet scroll architecture (ห้ามกลับไปเลื่อนที่ window) + แก้คำอธิบาย pan/auto-scroll ที่อ้าง window เดิม verify: npx tsc --noEmit ผ่าน, vitest 139/139 ผ่าน — การวาด/สไตลัส/pressure และพิกัด % ไม่ถูกแตะ Co-Authored-By: Claude Code <noreply@anthropic.com> |
||
|
|
79b38fa152 |
fix: selection preview not appearing when dragging with pen
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 3s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 41s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 13s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
|
||
|
|
5c8e52bd62 |
ไฮไลต์เสร็จแล้ว popup ซ้อนกันอีก 2 รูปแบบ (tooltip ทับแผงบน desktop + เมนู tool ค้างเปิดบน iOS) + งาน TEMP-DEBUG ของปัญหา pencil ลากไม่ติดบน iPad:
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 3s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 44s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 13s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 0s
ส่วนที่ 1 — แก้ popup ซ้อนตอนสร้างไฮไลต์ (bug report: กด tool highlight → ไป highlight → เสร็จแล้วมี popup ของแก้ไขแสดงอีกอัน): (1) Tooltip hover (N1) ซ่อนเมื่อรายการที่ชี้ = selectedId — เดิมลากคลุมจบแล้วเคอร์เซอร์ยังอยู่บนไฮไลต์ใหม่พอดี auto-select เปิดแผง selection แล้ว tooltip ยังโผล่ซ้อนอีกกล่องบนตำแหน่งเดียวกัน; ชี้รายการอื่นที่ไม่ได้เลือก tooltip ยังทำงานตามเดิม (2) handleCreateAnnotation ตรง branch AUTO_SELECT_TYPES ปิดเมนู dropdown ทั้ง 5 + popup bookmark ก่อนแผงโผล่ — บน iOS การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนูเอง (Android/desktop ปิดตั้งแต่เริ่มกดแล้ว) ไม่ปิดตรงนี้ = เมนูค้างเปิดซ้อนแผงบนจอสัมผัส - sync rules/pdf-viewer.md: กติกา popup ทีละชุดเพิ่มกรณี auto-select ปิดเมนู + เงื่อนไขซ่อน tooltip เมื่อ hovered = selectedId - verify: tsc ผ่าน + Playwright 6/6 — desktop: ไฮไลต์เสร็จแผงโผล่/ไม่มี tooltip ซ้อน/ชี้รายการอื่น tooltip กลับมา; เลียน iOS (dispatch touch PointerEvent ล้วนไม่มี compat mousedown): ลากคลุม commit จริง + แผงโผล่ + เมนูที่เคยเปิดค้างถูกปิด ส่วนที่ 2 — TEMP-DEBUG ปัญหา pencil ลากแล้วบางทีไม่ commit บน iPad (ทำคู่กันระหว่าง debug จริงเครื่อง ยังไม่ถึงเวลาเก็บกวาด): - components/DebugConsole.tsx (ใหม่): เปิด Eruda จาก CDN เมื่อ URL มี ?debug — อ่าน console บนจอ iPad จริงโดยไม่ต้องต่อ Safari Web Inspector (โหลดพลาดมีแบนเนอร์แจ้ง) + mount ใน app/annotate/[id]/page.tsx — ทั้งคู่ mark TEMP-DEBUG ไว้ลบพร้อมกัน - hooks/useCustomSelection.ts: log [debug] ตำแหน่งสำคัญของ gesture (pointerdown / move แรกของ pointerId นั้นหลัง down — กัน log 60-120Hz ท่วม console จนเปลี่ยนพฤติกรรมที่ดูอยู่เอง / แยก pointerup vs pointercancel / นิ้วที่สองถูกเมิน) + เก็บความเห็นใหม่ว่า guard นิ้วที่สองครอบคลุม touch ปลอมจาก palm rejection (มือพักบนจอระหว่างลากด้วย pen) - components/UploadFileForm.tsx: ปุ่มอัปโหลด ml-auto ชิดขวา (เดิมติด input ไปฝั่งซ้าย) Co-Authored-By: Claude Code <noreply@anthropic.com> |
||
|
|
0c4af09e85 |
จอสัมผัส (iOS/Android): ไฮไลต์ด้วย custom selection ลาก→ปล่อย=สร้างทันที แทน native selection + ปุ่มยืนยันลอย (code review ครบ):
Some checks failed
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Failing after 3s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Has been skipped
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Has been skipped
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 0s
(1) hooks/useCustomSelection.ts (ใหม่): tool highlight บนอุปกรณ์ pointer หลัก coarse ลากเลือกข้อความเองผ่าน Pointer Events ชุดเดียวคุม mouse+touch — pointerdown เก็บ anchor caret (caretRangeFromPoint → caretPositionFromPoint fallback) ยังไม่ทำอะไร → เลย threshold 8px จึง setPointerCapture + เริ่ม select จริง (จับตอนเริ่ม select ไม่ใช่ pointerdown — capture ตั้งแต่กดจะ retarget click ไป container จน tap เลือก annotation/pin ใช้ไม่ได้) → pointerup = commit ทันทีผ่าน commitHighlight เดิม (ห้ามแก้ signature — ไปทาง readTextSelection(pi, { range }) ที่เพิ่ม opts.range ให้อ่าน Range จากภายนอกแทน native selection) — ชดเชย UX ที่หายจาก native: word snap ขอบคำทั้ง anchor/focus (Intl.Segmenter แยกคำไทย dictionary → fallback regex \S+, กลางช่องว่างไม่ขยาย), finger offset อ่าน caret ที่ y−12 เพราะนิ้วบังข้อความ (เฉพาะ pointerType touch), auto-scroll rAF loop เมื่อนิ้วใกล้ขอบบน/ล่าง viewport 60px, live preview rAF throttle (getClientRects → rectsInSweptPath → % + mergeRectsByLine รูปร่างเดียวกับ liveMark ส่งเข้า PdfPage ทดแทนตอนมีค่า customSelPreview ?? liveMark)
(2) กัน "ลากแล้วบางทีไม่ commit" ตาม bug report 3 จุด + code review: การหา caret เป็น 4 ชั้นเรียงตามต้นทุน (resolveTextCaretDeep ใช้เฉพาะ pointerdown กับทั้งจุด y−12 และจุดแตะจริง): caret API + descend หา text node (childNodes[startOffset] + findFirstTextNode DFS) → retry 8 ทิศ ±2/3px → elementsFromPoint เจาะ overlay ไร้ text (SVG ของ InkLayer/มาร์ก/กรอบ) จับ span ใต้จุด → span ของหน้านั้นที่ใกล้สุดแนวตั้ง ≤40px (เกินถือว่าไม่มีข้อความจริง; cache span list ต่อ 1 gesture) — ระหว่างลากทุก rAF frame ใช้เฉพาะ 2 ชั้นถูก หาไม่เจอ (ผ่านพื้นที่ว่าง/ช่องว่างระหว่างหน้า) ให้คง focus เดิมจาก frame ก่อน (lastFocus) ไม่ re-scan / lastRef เคลียร์เฉพาะ pointerdown, finish และเมื่อหน้า anchor โดน virtualization unmount กลางลาก (anchorPageEl.isConnected — กัน commit ของเก่าเงียบ ๆ) — frame ที่ rectsInSweptPath กรองว่าง (ลากเร็ว sample ห่าง/นิ้วสั่นตอนปล่อย) ล้างแค่ preview / ลากล้ำเข้าหน้าถัดไป (range คร่อม 2 หน้า) ห้าม bail — clampToAnchorPage clamp ฝั่ง focus ไปท้ายหน้า anchor (ลากลง) หรือต้นหน้า anchor (ลากขึ้น) boundary cache ต่อ gesture (clampBounds ไม่เดิน TreeWalker ทุก frame) / จุดเส้นทางกรอง rectsInSweptPath ใช้จุด offset เดียวกับ caret (anchorPathPt = จุด probe ที่ resolve anchor + pathEnd = จุด focus หัก 12px) — ใช้พิกัดนิ้ว raw ตรง ๆ จะคลาด 12px จนบรรทัดเป้าหมายโดน lineTol ตัดทิ้ง (ลากขึ้น) หรือมาร์กเกินบรรทัด (ลากลง) เมื่อฟอนต์เล็ก
(3) ถอด path เดิมทั้งหมด: touchConfirm state, touchSelRef/touchSelTimerRef/touchHideTimerRef, debounce 400ms ใน effect selectionchange, handleTouchConfirmHighlight, ปุ่ม "ไฮไลต์ข้อความ" ลอยใต้ selection — desktop (pointer หลัก fine) ใช้ native mouse selection + mouseup commit ตามเดิมไม่เปลี่ยน (enabled = tool highlight + matchMedia coarse) / ระหว่าง enabled ให้ <main> ได้ class .pdf-custom-select (user-select/-webkit-user-select/-webkit-touch-callout: none + touch-action: none กัน browser ยึด gesture เป็น scroll → pointercancel) ถอดทันทีเมื่อเปลี่ยน tool (tool อื่น native selection + long-press copy ของ OS ตามปกติ) + restore user-select:text และ touch-action:auto ให้ input/textarea/contenteditable ใต้ main (reply ของ comment pin ยังเลือก/เลื่อนข้อความในช่องได้)
(4) ย้าย ClientPt + rectsInSweptPath จาก PDFViewer ไป utils/coordinates.ts (hook import ได้โดยไม่ circular) + unit tests rectsInSweptPath 4 เคส (ลากบรรทัดเดียว/ลากขึ้นหลายบรรทัด/บรรทัดนอกเส้นทาง/ไม่ขยับ) / gap-analysis.md แถว 5.3.18 อัปเดตเป็นกลไกใหม่ (รวมงานฉบับแก้ไขข้อความข้อ 5.3.18–5.3.25 ของเจ้าของเอกสารที่ค้างใน working tree) / sync rules/pdf-viewer.md ครบทั้งกลไก 4 ชั้น + path points + กรณีหน้า unmount
verify: tsc ผ่าน + vitest 104/104 + Playwright touch emulation 3 ชุด (รอยต่อ span คำติดกัน gap=0/ขอบซ้าย-ขวาสุดบรรทัด/ช่องว่าง line-height, ลากช้า-เร็ว flick/ล้ำหน้าถัดไปทั้งลงและขึ้น/ขยับต่ำกว่า threshold ไม่สร้าง/tap annotation ได้, จุดว่างใกล้ 20px → commit vs ไกล 46px → ไม่สร้าง) — console.warn ชุด TEMP-DEBUG คงไว้ชั่วคราวตาม protocol รอยืนยันบนมือถือจริง (Safari iOS + Chrome Android) ก่อนถอด
Co-Authored-By: Claude Code <noreply@anthropic.com>
|