6.6 KiB
UHM Editor - developer guide thực dụng
Tài liệu này dành cho người sửa editor hiện tại, không phải mô tả kiến trúc lý tưởng.
1. Entry points quan trọng
src/app/editor/[id]/page.tsx- orchestration chính của project editor
src/uhm/components/Map.tsx- container cho map và các hook map
src/uhm/lib/editor/state/useEditorState.ts- draft geometry + diff + undo
src/uhm/lib/editor/state/useEditorSessionState.ts- session/UI/project/wiki/entity state
src/uhm/lib/editor/snapshot/editorSnapshot.ts- normalize snapshot từ backend và build snapshot gửi ngược lại backend
Nếu chưa đọc 5 file này, chưa nên sửa behavior lớn của editor.
Docs nên đọc trước khi sửa editor:
src/uhm/doc/editor_operations.mdsrc/uhm/doc/editor_data_roles.mdsrc/uhm/doc/editor_snapshot_contract.mdsrc/uhm/doc/editor_manual_test_checklist.mdsrc/uhm/doc/editor_replay_actions.md
2. Cấu trúc thư mục nên ưu tiên hiểu
src/uhm/components/editor/- panel UI bên trái/phải
src/uhm/components/wiki/- wiki editor và wiki viewer/sidebar
src/uhm/components/map/- hooks tích hợp MapLibre
src/uhm/lib/map/engines/- logic interaction theo mode
src/uhm/lib/editor/session/- các nhóm session state
src/uhm/lib/editor/draft/- draft diff và undo
src/uhm/lib/editor/snapshot/- schema conversion / snapshot semantics
3. Cách editor thật sự vận hành
Editor có 3 tầng dữ liệu:
baselineSnapshot- snapshot gốc của session
baselineFeatureCollectionFeatureCollectionrehydrate từ snapshot đó- seed/reset cho
useEditorState()
mainDraft- working copy để user sửa trên map
Map không render trực tiếp mainDraft mọi lúc. Page tạo mapRenderDraft từ mainDraft/replayDraft/preview draft sau khi áp timeline/replay filter, rồi truyền xuống Map dưới prop renderDraft. labelContextDraft chỉ dùng để lookup label, không được dùng để quyết định geometry nào hiện trên map.
Khi commit:
- geometry đi từ
mainDraft - entity/wiki/link đi từ snapshot collections
buildEditorSnapshot()quyết định operation nào làreference,binding,update,delete
Đừng tự build payload ở component nếu chưa hiểu file editorSnapshot.ts.
4. Khi thêm mode/tool mới
Checklist an toàn:
- Thêm mode vào
sessionTypes.ts. - Thêm button vào
ToolsPanel.tsx. - Nếu mode cần preview source/layer mới, thêm vào
setupMapLayers(). - Nối mode với engine trong
useMapInteraction.ts. - Nếu tool tạo geometry mới, chọn default:
typegeometry_presetentity_idsbound_with
- Kiểm tra interaction cleanup khi chuyển mode.
Nếu mode chưa được cleanup đúng, map rất dễ giữ preview cũ hoặc event listener cũ.
5. Khi thêm geotype mới
Checklist ngắn:
- Cập nhật
geoTypeMapnếu cần mapping backend code <-> key. - Cập nhật
geometryTypeOptions.ts. - Tạo style file trong
styles/geotypes/. - Register ở
geotypeLayers.ts. - Kiểm tra point icon hoặc label pipeline nếu type mới là point/route/polygon label.
Nếu chỉ sửa geometryTypeOptions.ts mà quên style registry, UI sẽ cho chọn type nhưng map không render đúng.
6. Khi sửa snapshot semantics
File quan trọng nhất là editorSnapshot.ts.
Ở đó đang có hai hướng xử lý khác nhau:
normalizeEditorSnapshot(raw)- đọc payload từ backend
- rehydrate fields UI như
entity_ids,entity_name,bound_with,time_start,time_end
buildEditorSnapshot(options)- strip các field generate-only khỏi
editor_feature_collection - build
geometry_entity[]vàentity_wiki[] - tính operation phù hợp
- strip các field generate-only khỏi
Nguyên tắc:
- feature trong editor có thể mang field denormalized để UI dễ dùng
- payload gửi backend thì không nên mang những field denormalized đó
7. Khi sửa wiki editor
Wiki project editor hiện là Quill, không phải Tiptap.
Các file nên đọc trước:
WikiSidebarPanel.tsxPublicWikiSidebar.tsx
Các điểm dễ làm hỏng:
- sanitize link của Quill
- compatibility với doc dạng HTML/plain text
- slug links nội bộ
- sentinel
__missing__
Nếu thay storage format, phải sửa cả editor lẫn viewer compatibility path.
8. Những key localStorage thật sự đang dùng
uhm.backgroundLayerVisibility.v1uhm:mapProjection
Hiện không có local draft autosave toàn editor. Đừng dựa vào doc cũ hoặc giả định rằng F5 sẽ hồi lại draft geometry/wiki/entity.
9. Restore commit hiện là FE-only
CommitHistoryPanel -> Restore:
- load snapshot từ commit cũ
- reset editor state ở frontend
- không đổi head commit trên backend
Nếu muốn restore server-side thật, cần dùng endpoint backend riêng và sửa cả UI wording.
10. Pending submission lock là rule thật
openSectionEditor() chủ động chặn project có PENDING submission.
Nghĩa là:
- không nên "lách" UI để cho sửa tiếp
- nếu đổi behavior này, phải thống nhất với backend contract
11. Performance và state hygiene
Một số nguyên tắc nên giữ:
- dùng
renderDraftRef/refs trong map engines để tránh rebind handler vô ích - giữ component panel càng dumb càng tốt, logic patch state đặt ở page/hooks
- khi cần undo cho entity/wiki/link, đi qua
editor.setSnapshot*()để undo stack biết - khi cần undo cho replay script, đi qua
editor.mutateActiveReplay()hoặc replay collection helper hiện có - hạn chế thêm
JSON.stringifycompare ở chỗ nóng nếu chưa đo hiệu năng
12. Chỗ dễ gây hiểu nhầm khi debug
Geometry biến mất
Có thể do:
- timeline filter
- geometry visibility theo type
- bound_with filter
Không phải lúc nào cũng là bug render layer.
Commit count lạ
Commit (N) là pendingSaveCount, không phải số mutation backend.
Selection mất
Selection hiện bám theo editor.draft, không theo mapRenderDraft. Vì vậy geometry đang chọn có thể bị timeline filter ẩn khỏi map nhưng panel metadata vẫn đọc được draft gốc.
13. Nên test gì sau khi sửa
Ít nhất nên test thủ công:
- mở project có commit cũ
- tạo geometry mới bằng mode liên quan
- sửa metadata geometry
- bind entity và geometry
- tạo/sửa wiki
- link entity-wiki
- commit
- restore từ commit cũ
- mở project có pending submission nếu đang debug flow đó