#history#tabs#hotkeys#dirty-guard
ภาพรวม
TabWorkspace ให้ทุกแท็บ mount ค้าง (แท็บไม่ active แค่ซ่อน) บั๊กตระกูลเดียวกันจึงโผล่ 22-25 ต.ค. 2026: คีย์ลัด window ถูกแท็บที่มองไม่เห็นรับ, คีย์ตั้งค่าอิงแท็บ active ปนกัน, ปุ่มที่ทิ้งข้อมูลโดยไม่ถาม เกิดซ้ำทุกจอใหม่ เพราะแต่ละจอผูก window.addEventListener("keydown") เอง
อาการ
- A คีย์ลัดแท็บซ่อน (KMS T6, M1, M2): F12/Esc/Insert/ลูกศรทำงานในแท็บที่มองไม่เห็น บันทึก/ปิด/เปิดเอกสารใหม่ของแท็บอื่น; กด Esc ปิดกล่องแจ้งเตือนแล้วข้อมูลที่แก้หาย (Esc ลามไปจอหลัก)
- B listKey ชน (M3): สลับแท็บแล้วรายการโหลดคอลัมน์/SQL ที่ตั้งไว้ของจออื่น
- C ทิ้งข้อมูลเงียบ (M6): ปุ่ม
+/Insert/ดินสอ/ดับเบิลคลิก/ปิดแท็บ/ปิดทั้งหมด ทิ้งข้อมูลที่แก้ค้างโดยไม่ถาม - D state ค้างข้ามแท็บ/เอกสาร (T5, T7, M4, M5): StockBeginningBalance หัวเอกสาร B + บรรทัดของ A แล้วบันทึกทับ; ชื่อพนักงานขาย/แผนกเก่าติดเอกสารใหม่; บาร์โค้ด+Enter เร็วเลือกผลค้นหาเก่า; รหัสใหม่แต่ชื่อเก่าค้าง
NEWCODE~ชื่อเดิม
สาเหตุ (root cause)
- A handler ผูก window โดยไม่เช็กว่าแท็บ active;
SmlDialogไม่ใช้ capture - B
SmlDataListใช้tabsCtx.activeKey(แท็บที่ผู้ใช้เปิดอยู่ ไม่ใช่แท็บเจ้าของรายการ) เป็นคีย์เมื่อไม่ได้ส่งlistKey - C ปุ่ม
+/Insert/ดินสอ/ดับเบิลคลิก เรียก handler เพิ่มใหม่/เลือกแถวตรงๆ ไม่มีขั้นถาม (อนุมานจากข้อความ commit7dc1b3a6); ตัวแท็บไม่รู้ว่าจอข้างในแก้ค้าง - D โหลดหลาย
awaitไม่มีลำดับงาน; ผล async ลงตามตำแหน่งแถว (ลบ/ย้ายระหว่างรอ = ลงผิดแถว); Enter มาก่อนผลค้นหาใหม่
วิธีแก้
- A
678fc5fc: F12 เฉพาะแท็บที่เห็น (listHotkeysActive),hotkeysActive={listHotkeysActive && !isEditing};7dc1b3a6:SmlDataListข้ามเมื่อoffsetParent === null,SmlDialogฟัง capture +stopPropagation - B
7dc1b3a6:TabsContext.screenKeyผ่านScreenTabsProvider(memo) ให้รายการใช้ก่อนactiveKey - C
3b55658eปุ่ม "ปิด Tab ทั้งหมด";a422b74fตรวจก่อนปิดแท็บ/ปิดทั้งหมด พร้อม modal เตือนรายชื่อแท็บที่ยังไม่บันทึก;7dc1b3a6/324bd2a6เพิ่มconfirmDiscard()ในจอ master และ snapshot dirty ในUniversalTransScreen(ตามข้อความ commit) - D
678fc5fcsequence guard + ล้างค่าที่รั่ว;324bd2a6row_key;7dc1b3a6Enter รอผลค้นหาใหม่, ค้นชื่อใหม่ตอน blur เมื่อรหัสเปลี่ยน (ตามข้อความ commit) - E
b9c13081(25 ต.ค.): ถอด F1-F12 ทั้งระบบ เหลือ Ctrl+S และปุ่มกด
commit ที่เกี่ยวข้อง
3b55658e(22 ต.ค.) ปิดทุกแท็บ;a422b74f(23 ต.ค.) เช็กค้างก่อนปิด678fc5fc,7dc1b3a6,324bd2a6,6cb904f7(24 ต.ค.) — hotkey แท็บซ่อน, screenKey, confirm ทิ้ง, row_key, สิทธิ์ต่อแท็บb9c13081(25 ต.ค.) — Button-Only
โค้ดปัจจุบันที่ป้องกัน
frontend/account/tabs/TabWorkspace.tsx:95isTabActive+screenKeymemo;:636dirtyInputTabsRef,:643checkTabDirty(สแกนป้าย "แก้ไขแล้ว..." ใน DOM),:705closeAllTabsfrontend/account/master/SmlDataList.tsx:151effectiveListKey,:574ข้ามเมื่อซ่อนfrontend/account/master/SmlDialog.tsx:94-103capture +stopPropagation;frontend/account/master/SmlLookupModal.tsx:206,503enterWhilePendingReffrontend/account/transaction/UniversalTransScreen.tsx:543listHotkeysActive,:2958confirmDiscard,:2980Ctrl+S เฉพาะแท็บ active;frontend/account/transaction/StockBeginningBalanceScreen.tsx:132,1730tabActive;frontend/account/master/StandardMasterScreen.tsx:456confirmDiscard
บทเรียน / กฎที่ได้
- คีย์ลัดระดับ window ต้องเช็ก
tabsApi.isActive(menuKey)และปิดคีย์ลัดรายการขณะแก้ไข (b9c13081เพิ่มไว้ใน AGENTS.md; ปัจจุบัน AGENTS.md:41 "ห้ามใช้ F1-F12") - ป๊อปอัป/dialog ที่ดัก Esc/Enter ต้องใช้ capture +
stopPropagation; คีย์ตั้งค่าต้องมาจากแท็บเจ้าของจอ ไม่ใช่แท็บ active - ทางเข้าที่ทิ้งข้อมูลทุกทาง (ปุ่ม, Insert, ดับเบิลคลิก, ปิดแท็บ, ปิดทั้งหมด) ต้องผ่าน
confirmDiscard; ผล async ต้องผูกกับตัวตนแถว/เลขลำดับงาน - เปราะ:
checkTabDirtyจับ dirty จากข้อความป้ายใน DOM เปลี่ยนป้ายต้องแก้ regex ด้วย
ดูเพิ่ม
พื้นที่ทำงานแบบแท็บ MDI และสิทธิ์จอ (TabWorkspace) · จอข้อมูลหลักมาตรฐานและจอ master-detail (StandardMasterScreen / MasterDetailScreen) · บทเรียนบั๊ก UX/หน้าจอธุรกรรม/Master/แท็บ MDI (Frontend UX Bug Lessons) · บั๊กตาราง/เลย์เอาต์ที่เคยถอยหลัง (Grid & Layout UX Regressions) · บทเรียนระบบสิทธิ์เมนู (Permission & Menu Rights) · history-overview
ที่มาในโค้ด
- git: ทุก SHA ข้างต้น (ตรวจ diff:
7dc1b3a6,678fc5fcบางไฟล์,a422b74f,3b55658e,b9c13081; ส่วนที่เหลือตามข้อความ commit; ยังไม่ได้ตรวจ diff) docs/kms/KMS_SECURITY_DATA_INTEGRITY_LESSONS.md§4-5 (T5-T7, M1-M6)