Commit graph

  • 6ce071ee14 fix(token): restore environment variable checks for DOCVER_API_KEY and DOCVER_TOKEN_SECRET dev v1.0.34 Suphonchai Phoonsawat 2026-10-26 18:37:02 +07:00
  • ab62a2b3e2 feat(docker): add MinIO configuration and token settings in Dockerfile v1.0.33 Suphonchai Phoonsawat 2026-10-26 13:02:25 +07:00
  • c06da83f92 fix(docver): update token secret handling to use hardcoded value for testing v1.0.32 Suphonchai Phoonsawat 2026-10-26 12:37:29 +07:00
  • 86353cbe57 refactor(docver): improve code readability and structure in token handling functions v1.0.31 Suphonchai Phoonsawat 2026-10-26 12:29:08 +07:00
  • 0b2c9e6343 feat(docver): derive docId from fid claim and drop [docId] URL segment v1.0.30 Suphonchai Phoonsawat 2026-10-26 10:05:01 +07:00
  • aad6f155fe Merge branch 'dev' into working Suphonchai Phoonsawat 2026-10-26 09:02:13 +07:00
  • 98b370c925 feat(docver): add access token endpoint and Bearer auth on all endpoints Suphonchai Phoonsawat 2026-10-26 06:29:19 +07:00
  • ac6c0dc71e Add new template format with hashing algorithm v1.0.29 oom 2026-10-22 22:33:14 +07:00
  • 280cc2da40 Merge branch 'dev' into feature/versionning feature/versionning Suphonchai Phoonsawat 2026-10-20 11:03:12 +07:00
  • 949861ba86 Merge branch 'dev' of https://forgejo.chamomind.com/chamomind/eCourt-note into dev Suphonchai Phoonsawat 2026-10-20 11:03:02 +07:00
  • ed8493004c Merge branch 'dev' of https://forgejo.chamomind.com/chamomind/eCourt-note into feature/versionning Suphonchai Phoonsawat 2026-10-20 11:02:02 +07:00
  • 765946f0d5 feat(docver): add document versioning API (docx+pdf in MinIO) Suphonchai Phoonsawat 2026-10-20 10:52:35 +07:00
  • 6daee15851 feat(wordaddin): add template tools tab v1.0.28 Your Name 2026-10-20 09:49:17 +07:00
  • 5917c0532c file Suphonchai Phoonsawat 2026-10-20 09:35:25 +07:00
  • a172b111a3 docs(wordaddin): sideload dev ชี้ trusted catalog ที่ public/wordaddin Your Name 2026-10-16 14:48:04 +07:00
  • 1aeaae38f1 fix(wordaddin): lower WordApi requirement for Office 2019 Your Name 2026-10-16 14:20:24 +07:00
  • 4bcd01a95f feat(wordaddin): serve manifest with automatic origin rewrite v1.0.27 Your Name 2026-10-16 13:54:25 +07:00
  • 808233191d fix(docker): copy public/ into production image v1.0.26 Your Name 2026-10-16 13:28:04 +07:00
  • 2b70a50c62 feat(wordaddin): add e-Court pass-through sign-in v1.0.25 Your Name 2026-10-16 13:06:28 +07:00
  • dc851fbb73 chore: add pnpm lockfile Your Name 2026-10-16 12:11:12 +07:00
  • 33670d0b18 chore(template): add manual test fixture Test.docx Your Name 2026-10-16 12:11:03 +07:00
  • c401f4deb7 feat(wordaddin): port Word add-in taskpane from poc Your Name 2026-10-16 12:07:25 +07:00
  • 7a146d48be fix(config): move root redirect to page and skip slash redirect v1.0.24 Your Name 2026-10-08 11:11:48 +07:00
  • b41816e2b6 feat(template): add restrict-edit protection script v1.0.23 Your Name 2026-10-07 16:55:30 +07:00
  • 073f72db7d feat(api): add swagger ui and docx template api Your Name 2026-10-07 10:16:12 +07:00
  • 8cc15ea75d refactor(routes): move routes under /note, APIs under /api/note v1.0.22 Your Name 2026-10-06 14:55:07 +07:00
  • b123ecc9b2 fix(pdf-selection): match text to dragged lines v1.0.21 Your Name 2026-10-05 11:31:33 +07:00
  • c08aaab1ed fix(upload): decode mojibake of Thai filenames Your Name 2026-10-05 11:30:56 +07:00
  • e4ecc026b0 perf(pdf-viewer): limit drag preview re-render to its page v1.0.20 Your Name 2026-09-29 14:49:05 +07:00
  • 0681e056fd refactor(toolbar): extract toolbar tool logic to module Your Name 2026-09-29 14:47:12 +07:00
  • 8571ef9aaf toolbar: ปุ่ม tool dropdown แสดงค่าที่เลือกเฉพาะตอน active + ฟื้น effect เลื่อนปุ่ม active กลับกึ่งกลางแถบ (เสียเงียบมาตั้งแต่ 2026-09-23) — (1) ปัญหา: คำขอผู้ใช้ 2026-09-23 เดิมให้ปุ่ม dropdown ที่มีค่าย่อย (มาร์กข้อความ/วาดเส้น/รูปทรง/ตรายาง) แสดง "ชื่อตัวเลือกล่าสุด" แทนชื่อ tool เสมอเพื่อเห็นค่าปัจจุบันโดยไม่ต้องเปิดเมนู — แต่ทำให้ชื่อ tool หายไปจาก toolbar ทั้งหมด: ปุ่ม "วาดเส้น" แสดง "ปากกา" ตั้งแต่ยังไม่ได้เลือกอะไรเลย (default drawPen=pencil) และค้างอยู่อย่างนั้นแม้สลับไป tool อื่น (ผู้ใช้รายงาน 2026-09-29: "label ของ tool ทำไมไม่เปลี่ยนทั้งที่ยังไม่ได้เลือน เช่น วาด แต่แสดง ปากกาเลย") — ขัดเหตุผลเดิมของการใส่ข้อความบนปุ่ม (2026-09-21: ผู้ใช้ศาลไม่คุ้น icon ล้วน) เพราะมองคำ "ปากกา" แล้วไม่รู้ว่านี่คือปุ่มวาดเส้น (2) แก้ตามคำขอผู้ใช้ 2026-09-29 (เลือกจาก 3 ทางเลือกที่เสนอ): tool ไม่ active → แสดง text ชื่อ tool + Icon ของ tool, title/aria-label = label เต็มเหมือนปุ่มเดี่ยวอื่น; tool active → แสดงค่าที่เลือกไว้ (ค่าที่จะใช้จริงเมื่อวาด) + icon ของค่า (draw/highlight เปลี่ยน icon ตามชนิดปากกา/รูปแบบมาร์ก, shape/stamp ใช้ tool icon เหมือนเดิม), title = "label — ค่า" ตาม pattern เดิม — กดปุ่ม dropdown เรียก switchTool เสมออยู่แล้ว (เคลียร์ popup) ปุ่มจึงสลับไปแสดงค่าทันทีที่เปิดเมนู สอดคล้องกับ "กำลังจะใช้ค่านี้" — ปุ่ม active กว้าง/แคบตามความยาวค่า ("ปากกา" สั้นกว่า "วาดเส้น") แถบรองรับอยู่แล้ว (whitespace-nowrap + max-lg:overflow-x-auto) (3) แก้ bug แฝงที่เจอระหว่างแก้: effect เลื่อนปุ่ม tool active กลับกึ่งกลางแถบบนจอ < lg (UX review A2 2026-09-21) ใช้ querySelector(button[title="..."]) exact-match ซึ่งไม่เจอปุ่ม dropdown ทั้ง 4 มาตั้งแต่ 2026-09-23 เมื่อ title ถูกเติม " — ค่าย่อย" ต่อท้าย — ฟีเจอร์เลื่อนปุ่มกลับจึงเสียเงียบ ๆ กับ 4 ปุ่มนี้ตลอดมา — เพิ่ม data-tool={key} ที่ปุ่ม tool ทุกตัว (ครบ 10/10 ค่าของ union Tool ไม่ซ้ำ) แล้ว query ด้วย button[data-tool="..."] แทน title (ตัด TOOLS.find ออกจาก effect — TOOLS ยังถูกใช้ที่ visibleTools จึงไม่ orphan) (4) แก้ตาม code review 2026-09-29: aria-pressed ของปุ่ม dropdown 4 ตัวใช้ตัวแปร active ใหม่ (const active = tool === key ประกาศครั้งเดียวใน map ใช้ร่วมกับ activeCls) แทน tool === "…" ซ้ำ (ค่าเท่ากันเป๊ะ branch บอง key) + sync rules/pdf-viewer.md ประโยค "ปุ่ม tool แสดง label กำหนดเอง" ให้ระบุเงื่อนไขเฉพาะตอน tool active ตรงโค้ดปัจจุบัน — ผ่าน code review (ไม่มี bug ระดับต้องแก้: active/activeCls equivalent ทุก branch, nested ternary stamp parse ถูก, ตำแหน่งเมนูไม่เพี้ยนเพราะ openToolMenu อ่าน getBoundingClientRect ก่อน React commit setState ที่ batch ไว้ + เมนูยึดขอบซ้ายปุ่ม, effect A2 กลับมาทำงานพร้อม guard เดิมครบ anyMenuOpen/selfScrollUntilRef) + tsc + vitest 193/193 (คำขอผู้ใช้ 2026-09-29) v1.0.19 Your Name 2026-09-29 10:51:50 +07:00
  • 651e354cad stamp-editor: ตรายางพิมพ์เอง+จัดบรรทัดแบบถอยหลัง+มุมเอียง, editor กล่องข้อความ contentEditable + แก้ตาม code review 2026-09-29 ครบ 5 ข้อ — (1) STAMP คำพิมพ์เอง (D7): dropdown ของ tool stamp เป็น 2 หน้า — preset + แถว "กำหนดเอง…" (stampCustomMode reset ทุกครั้งที่เปิดเมนู) เปิด popup แก้ข้อความ/สี/ขนาด พร้อม preview ตรายาง live ตามสัดส่วนจริง (ความกว้างหน้าแรก × scale%) ใช้สูตร/ฟอนต์/measure ตัวเดียวกับ render จริง, คำพิมพ์ live ไป stampPreset เดียวกับ preset, Enter/ปุ่ม "ใช้ตรายางนี้" = ปิดเมนูแบบเลือกค่าจบ, ปุ่ม tool แสดง "กำหนดเอง" แทนคำเต็ม + แก้คำตรายางที่วางแล้วผ่านปุ่ม "แก้ไขข้อความ" ในแผง selection (StampEditor textarea ใน TextboxStampLayer ใช้ editingId/commitEdit ร่วมกับ TEXTBOX, จำกัด STAMP_MAX_LABEL_CHARS=50 ที่ UI — validator ยังยอม MAX_TEXT_CHARS ตามเจตนา) (2) จัดบรรทัดแบบถอยหลัง (คำขอผู้ใช้รอบสอง): สูตรย้ายรวมที่ utils/geometry.ts — stampLayout: คำสั้น 1 บรรทัดฟอนต์เต็ม, คำยาวพอดี ๆ ย่อฟอนต์ให้อยู่บรรทัดเดียวก่อน (stampFitFontSize เชิงเส้น) จนถึง floor 10% ของความกว้างกรอบ ("ตรวจสอบแล้ว" ย่อ ~25% = บรรทัดเดียว), ยาวเกิน floor กลับไปฟอนต์ใหญ่แล้ว wrapStampLabel ≤3 บรรทัด (×0.92/รอบ เกินบรรทัดยอมแลกกับฟอนต์อ่านออก), stampBoxHeight ให้ 1 บรรทัด = 0.4w เป๊ะเหมือนเดิม — ทุกมิติเป็นสัดส่วนของ w จำนวนบรรทัดจึงไม่ขึ้นกับ scale — constants + ฟังก์ชัน re-export จาก utils/annotation-display.ts ให้ import เดิมทุกจุด + measureStampText (canvas bold system-ui คืน 0 ตอน SSR) เป็น measure ของ render/preview/hit-test/eraser ทั้งฝั่ง client (annotationBBox รับ optional measure — ให้ความสูงกรอบตรง render จริง แก้ขอบล่างกรอบเกินตรายาง) ส่วนฝั่ง server (validator/export sticky note/describeAnnotationChange) ใช้ stampLayoutEstimate ความกว้างต่ออักษร 0.58 (3) มุมเอียง StampData.rotation? (−180..180 หมุนรอบกลางกรอบ): ช่อง "มุมเอียง" ในแผง selection (patch stampRotation, 0° = ถอด field) + จุดจับหมุนวงกลมเหนือขอบบน (mode rotate — atan2 delta รอบกลางกรอบ + snap ทวีคูณ 15° ≤4°), กรอบ selection+handles หมุนด้วย CSS transform ที่ origin เป็นพิกัดใน element ผ่าน rotOn() (ใส่พิกัด page แล้ว div แต่ละตัวหมุนคนละจุดจนกรอบไม่ตรงตัวตรายาง — bug 2026-09-28), annotationBBox คืน AABB ของกรอบหมุน (stampRect/rotatedRectAABB/rotatePoint — anchor handle scale เป็นมุมหลังหมุนบนจอ และ k คำนวณใน frame ของกรอบ), clampStampIntoPage เลื่อน position กลับเข้าหน้าตอนตั้งมุม (validator ตรวจ AABB ไม่ติดลบ ไม่ใช่ position ตรง ๆ — position มุมก่อนหมุนติดลบได้เมื่อกรอบจริงยังในหน้า), StampEditor แสดงตรง 0° พิมพ์ง่าย ปิดแล้วกลับไปเอียงค่าเดิม, export ใช้ pdf-lib rotate: degrees(−θ) + เลื่อน anchor ไปมุมล่างซ้ายหลังหมุน (pdf-lib หมุนรอบ anchor ไม่ใช่ center) + per-line baseline หมุนรอบกลางกรอบตรง CSS rotate ทุกองศา (4) TextboxEditor เป็น contentEditable WYSIWYG แทน textarea (ตัวหนาเฉพาะส่วน Ctrl/Cmd+B + Enter = insertLineBreak ไม่แตก div, paste เป็น plain text): utils/textbox-editor.ts ใหม่ — segmentsToHtml (escape & < > ก่อนใส่ dangerouslySetInnerHTML + <b> + <br>) และ serializeEditor (DOM → text รวม + segments แยกตัวหนา: ขึ้นบรรทัดจากทั้ง <br> และ <div>/<p> ที่ browser สร้าง — push \n ก่อนเข้า block ไม่ใช่หลังจบ block ไม่งั้น "abc<div>def</div>" ของ Chrome กลายเป็น "abcdef", dedupe <br>+block ติดกัน, trimEnd ตัดจาก segments ให้ตรง text กัน <br> ปิดท้ายของ browser ทิ้ง \n ค้างใน DB) — แยกเป็น pure DOM util ให้ unit test กับ jsdom ได้ (tests/utils/textbox-editor.test.ts ใหม่ 16 เคส ตาม pattern useCustomSelection-dom) (5) คิว update ต่อ id ใน PDFViewer (updateChainRef + pendingDataBaseRef): update ทุกตัวของ id เดียวกันเข้าคิวเดียวกัน base คำนวณตอนถึงตาคิวจาก data ล่าสุดที่คิวก่อนหน้าบันทึกไว้ — จอสัมผัส blur-commit ข้อความแล้วแตะสี/ขนาดต่อทันที คอมมิต full object จาก base เก่าจะทับค่าที่เพิ่งเปลี่ยน (code review 2026-09-28 รอบสอง) — ตัวควบคุมเล็กของแผง/editor commit เป็น AnnotationPatch ผสานผ่าน applyAnnotationPatch (color/strokeWidth/highlightStyle/highlightOpacity/stampScale/stampRotation/textbox/textboxText/label) (6) describeAnnotationChange: STAMP แสดง "มุมเอียง" และข้าม geometry เมื่อ label/มุมเปลี่ยน (geometry ต่างเป็นผลพลองของการจัดวาง แบบเดียวกับ TEXTBOX ที่ข้ามปรับขนาดเมื่อข้อความเปลี่ยน) (7) แก้ตาม code review 2026-09-29 ครบ: (a) patch stampScale/label ของตรายางเอียงเรียก clampStampIntoPage เหมือน stampRotation — เดิมขยาย/แก้ข้อความที่ชิดขอบแล้ว AABB ยื่นติดลบ update โดน validator ปฏิเสธเด้งกลับทั้งที่บนจอกรอบยังอยู่ในหน้า (b) validator STAMP ผ่อน tolerance ให้ AABB ติดลบได้ ≤2% ของหน้า กลืนความคลาด measure จริงของ browser กับ stampLayoutEstimate ฝั่ง server (ฟอนต์ต่อเครื่องทำให้จำนวนบรรทัด/AABB คลาด — ค่าที่บนจอพอดีเป๊ะต้องไม่ถูกปฏิเสธ) (c) applyUpdate: applyAnnotation เข้า try เดียวกับ action ให้ resolve() ใน finally ครอบทั้ง callback — เดิม dispatcher โยน sync แล้ว promise ไม่ settle ตลอดกาลจนคิวของ id นั้นตายถาวร + enqueueUpdate เปลี่ยน then(cleanup, cleanup) กัน unhandled rejection (d) catch ของ applyUpdate ล้าง pendingDataBaseRef — เดิม action กลางคิวล้มแล้ว update ถัดไปผสาน base ที่ไม่เคยติด DB จน commit การเปลี่ยนที่ล้มไปด้วย และ undo กู้สถานะที่ไม่มีอยู่จริง (e) ถอด as StampData cast 2 จุดใน AnnotationSelectionLayer — ย้าย stampData ที่ narrow ด้วย "label" in ขึ้นใช้แทนให้ type guard ทำงาน (f) export sticky note ของ STAMP ส่ง measure เดียวกับตอนวาดกรอบ (thaiFont.widthOfTextAtSize) ให้มุมเกาะตรงกรอบจริงไม่ใช่ estimate (8) InkLayer eraser ของ stamp ส่ง measureStampText ให้ bbox ตรง render จริง (9) docs sync ครบ: PLAN-FEAT.md D7/F18 + CLAUDE.md + rules/coordinates.md (patch เปลี่ยนขนาดกรอบเอียงต้อง clamp + tolerance validator 2%) + rules/pdf-viewer.md — ผ่าน code review (ไม่มี bug ระดับต้องแก้) + tsc + vitest 193/193 (คำขอผู้ใช้ 2026-09-28/29) Your Name 2026-09-29 09:56:00 +07:00
  • 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) Your Name 2026-09-28 11:01:09 +07:00
  • c2ed9dabc1 docker: แก้ build ล้มที่ stage deps — postinstall หา scripts/patch-pdf-worker.mjs ไม่เจอ — เพิ่ม COPY scripts/patch-pdf-worker.mjs scripts/ ก่อน npm ci: Dockerfile เดิมคัดลอกแค่ package.json/package-lock.json แล้วสั่ง npm ci ทันที แต่ postinstall (node scripts/patch-pdf-worker.mjs ตาม invariant #6 — Docker build ต้องใช้ตัว patch ไม่ใช่ copyFileSync เปล่า) รันตอน npm ci ซึ่งไฟล์ script ยังไม่ถูกคัดลอกเข้า image (จะเข้าเองใน stage builder ผ่าน COPY . . ซึ่งช้ากว่า) → MODULE_NOT_FOUND exit 1 — คัดเฉพาะไฟล์เดียวไม่ทำลาย cache layer ของ dependencies (แก้ script อื่น migrate/seed-samples ไม่ต้อง install ใหม่) (คำขอผู้ใช้ 2026-09-25) v1.0.18 Your Name 2026-09-25 13:35:36 +07:00
  • 882325b58a docs: คู่มือการใช้ N10 Documents API สำหรับทีม backend e-Court (docs/API-DOCUMENTS.md) — เอกสารส่งต่อผู้เรียกใหม่ แยกจาก docs/PLAN-API.md ที่เป็นสเปก/เหตุผลการออกแบบภายใน: เนื้อหาเขียนจากโค้ดจริงทุก field (route + lib/external-api.ts + lib/external-api-auth.ts + db/schema.ts DTO) — (1) ภาพรวม 3 แบบการถาม (fileId เดียว / typeId ทั้งกลุ่ม / ทั้งคู่ = AND) + URL เต็มใต้ basePath /docedit/note + เหตุผลใช้ POST (ถามด้วย JSON body — GET ที่แนบ body ไม่มี defined semantics ตาม RFC 9110 proxy/gateway อาจตัดทิ้ง) (2) auth ผ่าน header x-api-key: ข้อความ 401 ซ้ำกันกัน enumerate + key เชื่อถือระดับ backend เท่านั้น (เห็นตำแหน่ง REDACTION) (3) request body ตาราง field ระดับบนสุด + filter ทั้ง 3 มิติ (type/userId/annotationTypes) พร้อมกติกา trim ค่าว่าง = ไม่ได้ส่ง + strict validation 400 กัน typo (4) response 200: ตาราง field เอกสาร/annotation/bookmark/reply + data ตามชนิด annotation ทั้ง 7 (พิกัด % ของหน้า) + ลำดับรายการ (annotations pageIndex+createdAt / bookmarks N5 / replies createdAt / documents files.createdAt) + ความหมาย url deep link เต็ม (?ann= / ?page= 1-based) (5) ตาราง error ทุก status พร้อมข้อความไทยตรงตามที่โค้ดตอบจริง + การแยก 'ไม่มีข้อมูล' (200 + array ว่าง) จาก 'ผิดพลาด' (6) ตัวอย่าง curl 4 แบบ พร้อม response ตัวอย่างเต็มรูป annotation+reply+bookmark (7) ข้อควรรู้: เอกสารต้องเคยเปิดผ่านลิงก์ e-Court ก่อน, pageIndex 0-based vs ?page= 1-based, reverse proxy ต้องส่ง x-forwarded-proto/host ให้ถูกไม่งั้น url ชี้ origin ผิด, ไม่มี pagination/write API — README แก้จุดเดียว: หัวข้อ N10 ชี้มาที่คู่มือ โดยคงลิงก์ PLAN-API.md เป็นสเปก (คำขอผู้ใช้ 2026-09-25) Your Name 2026-09-25 13:19:43 +07:00
  • f4d46c7720 external-api: N10 API ดึงข้อมูลให้ระบบภายนอกด้วย API Key + ถอด F16 export CSV / N7 bookmark JSON แทนด้วย N10 — (1) endpoint เดียว POST /api/external/documents (JSON body {fileId และ/หรือ typeId — ส่งทั้งคู่ = AND เหลือเอกสารเดียว, filter:{type bookmarks|annotations, userId string|array, annotationTypes ค่า enum}}) ให้ backend e-Court เรียก server-to-server ผ่าน header x-api-key คืน {documents:[…]} รูปเดียวเสมอ — bundle ยุบชั้น field เอกสาร + annotations (พร้อม url deep link + replies) + bookmarks ระดับเดียวกัน, url เป็น URL เต็ม origin จาก request ผู้เรียก (x-forwarded-proto/host fallback host ตรง) + basePath (เดิม 2 GET query-param รวมเป็นเส้นเดียว 2026-09-24 · OR→AND + url relative→เต็ม + filter.types→annotationTypes กันสับสนกับ filter.type 2026-09-25) (2) lib/external-api-auth.ts — requireExternalApiKey เทียบ SHA-256 + crypto.timingSafeEqual, env EXTERNAL_API_KEY ไม่ตั้ง/ว่าง = 401 fail closed, ข้อความ 401 เดียวกันกัน enumerate + externalApiError คืน JSON {"error": ไทย} ใช้ร่วมทุก route — module DB-free ให้ tests/lib/external-api-auth.test.ts (5 tests) รันได้ไม่ต้องมี DATABASE_URL (3) lib/external-api.ts parseQueryBody strict กัน typo เงียบ ๆ (field นอก 3 ตัว = 400, annotationTypes ค่านอก enum = 400 บอกค่าที่รับ) + loadDocumentBundles bulk inArray = 3 query ต่อ request ไม่ว่ากลุ่มกี่ไฟล์ กรอง userIds/annotationTypes ใน SQL เรียง F15/N5 (4) route: auth ก่อนแตะ DB + ครอบ try/catch ทุก DB call ล้ม = log ฝั่ง server + 500 JSON ตาม contract ไม่ปล่อยเป็น 500 ของ framework, read-only ไม่ publish SSE ไม่ revalidatePath ไม่มี CORS, REDACTION รวมในผลลัพธ์ (5) ถอด F16/N7 ตามคำขอผู้ใช้ 2026-09-25 (N10 ครอบคลุมข้อมูลชุดเดียวกันพร้อม auth): ลบ app/api/annotations/export-csv + app/api/bookmarks/export + utils/csv.ts + tests/utils/csv.test.ts + ปุ่มส่งออก 2 ปุ่มบนหน้าสรุป + ปุ่ม JSON ใน sidebar + รูป requirement 1.png/2.png (บรรทัดอ้างถึงใน gap-analysis.md เปลี่ยนเป็น historical note) (6) docs sync ครบ: PLAN-API.md แผนฉบับเต็ม 9 รอบ + PLAN-FEAT.md §7 N10 + PLAN-BOOKMARK-PDF.md/gap-analysis.md 5.3.14 เปลี่ยนตอบด้วย N10 + CLAUDE.md + README (env/ตัวอย่าง curl/troubleshooting) + .env.example + .claude/rules/data-layer.md — ผ่าน code review 2 รอบ 2026-09-25: แก้ 500 JSON ตอน DB ล้ม + ถอด fan-out ต่อไฟล์เป็น bulk + ถอด as cast แทน type guard (tsc + vitest 149/149) (คำขอผู้ใช้ 2026-09-25) Your Name 2026-09-25 12:02:42 +07:00
  • df502088b7 groups+summary: redesign หน้า /groups/[typeId] + หน้าสรุปเป็น explorer 2 คอลัมน์ (ตัวกรองซ้าย + list เดียว) — หน้าสรุปใช้มิติแรก "หน้า" ตามแบบหน้ากลุ่มที่ใช้ "ไฟล์" — (1) หน้า /groups/[typeId]: ถอด navbar (เหลือ back link — AppNav เหลือใช้ที่หน้า / อย่างเดียว) + ถอดหมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้", query ทั้งหมดฝั่ง server ส่ง GroupFileSummary[]+GroupListItem[] ให้ GroupExplorer กรองใน memory — ตัวกรอง tree menu 2 ระดับสไตล์ Quasar docs 4 กลุ่ม (ไฟล์/ชนิดรายการ/ชนิดของ Annotation/ผู้บันทึก — ชนิดรายการเป็น checkbox คู่ ไม่เลือก = แสดงทั้งหมด, ติ๊กชนิด → เลือก Annotation อัตโนมัติ, เลิกเลือก Annotation → ล้างชนิดค้าง) + GroupList แถว passive จุดคลิกเดียวคือปุ่ม "View" (2) แยกแผงตัวกรองเป็น component กลาง components/explorer/FacetFilterPanel.tsx — มิติแรกส่งเข้าทาง prop firstGroup (groups = ไฟล์ / สรุป = หน้า พร้อม optionsScroll ให้ scroll ภายในกลุ่ม max-h-64 กันหลายสิบหน้าดันกลุ่มอื่นออกจอ) + TYPE_SHORT/TYPE_ORDER รวมจาก 2 ที่ที่เคย define ซ้ำมาอยู่ utils/annotation-display.ts จุดเดียว (3) หน้าสรุป /annotate/[id]/summary: server ส่ง ExplorerItem[] (หน้า → bookmark ก่อน annotation → ในหน้าเดียวกัน bookmark เรียง yOffset ตาม N5/annotation ตาม createdAt) ให้ SummaryExplorer + SummaryList การ์ดรายหน้าหัวการ์ด sticky (เลขหน้า + บรรทัดสรุปหน้าคำนวณจากรายการที่เห็นจริงหลังกรอง + ปุ่มเปิดหน้านี้) + แถว passive ปุ่ม View deep link ?ann=/?page= เหมือนหน้ากลุ่ม — ถอดรายการตามหน้าจุดไข่ปลา + การ์ด folio + CollapsibleBlock ออก (anchor #page-N ของ section คงไว้ให้ลิงก์เก่ายังใช้ได้) (4) ตอนพิมพ์แสดงฉบับเต็มเสมอ — ตัวกรองที่ตั้งค้างต้องไม่ตัดรายการออกเงียบ ๆ บนกระดาษ (ตอนกรองอยู่ render ชุดบนจอ print:hidden + ชุดเต็ม hidden print:block, prop anchorIds กัน duplicate id) + กฎ print ใหม่ใน globals.css (section>ul>li break-inside:avoid-page + .page-head position:static) (5) ผ่าน code review 2026-09-24 (tsc/build/vitest 144 tests): คืน N5 yOffset ให้ bookmark ต่อหน้าที่ sort createdAt ทับไปโดยไม่ตั้งใจ + จดรับความเสี่ยง localeCompare "th" รันทั้ง SSR/hydration ไว้ที่ comment พร้อมทางแก้ + docs sync ครบ (CLAUDE.md โครงสร้างไฟล์ + PLAN-FEAT ถอดหมายเหตุ + PLAN-DOCSET อ้าง FacetFilterPanel) (คำขอผู้ใช้ 2026-09-24) Your Name 2026-09-24 17:34:23 +07:00
  • 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) Your Name 2026-09-24 11:46:02 +07:00
  • 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) Your Name 2026-09-23 11:06:44 +07:00
  • 329ae8e956 ecourt+groups: N8 เปิดเอกสารจากลิงก์ ?fileUrl=&fileId=&typeId= (metadata-only + allowlist + ดึงไฟล์สด + login วนกลับ + การ์ดจำลองลิงก์ + path dev /samples) + N9 กลุ่มเอกสารตาม typeId — หน้ารายการจัด section ต่อกลุ่ม + "ไม่มีกลุ่ม" ท้ายสุด + หน้า /groups/[typeId] พร้อมหมายเหตุ "อาจไม่ครบทุกฉบับ" + docs sync ครบ (2026-09-23) Your Name 2026-09-23 10:44:14 +07:00
  • 0aef3ff028 export+sidebar: กรองตามรายคนที่เลือกดูครบทั้งจอและ export + REDACTION คงไว้เสมอ (คำขอผู้ใช้ 2026-09-22) Your Name 2026-09-22 15:34:37 +07:00
  • 9ba8f0a8ec viewer: ซ่อน tool มาร์กข้อความเมื่อไฟล์ไม่มีข้อความให้คลุม — ทั้งไฟล์รูปภาพ (N3) และ PDF สแกนล้วน (คำขอผู้ใช้ 2026-09-22) Your Name 2026-09-22 15:25:03 +07:00
  • 3a06557bd4 sidebar: แก้ไอคอน bookmark root ตรงแนวกับ sub บนจอสัมผัส — chevron ต้นไม้คง 14px + ขยาย hit area ผ่าน ::after (bug report 2026-09-22) Your Name 2026-09-22 11:49:08 +07:00
  • 357f3e972f toolbar: แก้ quick wins ตาม usability audit 2026-09-22 — ปิดเมนูหลังเลือกแถวเดี่ยว + แบนเนอร์ hint ยางลบ/tool วางของ + ปุ่ม % ซูมรีเซ็ต fit-width + ชื่อ tool "เลือกข้อความ" เป็น "เลือก" Your Name 2026-09-22 11:48:53 +07:00
  • f7cd4b8312 viewer: เปลี่ยนตัวเลื่อนของหน้า annotate จาก window มาเป็น <main> ของเอกสาร — zoom เกินจอแล้วเลื่อนได้ทั้งสองแกน sidebar/header อยู่กับที่ (คำขอผู้ใช้ 2026-09-22: เดิมเลื่อน/ซูมทีเดียวเป็นทั้งหน้าเว็บรวม sidebar ไปด้วย) Your Name 2026-09-22 09:57:55 +07:00
  • f7b5a8488d toolbar: ใส่คำอธิบายไทยหลัง icon ทุกปุ่มของ header หน้า annotate (คำขอผู้ใช้ 2026-09-21) v1.0.17 Your Name 2026-09-21 12:03:16 +07:00
  • e713bcc5e3 summary: เปลี่ยนคำว่า "สารบัญ" เป็น "รายการตามหน้า" ทั้งหน้าสรุป — ตามคำขอผู้ใช้ 2026-09-18 (คำเดิมสื่อว่าเป็นสารบัญครบทุกหน้าของเอกสาร แต่บล็อกนี้แสดงเฉพาะหน้าที่มี annotation/bookmark คำใหม่ตรงเนื้อหาจริงกว่า) แก้ให้เป็นชื่อเดียวกันทุกจุด: หัวข้อ desktop (h2), ป้ายมือถือ <details> ("รายการตามหน้าและผู้ร่วมทำ"), aria-label ของ <nav> ทั้ง 2 จุด (ตัดส่วน "หน้าที่มีรายการ" ทิ้ง — คำใหม่สื่อความหมายนั้นอยู่แล้วและ link แต่ละรายการมี aria-label "หน้า N, X รายการ" ของตัวเอง) + sync comment ที่เรียกบล็อกนี้ (page.tsx 6 จุด, globals.css 1 จุด) และคำอธิบายหน้าใน CLAUDE.md (3 จุด) — เหลือคำ "สารบัญ" จุดเดียวโดยเจตนาใน comment page.tsx ("จุดไข่ปลาแบบสารบัญของเอกสารราชการ") เพราะอ้างถึงรูปแบบภาพ dot leader ไม่ใช่ชื่อบล็อก — เป็นการแก้ string/comment ล้วน ไม่แต่ตรรกะ (grep ยืนยันไม่มี test/e2e ผูกข้อความนี้) v1.0.16 Your Name 2026-09-18 12:09:43 +07:00
  • f9114b3f00 summary: หน้าสรุป design "แฟ้มสำนวน" + แก้ตาม design review 2026-09-17 — การ์ดรายหน้า 1 หน้า 1 การ์ด + สารบัญมือถือ + ghost buttons v1.0.15 Your Name 2026-09-17 19:48:58 +07:00
  • b1bc65e65d sidebar: แท็บหลักกลับไปใช้ label ภาษาอังกฤษเหมือนเดิม — Bookmark (N) / Annotation (N) แทน ที่คั่นหน้า/หมายเหตุ (คงตัวเลขนับและ key ของ tab เดิม ไม่แต่ตรรกะอื่น) Your Name 2026-09-17 17:42:23 +07:00
  • c56c39f2c5 CD: ย่อ description ของ input version ใน uat.yml ให้สั้นลง — "เวอร์ชันที่จะ deploy เช่น 1.2.3 (ว่าง = latest)" Your Name 2026-09-17 17:16:30 +07:00
  • 025ca2ce48 CD: uat.yml แก้ Run workflow จาก UI ไม่ได้บน Forgejo 11 — อาการ: กด Run workflow แล้วไม่มีช่องกรอก version แต่ backend ขวางด้วย "Require value for input" (Forgejo 11 base Gitea 1.22 = UI ยังไม่ render workflow_dispatch input fields, Gitea เพิ่มใน 1.23-1.24) — แก้ 2 ชั้น: (1) input เติม type: string ตาม GitHub spec เผื่อ renderer ต้องเจอ type ถึงวาดช่อง (2) required: false + step Resolve รองรับค่าว่าง: dispatch โดยไม่ใส่ version = deploy :latest (image ล่าสุดที่ build.yml push ตอน tag v* — คือเวอร์ชันที่เพิ่ง deploy Test ไป) ใส่ค่ามา (1.2.3/v1.2.3/deploy1.2.3) ก็ map เป็น v1.2.3 เหมือนเดิม + เขียน if/else แยก event แทน &&/|| เพราะ input ว่างจะ short-circuit ตกไปใช้ ref_name (ชื่อ branch) เกิดเป็น image vdev — verify: js-yaml ผ่าน + ทดสอบ resolve ครบ (deploy1.2.3/v1.2.3/1.2.3 → v1.2.3, ว่าง → latest) Your Name 2026-09-17 17:15:24 +07:00
  • 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 ผ่าน v1.0.14 Your Name 2026-09-17 17:02:31 +07:00
  • 8ba911a119 CD: uat.yml ไม่ build image เอง reuse ของ build.yml + ขึ้น UAT ผ่าน Forgejo UI ได้ — ลบ job build ทั้ง job (เดิม build ซ้ำทุกครั้ง) แทนด้วย steps ใหม่ใน deploy job: Resolve image tag map deploy1.2.3/1.2.3/v1.2.3 → v1.2.3 เสมอ (ตัด prefix deploy ตัด v เติม v กลับ — ใช้ image เดียวกับที่ build.yml push ตอน tag v*) + login registry + docker manifest inspect เช็ค image มีจริงก่อน SSH (fail-fast พร้อมข้อความบอกให้ push tag v* ก่อน ถ้ายังไม่มี) + workflow_dispatch เพิ่ม input version (required) — กด Run workflow ใน UI ใส่เวอร์ชันได้เลยไม่ต้องสร้าง tag deploy* (ทางเดิมยังใช้ได้) — deploy job เปิด if ให้รันกรณ้ dispatch (always() && !failure() && !cancelled() — ผ่านเมื่อ validate-schema success หรือ skip แต่ยัง block ถ้า fail เหมือนเดิม) + notify ถอด needs.build ที่ลบไป + field Tag โชว์เวอร์ชันจาก input ตอน dispatch แทนชื่อ branch + field Image Tag ดึงจาก output ของ deploy job — กติกาคงเดิม: image เกิดจาก build.yml ตอน push tag v* (deploy Test ไปด้วย) เท่านั้น UAT แค่ reuse — verify: js-yaml parse ผ่าน + ทดสอบ normalize ครบ 3 รูปแบบ (deploy1.2.3/v1.2.3/1.2.3 → v1.2.3) Your Name 2026-09-17 16:56:38 +07:00
  • 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 v1.0.13 deploy1.0.2 Your Name 2026-09-17 16:06:20 +07:00
  • 34c3cf2a8f fix deploy1.0.1 Your Name 2026-09-17 14:22:56 +07:00
  • 2f260e0f3b CD: เพิ่ม workflow uat.yml deploy เครื่อง UAT — mirror build.yml ทุกอย่าง เปลี่ยนตัวเดียว: DEPLOY_HOST อ่านจาก vars.SSH_UAT_DEPLOY_HOST ใหม่ (ตั้งเพิ่มใน Forgejo ก่อนใช้ ไม่งั้น deploy job SSH ไป string ว่าง) — trigger tag v*.*.* เดิม (push tag เดียว deploy Test + UAT พร้อมกัน), registry/image เดิม push :v* และ :latest ซ้ำกัน (commit เดียวกัน เนื้อหาเหมือน ไม่ชนกัน), SSH user/port/password ชุดเดิม, script ~/repo/replace-env.sh + deploy.sh เดิม (เครื่อง UAT ต้องมีไฟล์สองตัวนี้ + รับ credentials เดิม), Discord webhook/Web URL ตัวเดิม — เปลี่ยนเฉพาะข้อความ workflow/job/notification เป็น "UAT" ให้แยก notification สองเครื่องได้ Your Name 2026-09-17 14:06:19 +07:00
  • 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 ก่อน) v1.0.12 Your Name 2026-09-17 13:43:16 +07:00
  • 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 ตามไปด้วย Your Name 2026-09-17 13:43:00 +07:00
  • 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) Your Name 2026-09-17 13:41:02 +07:00
  • 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/ลบติดกันคั่นเส้นแนวตั้ง v1.0.11 Your Name 2026-09-17 10:09:57 +07:00
  • bc8ba83deb หน้ารายการ: แยกฟอร์มอัปโหลดเป็น section "เพิ่มเอกสาร" ของตัวเอง (เดิมอยู่ใต้หัว "เอกสาร" ดูเหมือนส่วนเดียวกับรายการ) + บอกชนิดไฟล์ที่รองรับใต้ฟอร์ม (PDF, JPG, PNG, TIFF — ไม่เกิน 100MB ต่อไฟล์) + การ์ดไฟล์กลับเป็นปุ่ม "Annotate" แทนลิงก์กางเต็มการ์ด (ย้อน M4 ตาม feedback) เป้าจิ้ม 36px + focus ring Your Name 2026-09-16 16:52:08 +07:00
  • 1ecb8efcea NumberCombo (ความหนา/ความทึบ/ขนาด) เป็น combobox เต็มความกว้าง — dropdown preset เปิด "ขึ้นบน" (แถวท้ายเมนูไม่ล้นเขต scroll ของเมนูอีก — L2 แก้ตาม feedback ที่ต้องเป็น dropdown + กรอกเพิ่มได้) พิมพ์ค่าเอง/▲▼ ได้ตามเดิม + แถวสีปรับใหม่: วงกลม 12 วง (เพิ่มสีน้ำตาล #b45309) grid 4 คอลัมน์ 3 แถว ช่อง 32px เอา label "สี" ออกให้ตารางเต็มแถว + เส้นคั่น slate-100 ระหว่างแถวตั้งค่าทุกแถว (.panel-row + .panel-row — เทียบเท่าแทรก div my-1.5 border-t) Your Name 2026-09-16 16:43:17 +07:00
  • 33c7002ffd chore: gitignore docker-compose.test.yml (compose ทดสอบ local — ไม่เข้า repo) Your Name 2026-09-16 16:42:19 +07:00
  • a4cac15e42 fix(deploy): อัปโหลด/เปิดไฟล์ใน Docker ไม่ได้ — 3 จุดรวมกัน Your Name 2026-09-16 15:57:06 +07:00
  • 21b9958600 UX audit ทั้งแอป + typography audit 2026-09-16: toast แจ้ง error การบันทึก (H1 — เดิมรายการหายเงียบ + ReplyBox เก็บ draft ตอน fail), คำเตือน redaction แสดงทุกขนาดจอ (H2), toolbar แสดงชื่อ tool active + aria-pressed บนจอสัมผัส (H3/M9), ปุ่ม popover จิ้มง่าย 36px + focus ring เติมครบ (H4) + M1-M9 (ปิด sidebar ตอน navigate, bookmark หัวแถวจอเล็ก, hint โหมดมาร์กบนจอสัมผัส, การ์ดหน้ารายการคลิกได้ทั้งใบ, เช็ค 100MB ฝั่ง client, feedback ✓/pending ตอนบันทึก bookmark, dropdown วางที่ sort ตามหน้า+เลขหน้า, slate-500 แทน slate-400) + webfont Sarabun ผ่าน next/font self-host 400/600 (Option B — แสดงผลตรงกันทุกอุปกรณ์ รวม TEXTBOX ที่ผูก stack นี้) + NumberCombo preset เป็น chips แทน dropdown ล้นเมนู (L2) + leading-relaxed/13px ข้อความไทยที่ wrap + tabular-nums + prefers-reduced-motion (L7) + L1/L4/L5 + แถว bookmark แสดงชื่อผู้บันทึกเหมือนผลการค้นหา Your Name 2026-09-16 14:28:53 +07:00
  • 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 Your Name 2026-09-16 13:24:33 +07:00
  • 79b38fa152 fix: selection preview not appearing when dragging with pen v1.0.10 Your Name 2026-09-15 17:35:13 +07:00
  • b33af7d7f2 fix(debug): DebugConsole v1.0.9 DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-15 16:18:53 +07:00
  • 5c8e52bd62 ไฮไลต์เสร็จแล้ว popup ซ้อนกันอีก 2 รูปแบบ (tooltip ทับแผงบน desktop + เมนู tool ค้างเปิดบน iOS) + งาน TEMP-DEBUG ของปัญหา pencil ลากไม่ติดบน iPad: v1.0.8 DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-15 15:10:27 +07:00
  • 9cc38835f0 popup ซ้อนกัน/ไม่ปิดตอนกดปุ่ม tool + แท็บที่คั่นหน้ากลับไม่ได้ + งานค้างใน working tree: ไฟล์รูปภาพ annotate ได้ (N3) + อัปโหลดไฟล์ (N4): v1.0.7 DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-15 14:12:51 +07:00
  • 0c4af09e85 จอสัมผัส (iOS/Android): ไฮไลต์ด้วย custom selection ลาก→ปล่อย=สร้างทันที แทน native selection + ปุ่มยืนยันลอย (code review ครบ): DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-15 12:13:05 +07:00
  • df0392ea45 จอสัมผัส (iOS/Android): แก้ครบตาม UX audit C1+M1–M6 + code review + งาน export PDF ที่ค้างใน working tree: v1.0.6 DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-14 18:04:40 +07:00
  • b3b884c736 จอสัมผัส (iOS/Android): ไฮไลต์ commit ผ่านปุ่ม "ไฮไลต์" ลอยใต้ selection + ลาก handle กรอบ selection ได้จริง: (1) บน touch การลากคลุมข้อความไม่ยิง synthetic mouseup (ยิงเฉพาะ tap) ทำให้ HIGHLIGHT ไม่ถูกสร้างเลย — เพิ่มปุ่ม "ไฮไลต์" ลอยใต้บรรทัดสุดท้ายของ selection (แพทเทิร์น Acrobat mobile; ด้านบนถูกเมนู/handles ของ OS ยึด ใกล้ขอบล่างหน้าเกินพลิกขึ้นเหนือ rect แรก) แสดงเฉพาะ pointer:coarse + tool highlight เมื่อ selection หยุดนิ่ง ~400ms (debounce ใน effect selectionchange ข้ามช่วงลาก handles) (2) แตะปุ่ม commit จาก snapshot ใน touchSelRef ไม่ใช่ selection สด เพราะ iOS เคลียร์ selection ทันทีที่แตะปุ่มก่อน tap จะถึง — การซ่อนปุ่ม+ล้าง snapshot จึงหน่วง 250ms (scheduleTouchHide) ให้ tap นั้น commit ได้ก่อน และผูก commit ที่ pointerdown (เร็วกว่า click); desktop ยังใช้ mouseup ตามเดิม — สอง path ผ่าน commitHighlight ชุดเดียวกัน + ตั้ง lastSelectionTextRef (ชื่อ bookmark default) ใน debounce เหมือน mouseup (3) เติม touch-none ที่ handle ทั้ง 8 ของ AnnotationSelectionLayer (ขอบ 4 + มุม 4 — InkLayer มีอยู่แล้ว) ไม่งั้น browser ยึด gesture เป็น scroll ส่ง pointercancel กลายเป็นเลื่อนหน้าแทนการลากย้าย/ปรับขนาด (4) sync rules/pdf-viewer.md — verify ด้วย Playwright touch emulation: ปุ่มโผล่หลัง debounce, แตะแล้วสร้าง HIGHLIGHT จริง (server action + DOM + sidebar), วาดเส้นด้วยนิ้วได้, handles 9 จุด touch-action:none ครบ, tsc ผ่าน v1.0.5 DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-14 13:07:08 +07:00
  • 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 ให้ตรงพฤติกรรมใหม่ DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-14 11:35:05 +07:00
  • 4934b9d0b3 eraser ลบตรายาง (STAMP) ของตัวเองได้: hit-test ใน InkLayer.eraseAt ด้วย bbox จาก annotationBBox (สูตรเดียวกับ render) เช็คตราก่อนเส้น/รูปทรงเพราะตราอยู่ layer บน — ตอน tool erase ค่า TextboxStampLayer ปิด pointer-events อยู่แล้วจึงไม่ต้องแตะ layer stack DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-14 09:59:01 +07:00
  • 3026e30b34 icon ปุ่ม header อยู่กึ่งกลางบนจอสัมผัส: touch-44 บังคับปุ่ม 44px แต่ปุ่ม icon เดี่ยวไม่มี flex center ทำให้ icon ชิดซ้าย (แก้ที่ media query เดิมจุดเดียว) DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-14 09:57:43 +07:00
  • 1c2f4c834a sidebar จอกลางเป็นคอลัมน์ + bookmark row hover แยกแถว + PDF fit จอเล็ก v1.0.4 DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-11 18:05:38 +07:00
  • b588343d50 bookmark: pre-fill ชื่อ default "หน้า N" + focus เลือกข้อความทั้งหมด v1.0.3 DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-11 17:45:31 +07:00
  • 9e5083dae1 คลุมข้อความได้ทุก tool ที่ไม่ได้วาด: comment/textbox/stamp ลากคลุมได้ selection สีฟ้าเหมือน tool เลือกข้อความ (ปล่อยเมาส์จากการลากไม่วางรายการ) DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-11 17:41:58 +07:00
  • ca74fb1843 TEXTBOX กล่องกว้างคงที่สไตล์ Smallpdf: คลิกเดียวได้กล่อง 30% + ลากขอบซ้าย/ขวาปรับความกว้าง (ฟอนต์คงขนาด ข้อความ wrap ใหม่) DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-11 17:41:22 +07:00
  • 9e6158ed45 ปรับข้อความคำเตือน tool ปิดพราง (redact) ให้อ่านเข้าใจง่าย v1.0.2 DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-11 14:12:30 +07:00
  • 0c36b516fa NumberCombo: commit อัตโนมัติหยุดพิมพ์ 0.5 วิ + ความหนาเส้นไม่จำกัดขอบบน DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-11 14:04:58 +07:00
  • fbc78b78e9 เปลี่ยนชื่อระบบ Jude's Note → Judge's Note DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-11 13:37:31 +07:00
  • a066cb395d ปรับข้อความคำใบ้เส้นตรงใน dropdown ปุ่มวาดให้อ่านเข้าใจง่าย DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-11 13:37:11 +07:00
  • 6fd3a1515f ซ่อม text ไทย PDF (ActualText) + revalidatePath ทุก action + คีย์ลัด DEL + ตรายาง label บรรทัดเดียว DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-11 13:28:35 +07:00
  • 289d735bf3 เพิ่มตัวเลือก mock user 10 คนที่หน้าแรก + ปรับหน้าแรก + select ลูกศรเองทั่วแอป v1.0.1 DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-10 18:02:01 +07:00
  • 79c9e2cc41 Seed ไฟล์ตัวอย่างแบบ idempotent sync กับ public/samples/ DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-10 18:01:25 +07:00
  • ec26d79f0e Fix migrate ตอน start: standalone node_modules ไม่มี package postgres v1.0.0 DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-10 16:30:01 +07:00
  • 60658568f7 Auto-migrate ตอน container start (แทนการ push มือ) DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-10 16:25:14 +07:00
  • f6f4ed9129 CD: build/deploy จาก branch dev แทน main DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-10 15:58:27 +07:00
  • 83108dd16c Add Forgejo CD workflow + Docker image build main DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-10 15:43:17 +07:00
  • eddcec28c7 Add application codebase: PDF annotation for e-Court DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-10 14:29:40 +07:00
  • 6fd0c3174f first commit DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-10 14:21:21 +07:00