TEXTBOX กล่องกว้างคงที่สไตล์ Smallpdf: คลิกเดียวได้กล่อง 30% + ลากขอบซ้าย/ขวาปรับความกว้าง (ฟอนต์คงขนาด ข้อความ wrap ใหม่)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
9e6158ed45
commit
ca74fb1843
10 changed files with 248 additions and 41 deletions
|
|
@ -7,7 +7,7 @@ paths:
|
|||
# Data Layer Rules (รายละเอียด: docs/PLAN.md §3, §4)
|
||||
|
||||
- โครงสร้างตาราง/enum/JSONB union อ้าง §3 — หลังแก้ `db/schema.ts` ต้องรัน `/sync-db`
|
||||
- Enum/union ขยายตาม PLAN-FEAT.md §1 ทีละ task — ตอนนี้เพิ่มแล้ว: `SHAPE` + `ShapeData` (rect/ellipse เก็บ `rect` bbox, line/arrow เก็บ `from`/`to` ตาม D3), `REDACTION` + `RedactionData` (`{ rect }` — ดำทึบ ไม่มีสีให้เปลี่ยน), `TEXTBOX` + `TextboxData` (F17 — `{ position, text, color, fontSize, bold?, opacity?, align?, segments? }` position เป็นมุมซ้ายบน, fontSize = % ของ width; bold/opacity(0–1)/align('left'|'center'|'right') เป็น optional ไม่มี = ตัวปกติ/ทึบเต็ม/ชิดซ้าย — bold ตอน export ต้องมี `public/fonts/Sarabun-Bold.ttf` ไม่งั้น fallback เป็น Regular; `segments?` = ตัวหนาเฉพาะส่วน (rich text — editor เป็น contentEditable + Ctrl/Cmd+B, เก็บเฉพาะเมื่อมี bold run) โดย field `text` เก็บข้อความรวม (concat) เสมอเพื่อค้นหา F14/preview sidebar และ export ใช้ `wrapStyledText` วาดแบบ run ผสม), `STAMP` + `StampData` (F18 — `{ label, position, scale, color? }` scale = ความกว้างกรอบเป็น % ของ width, color optional ไม่มี = `STAMP_COLOR`), `DrawData.widths?` (F7 pressure, optional ยาวเท่า paths; `Point` define ที่ `db/schema.ts` แล้ว re-export จาก `utils/coordinates.ts`), `DrawData.pen?` ('pencil' | 'highlighter' — optional ไม่มี = pencil; ค่า preset ต่อ pen อยู่ที่ `DRAW_PENS` ใน `utils/annotation-display.ts`), `DrawData.opacity?` (0–1 optional — เส้นใหม่เก็บชัดเสมอ แถวเดิม fallback ตาม preset ของ pen ผ่าน `effectiveDrawOpacity`), `HighlightData.style?` ('highlight' | 'underline' | 'strikethrough' | 'squiggle' — optional ไม่มี = ไฮไลต์ทึบตามเดิม; เลือกจาก popup หลังคลุมดำ), `HighlightData.opacity?` (0–1 optional ไม่มี = ทึบเต็ม — ตั้งจาก dropdown ของ tool highlight ตอนสร้าง + แผง selection แก้ได้หลังสร้าง; export: ไฮไลต์ทึบคูณเข้าค่าเดิม 0.35 ส่วนเส้นทุกแบบใช้ตรง ๆ) — เพิ่ม type ใหม่ทุกครั้งต้องเติม case ใน `validateAnnotationData` ครบถ้วน (invariant #8)
|
||||
- Enum/union ขยายตาม PLAN-FEAT.md §1 ทีละ task — ตอนนี้เพิ่มแล้ว: `SHAPE` + `ShapeData` (rect/ellipse เก็บ `rect` bbox, line/arrow เก็บ `from`/`to` ตาม D3), `REDACTION` + `RedactionData` (`{ rect }` — ดำทึบ ไม่มีสีให้เปลี่ยน), `TEXTBOX` + `TextboxData` (F17 — `{ position, text, color, fontSize, bold?, opacity?, align?, segments?, width? }` position เป็น**มุมซ้ายบนของกล่องเสมอ** (align จัดข้อความในกล่องอย่างเดียวแบบ Smallpdf — กล่องไม่ขยับ), fontSize = % ของ width; bold/opacity(0–1)/align('left'|'center'|'right')/width (กล่องกว้างคงที่ % ของ width — สไตล์ Smallpdf: มี = กล่องกว้างคงที่ลากขอบปรับได้, ไม่มี = shrink-to-fit แถวเดิม) เป็น optional ไม่มี = ตัวปกติ/ทึบเต็ม/ชิดซ้าย/หุ้มข้อความ — bold ตอน export ต้องมี `public/fonts/Sarabun-Bold.ttf` ไม่งั้น fallback เป็น Regular; `segments?` = ตัวหนาเฉพาะส่วน (rich text — editor เป็น contentEditable + Ctrl/Cmd+B, เก็บเฉพาะเมื่อมี bold run) โดย field `text` เก็บข้อความรวม (concat) เสมอเพื่อค้นหา F14/preview sidebar และ export ใช้ `wrapStyledText` วาดแบบ run ผสม), `STAMP` + `StampData` (F18 — `{ label, position, scale, color? }` scale = ความกว้างกรอบเป็น % ของ width, color optional ไม่มี = `STAMP_COLOR`), `DrawData.widths?` (F7 pressure, optional ยาวเท่า paths; `Point` define ที่ `db/schema.ts` แล้ว re-export จาก `utils/coordinates.ts`), `DrawData.pen?` ('pencil' | 'highlighter' — optional ไม่มี = pencil; ค่า preset ต่อ pen อยู่ที่ `DRAW_PENS` ใน `utils/annotation-display.ts`), `DrawData.opacity?` (0–1 optional — เส้นใหม่เก็บชัดเสมอ แถวเดิม fallback ตาม preset ของ pen ผ่าน `effectiveDrawOpacity`), `HighlightData.style?` ('highlight' | 'underline' | 'strikethrough' | 'squiggle' — optional ไม่มี = ไฮไลต์ทึบตามเดิม; เลือกจาก popup หลังคลุมดำ), `HighlightData.opacity?` (0–1 optional ไม่มี = ทึบเต็ม — ตั้งจาก dropdown ของ tool highlight ตอนสร้าง + แผง selection แก้ได้หลังสร้าง; export: ไฮไลต์ทึบคูณเข้าค่าเดิม 0.35 ส่วนเส้นทุกแบบใช้ตรง ๆ) — เพิ่ม type ใหม่ทุกครั้งต้องเติม case ใน `validateAnnotationData` ครบถ้วน (invariant #8)
|
||||
- column `data` ใช้ `$type<AnnotationData>()` (discriminated union ตาม enum `type`) — ห้าม `any` หรือ object type กว้าง — `note?: string` (โน้ตคำอธิบายของรายการ เช่น เหตุผลที่วาด — PLAN-FEAT.md §7 N1) มีในทุกชนิด**ยกเว้น COMMENT** (มี `message` ทำหน้าที่นี้อยู่แล้ว) — validator `vNote` trim แล้ว ค่าว่าง = ไม่มีโน้ต; อ่านค่าผ่าน `annotationNote()` ใน `utils/annotation-display.ts` เท่านั้น (sidebar/สรุป/CSV ใช้ร่วม)
|
||||
- DTO (`AnnotationDTO`, `BookmarkDTO`): timestamp เป็น **ISO string** เสมอ ผ่าน mapper `toDTO` — ห้ามส่ง `Date` object ข้าม server→client
|
||||
- Server Actions: validate input ก่อนทุกครั้ง (type ถูกต้อง, ฟิลด์ครบ, `pageIndex ≥ 0`) ก่อนแตะ DB; เขียนด้วย `DATABASE_URL` ตรง ๆ + **publish event ผ่าน `lib/annotate-events.ts` หลังเขียนสำเร็จ** + **`revalidatePath` หน้า viewer+summary ของ fileId นั้น** (helper `revalidateFilePages` ในไฟล์เดียวกัน — invalidate Router Cache ฝั่ง client เพราะ `force-dynamic` ปิดแค่ฝั่ง server: ไม่งั้นกด back แล้วเข้าไฟล์ใหม่ได้ initial data เก่าจนต้อง hard refresh; action ใหม่ที่เขียน DB ต้องเรียกทุกครั้งเช่นเดียวกับ publish event)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue