- Ngôn ngữ: Tiếng Việt là chính (comment, tài liệu, giao diện); i18n phải có cả
en/vàvi/. Code bám sát phong cách file xung quanh. - License: MIT (
LICENSE). - Quy ước đặt tên: frontend
TM*(componentTM*.vue, classTM*.js, storeTM*.js, mixinTM*Mixin.js, directiveTM*.js); backend filetm_*.go+ tiền tố tầngbl_(service),dl_(database),rt_(router). - Không commit artifact: xem Luật 12 trong
AGENTS.md(dist/,out/,node_modules/,config.json,*.db*,src_wasm/pkg/,src_wasm/*/external_repo/). - Commit: nhỏ, 1 ý/commit; subject rõ nghĩa (có thể tiếng Việt ngắn gọn).
- Chưa có CI/test frontend: tự smoke-test kỹ trên
npm run dev; Go chạygo test ./...module đụng đến. - Main thread không được block: xử lý nặng tách worker (xem mục "Tác vụ nặng"); luôn báo tiến trình và để user thao tác được tab khác trong lúc chờ.
- Tạo component
src/views/tools/TM<Thing>.vue(thư mục con riêng nếu tool phức tạp, ví dụAPITesting/,PostgreSQLQuery/). Mẫu:<template> <div class="flex container">…</div> </template> <script> import TMToolBase from "@/views/tools/base/TMToolBase.vue"; export default { name: "TMThing", extends: TMToolBase, // data/methods/computed như component Vue bình thường }; </script>
TMToolBasecung cấp vòng đời tab:onTabEnter/onTabLeave,reBuildTabTitle,toggleSidebar, mixin layout (currentConfigLayout). - Đăng ký trong
src/stores/TMToolConfigs.js:- Tool đơn lẻ: thêm entry
{ type: "route", name, component: () => import(...), meta: { titleKey } }. - Tool trong nhóm: thêm con vào mảng
childrencủa group có sẵn (hoặc tạo group mới vớigroupKey,groupTitleKey). hide: truenếu chưa muốn hiện trên sidebar (vẫn search được nếu cần chỉnhgetAllSearchableRoutes).- Layout vùng nội dung tab (tuỳ chọn): khai báo ở cấp group
contentFlush: true(tool tự lo bố trí, ví dụ app vẽ canvas → không padding) và/hoặcconfirmOnClose: true(tool có dữ liệu tạm chưa lưu → đóng tab sẽ hỏi xác nhận). Ghi đè riêng cho 1 tool bằngmeta.contentFlush/meta.confirmOnClose.
- Tool đơn lẻ: thêm entry
- i18n: thêm
$tkey vàosrc/i18n/vi/i18nCommon.jsvàsrc/i18n/en/i18nCommon.jschotitleKey+ mọi chuỗi trong UI. Tiêu đề tab tự hiển thị từmeta.titleKey. - (Tùy chọn) Help: tạo
src/views/helps/TM<Thing>Help.vuevà nốihelpKeytrong tab meta; nội dung help đặt trongi18nHelp. - (Tùy chọn) Dialog:
TMDialogEnum+DialogComponentMapnằm trongsrc/common/TMDialogUtil.js— thêm enum ở cuốiTMDialogEnum, mapping trongDialogComponentMap, tạo component trongsrc/views/dialogs/(implementshow(param)). - (Tùy chọn) Monaco intellisense: override
registerIntellisense()/disposeIntellisense()trong tool; định nghĩa ngôn ngữ ởsrc/monarch/. - Tool cần lưu cấu hình: kế thừa
TMLayoutConfigMixin(cache keykeyCacheLayout). Tool master-detail GHÉP với backend: dùngTMCollectionMixin+TMCollectionList(xem mục backend). - Tool xử lý nặng: nếu có bước block main thread >~200ms (duyệt/xử lý ảnh, encode, parse dữ liệu lớn, vòng lặp nặng) → tách sang Web Worker qua
TMWorkerPool, không tối ưu bằngawaithaysetTimeout. Xem mục "Tác vụ nặng" bên dưới.
Mẫu tham chiếu: src/common/qrcode/ (đọc + sinh mã QR).
// 1. Worker file: src/common/<miền>/TM<Miền>Worker.js — KHÔNG import gì chạm DOM
import { process } from "<lib thuần>";
self.addEventListener("message", (e) => {
taskChain = taskChain.then(() => handle(e.data)).catch(sendError); // serialize!
});
// 2. Client: src/common/<miền>/TM<Miền>WorkerClient.js
const pool = getWorkerPool("<tên>", () =>
new Worker(new URL("./TM<Miền>Worker.js", import.meta.url), { type: "module" }),
);Ba lỗi hay gặp nhất — đều dính rồi nên ghi lại:
new Worker(new URL(...))phải literal. Truyền qua biến sẽ không được Vite transform, build vẫn xanh nhưng lúc chạy worker không tồn tại. Kiểm tra bằng cáchcurlfile client trên dev server và xem đã thành...?worker_file&type=modulechưa.- Payload phải clone được. Object trong
data()của Vue là reactiveProxy→DataCloneErrorkhi postMessage. Hằng số đặt ngoài component hoặcmarkRaw(). Function/DOM node cũng không được. - Worker không có DOM.
createImageBitmap(blob)thayImage/FileReader;OffscreenCanvas+convertToBlob()thay canvas DOM;ImageDatacó sẵn. Phần cần DOM giữ lại làm fallback ở main thread và chỉ chạy khi worker không xử lý được.
Ngoài ra: báo tiến trình (spinner + x/y), dùng token để lần gọi sau không bị lần trước đè, và nhớ worker.rollupOptions.output trong vite.config.js để file worker giữ prefix tjs- + version chống cache CDN. Chi tiết: AGENTS.md Luật 13 + docs/architecture.md.
- Model trong
src_backend/tm_core_service/internal/model/: struct embedTMBaseModel, implementTableName()+PrimaryKey(), tagjsonđúng tên cột. - Router
internal/router/rt_<domain>.go: đăng ký endpoint (app.HandleFunc("POST /domain/action", …)) kiểu method-pattern. - Service
internal/service/bl_<domain>.go: nếu là CRUD → tái dùngTMBLBase[T]hoặcTMCollection[TGroup,TItem](tự sinhget_all/create/update/delete_by_id/get_tree); gắn hookBeforeInsert/…khi cần logic thêm. Trả{success, message, data}. - Database
internal/database/dl_<domain>.go: dùngTMDLBase[T]; migration mới thêm filemigrations/000N_*.sql(idempotent). - Frontend gọi: thêm class
src/common/api/request/AgentAPI/TMServer<Domain>API.js(extendTMBaseAPI, không hard-code baseUrl) rồi dùng trong tool.
Tiêu chí xem có cần thêm backend không: việc phải ở máy local (SQLite, file, proxy mạng, nhạy cảm) → daemon; còn lại làm thuần frontend để bản web-hosted chạy được.
- Dùng component global
TMButton,TMInput,TMComboBox,TMPopup,TMTableViewer, … (đăng ký sẵn trongmain.js). - Toast:
this.$tmToast.success/error/warning/info(...). Dialog:TMDialogUtil.showPopup(...)/confirm(...). - SCSS trong
src/styles/*.scss; biến theme CSS trongsrc/index.css(:root). Không hard-code màu chung. - Đặt tên key i18n theo dạng dấu chấm:
i18nCommon.<feature>.<nội dung>; tool title trongi18nCommon.feature.*. - Backend: xử lý input cẩn thận (index/len từ người dùng), không panic; log qua helper
tm_common/bl_common_log.gothay vìfmt.Printlnrải rác (mở rộng nếu cần).
npm run dev # smoke-test tool mới/chỉnh sửa
npm run build # PHẢI xanh
cd src_backend/tm_config && go build ./...
cd ../tm_core_service && go build ./... && go vet ./... && go test ./...
cd ../tm_app && go build ./...Nếu đụng Web Worker, npm run build xanh chưa đủ — phải kiểm tra thêm: dev server đã transform đúng new Worker(new URL(...)) chưa; dist/assets/ có file worker với prefix tjs- + version; và chạy thật trong browser với tác vụ nặng để xác nhận main thread vẫn phản hồi (đếm tick của setInterval(…, 16) trong lúc chờ). Chi tiết ở AGENTS.md mục 5.
Cập nhật README.md / docs/ nếu hành vi hay quy trình build đổi. Không commit artifact (Luật 12, AGENTS.md).