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>
This commit is contained in:
Your Name 2026-09-24 11:46:02 +07:00
parent e254b935ce
commit c5c6b65bd2
42 changed files with 1429 additions and 745 deletions

View file

@ -13,9 +13,37 @@ import {
// ข้อมูลเข้า/ออกเป็นพิกัด % ตาม invariant #2 ยกเว้นฟังก์ชันที่รับ `size`
// เพื่อแปลง % → px (เช่น bbox สำหรับวาดกรอบ selection, hit-test หน่วย px)
/** ขนาดกล่อง bbox ของ comment pin (% ของความกว้างหน้า) — pin เป็นปุ่มขนาดคงที่ ไม่มีพิกัดกว้างยาวใน data */
/** ขนาดกล่อง bbox ของ comment pin (% ของความกว้างหน้า) — pin เป็นปุ่มขนาดคงที่ ไม่มีพิกัดกว้างยาวใน data
* เป็นค่าประมาณ % สำหรับ hit-test ที่ไม่รู้ขนาด render เท่านั้น — ทุกจุดที่รู้ขนาดหน้าจริง
* (กรอบ selection / scroll target / move-clamp) ใช้ commentPinBoxPx/commentPinBoxPct ตาม px จริงข้างล่าง */
export const COMMENT_PIN_BOX_PCT = 4;
/** COMMENT: ขนาด px จริงของปุ่ม pin — desktop 26px (icon 16 + p-1 รอบด้าน + border 1×2)
* และจอสัมผัส 36px (.coarse-36 ใน globals.css บังคับ min-width/height 2.25rem ให้ปุ่ม
* pin ใน PDFViewer) — pin เป็น px คงที่ไม่ scale ตาม zoom ต่างจากชนิดอื่นที่เป็น % ทั้งหมด
* (bug report 2026-09-23: กรอบไม่ตรงกล่อง + responsive audit 2026-09-24: scroll/clamp
* คิดจาก % เดิมจน pin จมใต้ header ตอนซูมออก) — แก้ปุ่ม pin หรือ .coarse-36 ต้องแก้ค่านี้ตาม */
export const COMMENT_PIN_SIZE_PX = 26;
export const COMMENT_PIN_COARSE_SIZE_PX = 36;
/** bbox px ของ COMMENT ตามขนาด render จริง — จัตุรัส px คงที่ กึ่งกลางแนวนอนที่จุด position
* และขอบล่างชนจุด (สูตรเดียวกับ -translate-x-1/2 -translate-y-full ของปุ่ม pin — ทั้งลูก
* ลอยเหนือจุดคลิก) */
export function commentPinBoxPx(d: { position: Point }, size: Size, pinPx: number): Rect {
const p = pointToPx(d.position, size);
return { x: p.x - pinPx / 2, y: p.y - pinPx, width: pinPx, height: pinPx };
}
/** bbox ของ COMMENT หน่วย % คิดจากขนาด render จริง (size = px ของหน้าที่กำลัง render) —
* ใช้กับ scroll target ของ goToAnnotation/deep link + move-clamp/bookmark ของกรอบ selection
* ให้เป็นมาตราฐานเดียวกับที่ render (responsive audit 2026-09-24 — เดิมสองมาตราฐาน:
* render ใช้ px แต่ scroll/clamp ยังใช้ COMMENT_PIN_BOX_PCT ของ annotationBBox) */
export function commentPinBoxPct(d: { position: Point }, size: Size, pinPx: number): Rect {
const wPct = (pinPx / size.width) * 100;
const hPct = (pinPx / size.height) * 100;
return { x: d.position.x - wPct / 2, y: d.position.y - hPct, width: wPct, height: hPct };
}
// TEXTBOX (F17/B2 — §5): ค่าประมาณสำหรับ bbox ของกล่องข้อความ
// render จริงกว้างสุด 50% ของหน้าแล้ว wrap อัตโนมัติ — bbox ประมาณจากตัวอักษร
// กว้าง ~0.6 × fontSize ต่อตัว บรรทัดสูง 1.5 × fontSize (ค่าประมาณพอสำหรับกรอบ selection)