Commit graph

22 commits

Author SHA1 Message Date
Your Name
c5c6b65bd2 genesis+worker: design system Genesis ทั้งชุด + patch pdf.worker แก้ fake-space ไทย + ปิด review design/responsive/quality รอบ 2026-09-24 — (1) Genesis: ฟอนต์ General Sans self-host + DM Sans + Sarabun, AppNav 56px backdrop-blur แทน SessionBar, DotGrid หน้า login, palette/รัศมี/เงา/focus ตาม docs/DESIGN.md ครบทุกหน้า (2) postinstall scripts/patch-pdf-worker.mjs ถอด if (scaledDim) รอบ prevTransform ให้อัปเดตทุก glyph — กัน addFakeSpaces แทรก space กลางคำไทยใน PDF ที่ justify ด้วย letter-spacing + test กัน regression (tests/scripts/ + fixture thai_letterspacing.pdf, invariant #6) (3) ฟอร์มอัปโหลด + ช่องกลุ่ม typeId ทางเลือก — normDocumentGroup ที่ utils/external-file.ts เป็น single source ร่วมกับ N8 (4) token fixes: focus ring ปุ่ม pin + แถว bookmark ตามหน้า, ปุ่ม action ใน popup/composer/dialog เป็น rounded-md ทั้งคู่, selection preview เป็น tint ของ primary แทน blue-500 ดิบ, default สี shape/highlight รวมที่ utils/annotation-display, loading animate-pulse + จดข้อยกเว้น (เงา pin, font weights ของ viewer) ลง DESIGN.md (5) responsive: Export PDF max-lg:order-first ให้เห็นบน iPad, แถวบน header แถวเดียวเลื่อนได้บนจอแคบ, re-fit ความกว้างตอน toggle sidebar เฉพาะเมื่อยังไม่ซูมเอง, pin popover flip ขึ้นบนเมื่อใกล้ท้ายหน้า, toast hint ตอนสลับ tool กลางเอกสาร, ป้าย "ชนิด · ผู้ทำ" ของรายการผู้อื่นสำหรับจอสัมผัส (6) คุณภาพ: @layer ครอบ @media กันกฎ touch รั่ยไป desktop (Tailwind v3 ยก @layer ออกจาก media ทิ้ง wrapper — ยืนยันด้วย build จริง + วัดบนจอ), กฎ M5 ครอบ contenteditable กัน Safari zoom ตอนแก้ไข TEXTBOX บน iPad, COMMENT ใช้ box มาตราฐานเดียวตาม pin px จริง (render/scroll target/move-clamp/bookmark), ตัดหัวการ์ดซ้ำกับ h2 หน้ารายการ — พร้อมไฟล์ untracked ที่ postinstall/โค้ดอ้างถึงครบชุด (app/fonts/, AppNav, DotGrid, tests/) กัน fresh clone fail ที่ npm install (คำขอผู้ใช้ 2026-09-24)
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-24 11:46:02 +07:00
Your Name
e254b935ce groups: หน้ากลุ่มแสดง bookmark+annotation ทั้งกลุ่มในการ์ดไฟล์ — หุบ/กาง <details> "Annotations & Bookmarks (N)" รวมรายการเดียวเรียงตามหน้า (หน้าเดียวกัน bookmark ก่อน) + deep link ?ann=/?page= เหมือนหน้าสรุป + วันเวลา (formatThaiDate) ต่อรายการ + meta การ์ดเป็น en + ย่อ banner เป็น "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" — ปิด gap 5.3.10 ครึ่งหลัง (bookmark รวมทั้งกลุ่ม) + docs sync ครบ (คำขอผู้ใช้ 2026-09-23)
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 11:06:44 +07:00
Your Name
329ae8e956 ecourt+groups: N8 เปิดเอกสารจากลิงก์ ?fileUrl=&fileId=&typeId= (metadata-only + allowlist + ดึงไฟล์สด + login วนกลับ + การ์ดจำลองลิงก์ + path dev /samples) + N9 กลุ่มเอกสารตาม typeId — หน้ารายการจัด section ต่อกลุ่ม + "ไม่มีกลุ่ม" ท้ายสุด + หน้า /groups/[typeId] พร้อมหมายเหตุ "อาจไม่ครบทุกฉบับ" + docs sync ครบ (2026-09-23)
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 10:44:14 +07:00
Your Name
0aef3ff028 export+sidebar: กรองตามรายคนที่เลือกดูครบทั้งจอและ export + REDACTION คงไว้เสมอ (คำขอผู้ใช้ 2026-09-22)
export PDF (F8):
- route รับ ?user=<userId> เพิ่ม: กรอง annotation ตาม userId นั้น ยกเว้น REDACTION
  ติดไปเสมอ (D4 — กล่องดำหาย = เนื้อหาที่ปิดกั้นกลับมาอ่านได้) และ bookmarks → PDF
  outline กรองตามผู้บันทึกด้วย (หัวข้อแม่โดนกรองแต่ลูกยังอยู่ ลูกขึ้น root ตามตรรกะ
  buildOutline เดิม) ไม่ส่ง param = ทั้งหมด เหมือนเดิม
- ชื่อไฟล์เติมชื่อผู้ใช้ท้าย: <title>-annotated-<ชื่อ>.pdf — ชื่อจาก user_name ที่
  mirror ในตาราง (ไม่รับ text จาก client) ผ่าน encodeRFC5987 รองรับชื่อไทย
- PDFViewer handleExportPdf: resolve filter ปัจจุบันก่อนยิง route (mine →
  user.userId) ส่ง &user= เมื่อไม่ใช่ ทุกคน

จอ (F4):
- visibleAnnotations คง REDACTION ของทุกคนไว้เสมอแม้กรองดูของคนอื่น — สูตรเดียวกับ
  export: กันเนื้อหาที่ปิดกั้นกลับมาอ่านได้บนจอ (เดิมหายไปตาม filter) และจอตรงกับ
  ไฟล์ export / ผลค้นหา F14 (visibleSearchResults) ใช้สูตรเดียวกัน
- sidebar แท็บ Bookmark กรองตามเจ้าของหมายเหตุเหมือน annotation (เดิม F4 คุมแค่
  annotation — bug report 2026-09-22): ต้นไม้ ตามหัวข้อ มุมมอง ตามหน้า ตัวเลข
  แท็บ และ badge จำนวนลูก ใช้ visibleBookmarks ชุดเดียวกัน — ลูกที่แม่โดนกรองขึ้น
  root เหมือน export; empty state แยก ไม่มีที่คั่นหน้าของเจ้าของหมายเหตุที่เลือก
  ออกจากไฟล์ว่างจริง
- popup ย้ายหัวข้อ (movableTo) และ bookmarkById ยังเห็นต้นไม้เต็ม — ย้ายไปใต้หัวข้อ
  ของคนอื่นได้ตามเดิม

verify: tsc + Playwright e2e (สร้าง redaction จริงเป็น user1 → สลับ user3 กรองของ
ตัวเอง: rect ดำของ user1 ยัง render ครบ, tab Bookmark (10)→(2) และแถวต้นไม้ 2 ตรง
DB เป๊ะ, ลบ redaction ทดสอบทิ้งจน DB เหลือ 0) + export จริง 4 filter นับ PDF outline
ตรงจำนวน bookmarks ของผู้ใช้นั้นใน DB ทุกแบบ — sync docs/PLAN-FEAT.md §F8

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 15:34:37 +07:00
Your Name
e0c78d68fe bookmark: ดักความยาวชื่อ/โน้ตที่ช่องกรอก + F19 สรุปสิ่งที่เปลี่ยนใน dialog ประวัติ + formatThaiDate — (1) bookmark limits: utils/bookmark-limits.ts ใหม่เป็น source of truth เดียว (ชื่อ 120/โน้ต 200 ตัวอักษร) server action import ร่วมแทน const ท้องถิ่น + ใส่ maxLength ที่ popup บันทึก bookmark (PDFViewer) และฟอร์มแก้ไข ✏ ใน sidebar ดักทั้งพิมพ์/paste ตั้งแต่ในช่อง ไม่รอ error ตอนกดบันทึก + แก้ off-by-one ของ normalizeBookmarkTitle (เดิม slice 120 แล้วต่อ … = 121 ตัว ทำให้คลุมข้อความยาวแล้วกด B บันทึกเลยโดน limit ของ server ตีกลับทั้งที่ไม่ได้พิมพ์เอง — ตอนนี้ slice 119 + … = 120 พอดีตามสเปก §7 N2 ที่เขียนไว้ ≤ 120 อยู่แล้ว) + PLAN-FEAT.md §7 N2 บันทึกเพิ่ม — (2) งานคู่ขนาดจากอีก session (แชร์ไฟล์ PDFViewer/sidebar เลยรวม commit): dialog ประวัติ F19 แสดงสรุปสิ่งที่เปลี่ยนต่อรอบผ่าน describeAnnotationChange ใหม่ใน utils/annotation-display.ts (เทียบ snapshot ก่อนแก้ v.data กับ state ถัดไป — snapshot รอบใหม่กว่า หรือค่าปัจจุบันจาก realtime lookup สด historyCurrent — สรุปเป็นหน่วยเดียวกับตัวปรับ UI: ความหนา ×10 ความทึบ % fontSize px ชื่อสี preset ย้าย/ปรับขนาดจาก bbox ผลต่างเกิน GEOM_EPS_PCT 0.5% ข้อความ/โน้ต/label) + dialog ขยาย w-96 max-h-[60vh] แถวเปลี่ยน items-start + utils/date-format.ts ใหม่ formatThaiDate วันที่ไทยเต็ม "18 กันยายน พ.ศ. 2569" (+เวลา) แทน toLocaleString("th-TH") ทุกจุดแสดงวันที่ใน UI (หน้ารายการไฟล์ sidebar ประวัติ/รายการ timestamps ใน PDFViewer) กัน server/client locale ไม่ตรงกัน + tests ใหม่/ขยาย (date-format + describeAnnotationChange) — CLAUDE.md sync โครงสร้าง utils — verify: tsc strict ผ่าน + vitest 139/139 ผ่าน
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 43s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 3s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 17:02:31 +07:00
Your Name
fa880b314e BASE_PATH: serve ใต้ /docedit/note — next.config.mjs ตั้ง basePath + env NEXT_PUBLIC_BASE_PATH (ประกาศค่าเดียวที่ไฟล์นี้) ให้ utils/base-path.ts (ใหม่) withBasePath() เติม prefix ให้ URL ฝั่ง browser ที่ Next ไม่เติมให้เอง (fetch/EventSource/plain href/workerSrc/img src): PDFViewer (workerSrc, /api/pdf ซ่อม ToUnicode, fallback file.pdfUrl, image branch N3 รวบเป็นตัวแปร fileUrl, export PDF F8), SSE stream ใน useAnnotationRealtime, ปุ่มส่งออก bookmark JSON (N7) + คัดลอกลิงก์ F12 ใน AnnotationSidebar (window.location.origin + basePath กัน paste แล้ว 404), Export CSV (F16) หน้า summary — <Link>/next/navigation/middleware.ts ไม่ต้องแก้ (Next 14.2 auto-prefix ให้ + matcher จับ path ที่ตัด basePath แล้ว + NextURL redirect ใส่กลับเอง ยืนยันจาก source ใน node_modules) + pdf_url ใน DB เก็บ path ไม่รวม basePath เติมตอนใช้เท่านั้น + sync เอกสารตาม governance: CLAUDE.md invariant #6 (workerSrc ผ่าน withBasePath) + #12 ใหม่ (URL ฝั่ง browser ที่สร้างเองต้องผ่าน withBasePath), PLAN.md A3/§4, rules pdf-viewer/realtime, plan-reviewer #8, README URL localhost:3000/docedit/note — verify: tsc strict ผ่าน + curl (root / 404, ไม่มี cookie 307→/docedit/note/login, worker 200, SSE reachable, มี cookie 200) + Playwright login จริงเปิดไฟล์ 44 หน้า render ครบ (SSE /api/pdf /worker 200 ผ่าน basePath) + code-reviewer ผ่าน (vitest 126 tests, ไม่พบ bug, blob URL ไม่โดน prefix ซ้ำ, env inline ครบทั้ง client/server ตาม define-env-plugin) — deploy: basePath เป็น build-time ต้อง build image ใหม่ถึงจะเสิร์ฟที่ /docedit/note + reverse proxy ต้อง forward path เต็มแบบไม่ strip prefix
All checks were successful
Deploy eCourt Note Web to UAT Server / Validate Database Schema (Drizzle) (push) Successful in 3s
Deploy eCourt Note Web to UAT Server / Build Web Docker Image (push) Successful in 48s
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 3s
Deploy eCourt Note Web to UAT Server / Deploy eCourt Note Web to UAT Server (push) Successful in 2s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 48s
Deploy eCourt Note Web to UAT Server / Notify Deployment Status (push) Successful in 1s
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
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 16:06:20 +07:00
Your Name
6940b3870d docs: ร่างแผน N8 ชุดเอกสาร (Document Set) + bookmark รวมทั้งชุด — ปิด gap 5.3.10 ครึ่งหลัง ("หรือชุดของเอกสาร"): ตาราง document_sets + files.set_id nullable (ไฟล์หนึ่งอยู่ชุดเดียว onDelete set null — ลบชุดไฟล์ไม่หาย คนละเรื่องกับ CASCADE ของ annotations) + หน้าใหม่ /sets/[id] bookmark รวมทุกไฟล์ของชุดคลิกกระโดดผ่าน deep link F12 + จัดการชุดบนหน้ารายการ (สร้าง/ย้ายเข้า-ออก/แก้ชื่อ/ลบ — ทุกคนที่ login จัดการได้ roles ยัง out of scope ตาม 5.3.23) — ยังไม่ implement (เสนอ 2026-09-17 รอ /implement-feat N8 ตาม governance: ต้องเขียนสเปกลง PLAN-FEAT.md §7 + ขยาย whitelist ใน CLAUDE.md ก่อน)
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 3s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 0s
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 13:43:16 +07:00
Your Name
55d09ce5c3 N7 ส่งออกรายการ bookmark เป็น JSON แทนรายงาน PDF (เปลี่ยน deliverable ตามคำขอผู้ใช้ 2026-09-17) — route /api/bookmarks/export เขียนใหม่จาก PDFDocument.create ทั้งชุด (ฟอนต์ Sarabun/layout A4/แบ่งหน้า/ต้นไม้ indent 3 ระดับ รวม ~190 บรรทัด) เป็น JSON ข้อมูลล้วนจาก DB: validate fileId (UUID → 400) + ไม่พบไฟล์ = 404 → query bookmarks ทั้งไฟล์ orderBy pageIndex → yOffset → createdAt (ลำดับ N5 เดียวกับ sidebar) → envelope { fileId, title, exportedAt (ISO), bookmarks: BookmarkDTO[] } ผ่าน toBookmarkDTO ตรง ๆ (invariant #5) เก็บ parentId ครบ lossless (reconstruct ต้นไม้จากข้อมูลเองได้) จึงถอด build tree/cycle guard/ฟอนต์/layout ทิ้งหมด; pretty-print 2 space + content-type application/json; charset=utf-8 + attachment filename* จาก encodeRFC5987("{title}-bookmarks.json") (pattern F8/F16); 0 bookmark = "bookmarks": [] ไม่ 404 (ปุ่มทำงานเสมอ); ไม่โหลดไฟล์เอกสารต้นฉบับเลย (invariant #1) + read-only ไม่ publish event ไม่ revalidatePath + ปุ่มท้ายแถว toggle เงื่อนไขค้นหา N6 ในแท็บ "ที่คั่นหน้า" เปลี่ยนไอคอน Download → Braces ({ }) พร้อม title/aria-label "เป็น JSON" (ยังเป็นลิงก์ธรรมดา pattern Export CSV F16 — ส่งออกทุก bookmark ของไฟล์เสมอ ไม่ผูกเงื่อนไขค้นหา N6) + PLAN-FEAT.md §7 N7 เขียนสเปกใหม่เป็น JSON + PLAN-BOOKMARK-PDF.md ติด note หัวไฟล์ว่าโค้ด PDF ในแผนเป็น historical record (สเปกปัจจุบันอยู่ PLAN-FEAT) + gap-analysis ปิด gap กลุ่ม B 5.3.11 ✅ (N5) 5.3.12 ✅ (N6) 5.3.14 ✅ (API นี้) 5.3.15 ✅ (export PDF ของ F8 ฝัง bookmark เป็น outline ติดไปกับไฟล์) พร้อม header อัปเดตล่าสุด 2026-09-17 — ไฟล์เดียวกันมี 5.3.19/5.3.22 ✅ mock login/5.3.23 ของงาน login ตามไปด้วย
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 13:43:00 +07:00
Your Name
65b72cf817 mock login e-Court หน้า /login + identity จาก cookie oacCase แทน localStorage — dropdown ผู้ใช้ MOCK_USERS (user1–user5, utils/user.ts pure module ให้ client+server ใช้ร่วม) รหัสผ่านกลาง Demo1234 ผ่าน server action login (เช็ครายชื่อฝั่ง server เป็น defense กัน hand-craft + delay 500ms ให้เห็น "กำลังตรวจสอบ…") ตั้ง cookie oacCase ตาม format จริงของระบบศาล 16 field — u_userid/u_userlogin ต่างกันต่อคน (= ชื่อผู้ใช้) เพื่อให้สิทธิ์ "แก้ไขได้เฉพาะของตัวเอง" (guard ด้วย userId) แยกกันจริงทั้ง 5 คน httpOnly/sameSite lax/8 ชม. + middleware.ts บังคับ login ทุกหน้า (parse จริงด้วย parseOacCase ไม่ใช่แค่มี cookie — ขยะ/ว่าง/ขาด field = redirect /login; matcher ยกเว้น /login, /api/*, /uploads/*, _next และ static ใน public/ — SSE/export จึงไม่โดน 302) + lib/session.ts getSessionUser อ่าน identity ฝั่ง server ใช้ในทุกหน้า (defense ซ้อน middleware) + PDFViewer รับ user เป็น prop จาก annotate page ตั้งแต่ first paint (หมดช่วง null user ที่เคยสร้าง annotation ด้วย userId="" ได้ + ไม่มี hydration mismatch; Server Actions ไม่ต้องแก้ — vString รับ userId ใหม่อยู่แล้ว) + SessionBar แสดง "เข้าสู่ระบบเป็น <user>" + ปุ่มออกจากระบบ (ลบ cookie ด้วย path "/" ตรงตอน set + redirect ใน action เอง — client router.replace เองชนกับ re-render ของหน้าที่เพิ่งเสีย session จน navigation ไม่เกิด) แทน UserPicker mock 10 คน (ลบไฟล์พร้อม MOCK_USERS/localStorage/getLocalUser ทั้งชุด) + LoginForm การ์ด max-w-sm กลางจอ reuse brand lockup และปุ่ม primary เดิม, input text-base 16px กัน iOS zoom บน iPad, dropdown placeholder "เลือกผู้ใช้" บังคับเลือก (ไม่ preselect กัน login ผิดคนโดยไม่ตั้งใจ), error แถบเดียว role=alert "รหัสผ่านไม่ถูกต้อง" (dropdown เลือกผิดไม่ได้แล้ว), ตอนผิดล้าง password คงผู้ใช้ที่เลือกไว้ + focus+select + tests/utils/user.test.ts ล็อก format จริงของ e-Court (parse ตัวอย่าง cookie จริง 235821/tu_2, garbage/empty/ขาด field → null, round-trip, buildOacCaseCookie ครบ 16 field) + sync CLAUDE.md/PLAN.md §0 A4/README — annotation สมัย mock-user-01/UUID ไม่ใช่ของตัวเองอีก (auth จริงเทียบระบบศาล + roles ยัง out of scope; /api/* และ /uploads/* ยังไม่ต้อง login ตามขอบเขต mock)
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 13:41:02 +07:00
Your Name
460cd9c600 N7 รายงานรายการ bookmark เป็น PDF (requirement 5.3.15): route /api/bookmarks/export สร้างเอกสารใหม่ล้วนด้วย PDFDocument.create (ไม่โหลดไฟล์ต้นฉบับเลย — invariant #1) ต้นไม้ indent 3 ระดับเรียงตาม N5 + หัวรายงาน + แบ่งหน้าต่อบรรทัด + footer หน้า n จาก N + cycle guard (seen ร่วมทุก root เติมโหนดนอก root เป็นระดับ 1) + 0 bookmark = หน้าเดียวมีข้อความแจ้ง + ปุ่ม Download ท้ายแถว toggle เงื่อนไขค้นหาในแท็บ "ที่คั่นหน้า" (ลิงก์ธรรมดา pattern F16) + export F8: outline bookmark เรียงตามลำดับหน้าเหมือน sidebar (N5: pageIndex→yOffset→createdAt แทน createdAt เดี่ยว) + ถอด drawNoteBox กลับเป็น sticky note annotation อย่างเดียวตามสเปก §F8 ไม่วาดข้อความ flatten ทับหน้ากระดาษ (กล่องเหลืองแบนทับเนื้อหาเอกสาร — ผู้ใช้ตัดสินใจ; tooltip ตัดไทยใน Chrome เป็น bug ฝั่ง PDFium) + แผง selection ของ COMMENT/REDACTION ที่ไม่มี row สไตล์หุบเป็น w-fit ปุ่ม bookmark/ลบติดกันคั่นเส้นแนวตั้ง
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 49s
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
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 10:09:57 +07:00
Your Name
a4cac15e42 fix(deploy): อัปโหลด/เปิดไฟล์ใน Docker ไม่ได้ — 3 จุดรวมกัน
- app/uploads/[name]/route.ts: Next production (next start/standalone) อ่านรายชื่อ
  public/ เก็บใน Set ครั้งเดียวตอน startup เท่านั้น ไฟล์ที่ uploadFile เขียน
  ตอน runtime GET /uploads/* แล้ว 404 (dev ไม่ผ่าน branch นี้ = local ผ่าน
  Docker พัง) → เสิร์ฟผ่าน route handler แทน: path guard lib/server-pdf +
  whitelist ext + content-type จาก utils/file-kind + cache immutable
- drizzle/0001: bookmarks.user_name (N6) + backfill ตาม PLAN-BOOKMARK.md —
  image ใช้ migrate.mjs จากไฟล์ SQL ไม่ใช่ drizzle push ของ dev ทำให้ schema
  ตกรุ่นจนหน้า annotate 500 ทั้งหน้า; แก้ schema.ts ครั้งต่อไปต้อง drizzle-kit
  generate ทุกครั้ง (กติกาใหม่ใน .claude/rules/data-layer.md)
- docker/web.Dockerfile: mkdir public/uploads + chown nextjs — volume ที่
  compose mount ทับได้ ownership จาก image (ไม่งั้น root เป็นเจ้าของ = EACCES)
  + .dockerignore กัน public/uploads ของเครื่อง dev ติดไปใน image
- docker-compose.test.yml: compose ทดสอบบนเครื่อง (port 6704 + volume uploads)

verify บน Docker จริง: migration apply บน DB เดิมได้, annotate 500→200,
Playwright อัปโหลด JPG+PDF→เปิด viewer ได้ 7/7, tsc ผ่าน, vitest 120/120

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-16 15:59:01 +07:00
Your Name
a020e96bda bookmark เรียงตามลำดับหน้า (N5) + ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ พร้อม bookmarks.user_name (N6) + UX audit sidebar 2026-09-16: เมนู ⋯ แทนไอคอนชุดติดกัน, มุมมองรอง ตามหน้า หัวกลุ่มหุบได้, focus ring, ปุ่ม sidebar จิ้มง่าย >=32px, Escape ปิด dialog ประวัติ + แผนใหม่ docs/PLAN-BOOKMARK.md + agent ui-ux-designer แทน ui-designer + skill frontend-design
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-16 13:24:33 +07:00
DESKTOP-1R2VSQH\Lenovo ThinkPad E490
9cc38835f0 popup ซ้อนกัน/ไม่ปิดตอนกดปุ่ม tool + แท็บที่คั่นหน้ากลับไม่ได้ + งานค้างใน working tree: ไฟล์รูปภาพ annotate ได้ (N3) + อัปโหลดไฟล์ (N4):
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 2s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 40s
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
ส่วนที่ 1 — แก้ popup ตาม bug report ("กดแก้ไขแล้วไปกดที่ tool popup ของแก้ไขไม่ปิด/แสดงซ้อนกัน"):
(1) ปุ่ม tool แบบ dropdown ทั้ง 5 (highlight/draw/shape/stamp/textbox) เรียก switchTool เสมอแม้เป็น tool เดิม — เดิมกดปุ่ม tool ที่ active อยู่จะ toggle เมนูอย่างเดียวไม่เรียก switchTool (ซึ่งเป็นที่เดียวที่เคลียร์ popup บนหน้า: editor/แผง selection/popover) ผลคือ popup แก้ไขค้างเปิด + dropdown เปิดซ้อนขึ้นมาอีกชั้น — ตอนนี้จำ intent เปิด/พับเมนูไว้ก่อนเรียก (switchTool ปิดเมนูทุกตัว) แล้วเปิดคืนเฉพาะที่ตั้งใจ; toggle พับเมนูและกลไกกลืนคลิก M4 เท่าเดิม (mousedown บนปุ่ม mark อยู่แล้ว)
(2) switchTool ปิด popup bookmark ด้วย + openBookmarkComposer ปิดเมนูทั้ง 5 ก่อนเปิด popup (เดิมคีย์ลัด B ระหว่างเมนูเปิด = สอง popup ซ้อนกัน)
(3) คลิกหน้าว่าง = ปิด popover ของ comment pin + เลือกรายการอื่น/goToAnnotation ปิด popover เดิมทิ้ง (เดิมปิด popover ได้แค่กด pin เดิม/Escape/สลับ tool)
(4) COMMENT ที่เปิด popover อยู่ไม่ render กรอบ selection/แผงเครื่องมือ — bbox ของ COMMENT ครอบพื้นที่ pin พอดี แผงจะทับ popover และแถบ handle บังการคลิก (sidebar ยังตาม selectedId ไฮไลต์เหมือนเดิม)
(5) แก้แท็บ sidebar ตาม bug report ("เลือก Annotation แล้วจะกลับไป tabs bookmarks ไม่ได้"): effect เดิม dep รวม tab และบังคับ setTab ไปแท็บ annotations ทุกครั้งที่รันตราบ selectedId ไม่ null ทำให้กดแท็บที่คั่นหน้าแล้วถูกดึงกลับทันที — ใช้ ref จำ id ที่ประกาศแล้ว บังคับสลับเฉพาะตอน selectedId เพิ่งเปลี่ยน เคลียร์เป็น null ตอน deselect ให้เลือกรายการเดิมซ้ำภายหลังยังสลับแท็บให้ ส่วน scroll แถวตอนเปิดแท็บหมายเหตุคงเดิม
- sync rules/pdf-viewer.md: กติกาใหม่ popup บนหน้าเปิดได้ทีละชุดห้ามค้างเปิดซ้อนกัน (ปุ่ม dropdown เรียก switchTool เสมอ + วงจรปิด popover/แผง) และยกเว้น markMenuClosed ของคีย์ B ในกติกา M4
- verify: tsc ผ่าน, vitest 120/120, Playwright 18 เคสผ่าน (repro ตรงตามรายงาน: สร้าง textbox → แก้ไขข้อความ → กดปุ่ม tool เดิม → editor+แผงปิด+เมนูใช้ได้; popover pin ไม่ซ้อนแผง/ปิดด้วยคลิกหน้าว่าง; popup bookmark ปิดตอนสลับ tool; แท็บที่คั่นหน้ากลับได้)

ส่วนที่ 2 — งานที่ค้างใน working tree ก่อนหน้า (ตามแผน docs/PLAN-IMAGE.md):
- N3 รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด (viewer render img แทน canvas ไม่มี textLayer, scale contract เดียวกับ export) + TIFF หลายเฟรม = หลายหน้า (decode ผ่าน utif2 dynamic import → PNG blob URL ต่อเฟรม ฝั่ง client) + export PDF ฝังรูป (utils/image-to-pdf.ts: embedJpg/embedPng + EXIF rotate) + utils/file-kind.ts derive ชนิดไฟล์ + tests ของทั้งสอง utils
- N4 อัปโหลดไฟล์ PDF/รูป ≤100MB ผ่านหน้ารายการ (components/UploadFileForm.tsx + app/actions/files.ts: validate + เขียนไฟล์ใหม่ที่ public/uploads/ เท่านั้น — invariant #1)
- เอกสาร: docs/PLAN-IMAGE.md เพิ่มใหม่ + sync PLAN.md/PLAN-FEAT.md/CLAUDE.md/README/gap-analysis/data-layer rule

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-15 14:12:51 +07:00
DESKTOP-1R2VSQH\Lenovo ThinkPad E490
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>
2026-09-15 12:13:05 +07:00
DESKTOP-1R2VSQH\Lenovo ThinkPad E490
df0392ea45 จอสัมผัส (iOS/Android): แก้ครบตาม UX audit C1+M1–M6 + code review + งาน export PDF ที่ค้างใน working tree:
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 42s
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
(1) C1 — เมนู dropdown 5 ตัว + popup bookmark เดิมเป็น absolute ใน scroll container ของแถบ tool ที่สลับ overflow ตอนเปิด ทำให้บนมือถือ scroll position รีเซ็ต (ปุ่มเด้งกลับซ้าย) เมนูเปิดนอก viewport และทั้ง app เอียงเลื่อนแนวนอนได้ — ย้ายทั้งหมดไป createPortal(document.body) เป็น position:fixed ตาม rect จริงของปุ่มตอนคลิก (fixedMenuPos + clamp ให้อยู่ในจอ; bookmarkPos จาก ref ปุ่ม 🔖 ใช้ร่วมทุกทางเข้า รวมคีย์ลัด B) + ถอดการสลับ overflow (คง overflow-x-auto เสมอ) + ปิดเมนูเมื่อเลื่อนแถบต่อ หรือ "ความกว้าง" จอเปลี่ยน (code review Major1: resize ที่เปลี่ยนแค่ความสูง เช่น URL bar ยุบ/คีย์บอร์ดของมือถือ ไม่ปิด — เมนู fixed ยึด sticky header อยู่กับที่อยู่แล้ว กันเมนู/popup ที่เปิดค้างปรับค่าโดนปิดกลางคัน) + outside-click ข้ามโหนดใน [data-tool-menu] (ref ของ wrapper มองไม่เห็นโหนดใน portal ไม่ข้ามแล้วคลิกในเมนู=ปิดเมนูเอง) + rule ปุ่ม 36px ใน globals.css เปลี่ยนไป target [data-tool-menu] ตรง ๆ (เดิมเจาะผ่าน .z-50 ที่ไม่มีแล้ว)

(2) M4 — tap/คลิกที่ใช้ "ปิดเมนู" (mousedown ปิดก่อน click ยิงถึง page) กลายเป็นวาง comment/textbox/stamp โดยไม่ตั้งใจ — menuClosedAtRef (markMenuClosed ทุก path ปิด: outside-click/Escape/switchTool เฉพาะเมื่อมีเมนูเปิดจริง/เลื่อนแถบ/resize) แล้ว handlePageClick เฉย 300ms แรก คลิกที่สองค่อยวาง (มาตรฐาน dropdown ใช้ทั้ง desktop+touch — pattern เดียวกับ touchHideTimerRef ของปุ่มไฮไลต์)

(3) M1+M2 ของ TEXTBOX — ทางเข้า editor เคยผูก double-click อย่างเดียว (บน iOS double-tap = zoom หน้า แก้ข้อความไม่ได้เลยตลอดชีวิตบนมือถือ) — เพิ่มปุ่ม "แก้ไขข้อความ" ในแผง selection (onStartEdit → startTextboxEdit) ใช้ได้ทั้ง touch/desktop และ default fontSize 1.2% (≈7px เล็กกว่า preset ต่ำสุด 10px ของตัวเอง) → 2.5% (≈15px อ่านออกทันที) กระทบเฉพาะกล่องใหม่

(4) M3+clamp — hit area บน pointer:coarse โดยของที่มองเห็นคงขนาดเดิม: มุมกรอบใช้ ::after (10→hit 28px), แถบขอบ (8→16px), ปุ่มแผง 20→36px, swatch 16→32px, icon sidebar 16/14→32px (hooks sel-handle/sel-strip/sel-panel/sel-panel-btn/color-swatches + aside[data-sidebar]) และ clamp แนวนอนของแผง selection (left: max(0, min(box.x, size.width-244))) กันรายการชิดขวาทำแผง w-60 ล้นขอบจอแคบจนปุ่มลบหลุดจอ (เจอตอน verify จอ 390px)

(5) M5 — undo/redo max-lg:order-first ไปหัวแถวเลื่อน: เห็นเสมอโดยไม่ต้องเลื่อนแถบ (undo คือทางแก้พลาดหลักของจอสัมผัส) จอ ≥ lg ไม่มี order ลำดับ/หน้าตาเดิมทุกประการ

(6) M6 อื่น ๆ — -webkit-tap-highlight-color: transparent + overscroll-behavior-y: contain (กัน pull-to-refresh ของ Android ล้าง undo history ฝั่ง client), button/label touch-action: manipulation (ตัด double-tap zoom), safe-area inset แบบ defensive (จะมีค่าเมื่อใส่ viewport-fit=cover ในอนาคต), no-callout กัน long-press magnifier เฉพาะ layer TEXTBOX/STAMP (textLayer ยัง select/copy ได้), ปุ่ม composer/popover/pin 24→36px (coarse-36), ปุ่มหน้าสรุป 30→44px (summary-page), ซ่อนชื่อไฟล์บนจอเล็ก, ปุ่ม X ปิด sidebar บน max-lg (เดิมปิดได้ทางเดียวคือจิ้ม backdrop เพราะ overlay z-50 กลบปุ่ม toggle ใน header), tab sidebar เป็นไทย "ที่คั่นหน้า/หมายเหตุ" ให้สอดคล้อง UI ไทยล้วน, chip แจ้ง "หน้าที่เปิดดูเป็นภาพสแกน" เมื่อ tool highlight และทุกหน้าที่ render มี 0 text spans (onTextLayerRendered รายงานต่อหน้า + pageSpanCount)

(7) งาน export PDF ที่อยู่ใน working tree ก่อนหน้า + แก้ตาม code review — COMMENT กลายเป็น sticky note จริง (/Subtype /Text), bookmark → PDF outline (/Dest คำนวณตาม /Rotate เหมือน annotation), โน้ต N1 → sticky note แนบตำแหน่ง, นิยาม yOffset เป็น "ขอบบนของสิ่งที่ดูอยู่" + goToBookmark ใช้ scrollToBelowHeader, และเพิ่ม guard roots.length===0 ใน buildOutline กัน bookmark cycle ไร้ root (DB ถูกแก้ตรง ๆ) ทำ kids[0].ref พังเป็น 500 ทั้ง export

(8) sync .claude/rules/pdf-viewer.md — portal เมนู+ห้ามสลับ overflow ของแถบ, guard 300ms, hit-area hooks ระดับ coarse, TEXTBOX default 2.5% + ปุ่มแก้ไขข้อความ

verify: tsc ผ่าน · vitest 100/100 · Playwright touch emulation จริง (iPhone 390×844 / iPad 820×1180 / desktop 1280): เมนูอยู่ใน viewport ทุกสถานะหลังเลื่อนแถบ + scrollLeft คงที่ + ไม่มี horizontal overflow, แตะในเมนูไม่ปิดเอง, เลื่อนแถบ/กว้างจอเปลี่ยน=ปิด/สูงเปลี่ยน=ค้าง, guard 300ms ทั้ง stamp/textbox/comment, ปุ่มแก้ไขข้อความเปิด editor พร้อมข้อความเดิม, fontSize ใหม่ 14.9px, hit มุม 28px/ปุ่มลบ 36px/swatch 32px, undo อยู่ในจอ + undo/redo ทำงาน, X ปิด sidebar, chip สแกนโชว์/ไฟล์ text ไม่โชว์, desktop เหมือนเดิม, แผง clamp (x=130..370 ปุ่มลบจบ 363) — annotation ทดสอบลบเฉพาะ id ที่สร้างเองทั้งหมด DB กลับสะอาด

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-14 18:04:40 +07:00
DESKTOP-1R2VSQH\Lenovo ThinkPad E490
6ddc35d6d5 เลือก annotation บนหน้า PDF → sidebar ตามไปเน้นรายการนั้น + สร้างเสร็จเลือกให้ทันที + กัน duplicate ชั่วขณะตอนสร้าง: (1) ส่ง selectedId ให้ AnnotationSidebar — ไฮไลต์แถว (ทั้งลิสต์หลักและผลค้นหา) + scrollIntoView block:nearest + สลับมาแท็บ Annotations ให้เองถ้ากำลังอยู่ Bookmarks (rAF รอแถว render หลังสลับแท็บค่อยเลื่อน) (2) AUTO_SELECT_TYPES — สร้าง DRAW/SHAPE/TEXTBOX/STAMP/HIGHLIGHT/REDACTION เสร็จ = เลือกตัวใหม่ทันทีด้วย saved.id (กรอบ selection + sidebar ตามไปเลย; COMMENT ยกเว้นเพราะ composer/pin ทำหน้าที่นั้นอยู่แล้ว) — แก้ปัญหา "วาดเสร็จคลิกที่เส้น tool ยังเป็นวาด sidebar นิ่ง" (3) กันอาการ row โผล่แล้วหาย (0→2→1): เดิม optimistic row กับ row จาก server คนละ id ทำให้ SSE event มาก่อน action เสร็จ dedupe ไม่ได้ — ตอนนี้ client ส่ง id ของ optimistic row ให้ createAnnotation ใช้ซ้ำ (validate UUID, ไม่ส่ง = สุ่มเองตามเดิมสำหรับ redo path) ทุกชั้น dedupe ด้วย id เดียวกันตั้งแต่แรก — sync PLAN.md §4 + rules/realtime.md ให้ตรงพฤติกรรมใหม่
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-14 11:35:05 +07:00
DESKTOP-1R2VSQH\Lenovo ThinkPad E490
4934b9d0b3 eraser ลบตรายาง (STAMP) ของตัวเองได้: hit-test ใน InkLayer.eraseAt ด้วย bbox จาก annotationBBox (สูตรเดียวกับ render) เช็คตราก่อนเส้น/รูปทรงเพราะตราอยู่ layer บน — ตอน tool erase ค่า TextboxStampLayer ปิด pointer-events อยู่แล้วจึงไม่ต้องแตะ layer stack
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-14 09:59:01 +07:00
DESKTOP-1R2VSQH\Lenovo ThinkPad E490
b588343d50 bookmark: pre-fill ชื่อ default "หน้า N" + focus เลือกข้อความทั้งหมด
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 5s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 45s
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
เดิมเปิด popup แล้วช่องชื่อเว้นว่าง (fallback "หน้า N" แค่ตอนบันทึก) — ตอนนี้
pre-fill ทันทีทั้งจากปุ่ม 🔖/คีย์ลัด B (ไม่มีข้อความที่คลุม) และปุ่ม bookmark
ในแผง selection (ชนิดที่ไม่มีข้อความ) พร้อม select ข้อความ default ไว้
ให้พิมพ์ทับ/Enter ยอมรับได้ทันที + sync spec N2 ใน PLAN-FEAT.md

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-11 17:45:31 +07:00
DESKTOP-1R2VSQH\Lenovo ThinkPad E490
ca74fb1843 TEXTBOX กล่องกว้างคงที่สไตล์ Smallpdf: คลิกเดียวได้กล่อง 30% + ลากขอบซ้าย/ขวาปรับความกว้าง (ฟอนต์คงขนาด ข้อความ wrap ใหม่)
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-11 17:41:22 +07:00
DESKTOP-1R2VSQH\Lenovo ThinkPad E490
fbc78b78e9 เปลี่ยนชื่อระบบ Jude's Note → Judge's Note
แก้เฉพาะชื่อที่แสดงต่อผู้ใช้และเอกสาร: หัวเว็บหน้าแรก (app/page.tsx),
<title> ทุกหน้า (app/layout.tsx), README, CLAUDE.md, docs/PLAN.md,
docs/PLAN-FEAT.md, .claude/agents/* — โลโก้ JN ใช้ initials เดิมได้

คง identifier ทางเทคนิคไว้: container judes-note-db + volume, package.json
name, localStorage key "judes-note:user" (เปลี่ยนแล้ว identity/ข้อมูลเดิมหาย)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-11 13:37:31 +07:00
DESKTOP-1R2VSQH\Lenovo ThinkPad E490
6fd3a1515f ซ่อม text ไทย PDF (ActualText) + revalidatePath ทุก action + คีย์ลัด DEL + ตรายาง label บรรทัดเดียว
- โหลด PDF ผ่าน /api/pdf?fileId= ที่สร้าง ToUnicode CMap ใหม่บนสำเนาในหน่วยความจำ
  จาก ActualText ใน marked content (lib/pdf-actual-text.ts + unit tests) — แก้
  ฟอนต์ไทยที่ cmap เพี้ยน (คำสั่งศาล) ให้ textLayer select/copy/ค้นหา/ชื่อ bookmark
  ถูกต้องตั้งแต่ต้น ไฟล์ใน public/ ไม่ถูกแตะ (invariant #1) — PDF ปกติได้ bytes
  เดิม ไม่มี cost และ fail แล้ว PDFViewer fallback fetch ไฟล์ดิบเอง
- ข้อความจาก selection ห้าม selection.toString() ตรง ๆ: ใช้ selectionTextWithin
  เดินทีละอักษร เรียงตามตำแหน่งบนจอ (จัดการ Thai combining marks + ctrl ขยะ)
  ปิดท้ายด้วย sanitizePdfText ก่อนเก็บ DB/ชื่อ bookmark/คลิปบอร์ด (มี tests)
- mergeRectsByLine ใช้ median ความสูง rect คำนวณเกณฑ์บรรทัดแทนค่าคงที่
- Server Actions ทุกตัวที่เขียน DB เรียก revalidateFilePages (revalidatePath
  หน้า viewer+summary) คู่กับ publish event — แก้กด back แล้วเข้าไฟล์ใหม่ได้
  RSC payload เก่าจาก Router Cache ฝั่ง client จนต้อง hard refresh
- คีย์ลัด DEL/Backspace ลบ annotation ที่เลือกอยู่ (ของตัวเองเท่านั้น ข้ามเมื่อ
  focus อยู่ใน input/textarea/select/contenteditable)
- STAMP: ย่อฟอนต์ label ให้อยู่บรรทัดเดียวพอดีกรอบทั้งบนจอ (canvas measure) และ
  export PDF (stampFitFontSize แชร์กัน) + เพิ่ม handle ปรับขนาดตรายางในแผง
  selection (uniform scale + clamp รายแกน)
- sync invariant/กติกา: CLAUDE.md #4, rules/*.md, PLAN-FEAT.md (F3)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-11 13:28:35 +07:00
DESKTOP-1R2VSQH\Lenovo ThinkPad E490
eddcec28c7 Add application codebase: PDF annotation for e-Court
Next.js 14 + Drizzle/PostgreSQL + SSE realtime sync — highlight,
freehand, comment, bookmark + ฟีเจอร์ต่อยอด F1–F19 (edit/move, undo/redo,
shapes, redaction, export PDF/CSV, reply, resolve, search, summary,
textbox, stamp, version history)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-10 14:29:40 +07:00