Delta team/fix comment ui #10

Merged
gitea-admin merged 21 commits from delta-team/fix-comment-UI into main 2026-09-09 16:46:16 +00:00
Collaborator
fix các bug theo yêu cầu https://fptsoftware362-my.sharepoint.com/:x:/g/personal/nampdt_fpt_com/IQAHBJ4A9xqDTLgvt2bhukJEAdRB5LRz2hbJpTivvIiBSYM?wdExp=TEAMS-TREATMENT&web=1&isSPOFile=1&ovuser=f01e930a-b52e-42b1-b70f-a8882b5d043b%2CAnhTNM1%40fpt.com&clickparams=eyJBcHBOYW1lIjoiVGVhbXMtRGVza3RvcCIsIkFwcFZlcnNpb24iOiI0OS8yNjA4MTMxOTMxNyIsIkhhc0ZlZGVyYXRlZFVzZXIiOmZhbHNlfQ%3D%3D
gitea-admin added 21 commits 2026-09-09 16:44:09 +00:00
Add a 7-module instruction set for an agent that fixes bugs and UI/layout
defects (role, task, input contract, process, output contract, quality gate,
examples) under docs/instruction/agent/, plus a condensed skill_library entry
so the app can load it from the Skill Manager.

The modules encode the working principles this project expects: understand the
code first, fix the root cause rather than the symptom, keep the diff minimal,
change no behavior outside the requirement, follow the existing architecture
(ADR-001 4-tier layering) and patterns, and judge changes on compatibility,
security, maintainability and testability. Unresolved points must be recorded
as Assumption, Open Question or Limitation instead of being decided silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bộ 7 role chuyên biệt (triage → specialist → implementer → reviewer) cùng
lớp dùng chung: guardrail, tri thức về repo, checklist, và contract đầu ra.

Vì sao có: bug UI/UX được báo bằng lời kể triệu chứng, và người sửa hay bỏ
qua ba thứ mà repo này rất dễ vi phạm — luật "không file nào ngoài theme/
được đặt tên một màu", trần LOC theo bánh cóc, và việc ui/ với presentation/
cùng tồn tại nên sửa nhầm file là "đã fix mà vẫn thấy lỗi".

knowledge/qt_pitfalls.md chép lại 20 nguyên nhân gốc hay gặp của bug PySide6;
examples/bad_fix.md có hai ca CÓ THẬT, gồm ca chính bản vá trong nhánh này
từng mắc (compare_digest trên str ngoài ASCII) và lọt qua vòng review đầu.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
DEFAULT_CONFIG ship agent_security.sandbox_pw = "" kể từ commit 3827552, và
cấu hình đưa tới dialog luôn được deep-merge với defaults đó. Nghĩa là trên
mọi bản cài không đặt COWORK_SANDBOX_PASSWORD, mật khẩu đã lưu là chuỗi rỗng
— và phép so `pw == self._sandbox_pw` nhận luôn ô nhập trống. Bấm Unlock với
ô trống là mở được toàn bộ nhóm Sandbox Security.

Commit 3827552 chỉ sửa config.py nên bỏ sót bản sao thứ hai của literal nằm
trong ui/settings_dialog.py, và chính nó tạo ra lỗ hổng rỗng này.

Sửa:
- gỡ literal credential khỏi mã nguồn (nó vốn là code chết: deep-merge làm
  tham số mặc định của .get() không bao giờ chạy);
- chặn rỗng trước khi so, theo đúng mẫu mà MS365 unlock đã dùng;
- so sánh timing-safe trên BYTES, không trên str — secrets.compare_digest
  ném TypeError với str ngoài ASCII, mà app mặc định tiếng Việt và phục vụ
  khách Nhật nên mật khẩu có dấu là input bình thường;
- chưa đặt mật khẩu thì báo đúng trạng thái đó, không báo "sai mật khẩu" —
  người dùng sẽ gõ lại mãi một thứ không tồn tại.

Root cause: ui/settings_dialog.py:104 (bản cũ)
Test: tests/ui/test_sandbox_unlock_security.py

LƯU Ý CHO REVIEWER: literal cũ vẫn nằm trong 6 commit của Git history. Gỡ ở
đây không gỡ khỏi lịch sử — cần xoay credential trên các máy còn giá trị đó
trong config.json. Không rewrite history (SECURITY.md).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
run.bat chỉ hỏi junction "có tồn tại không", không hỏi nó trỏ vào đâu. Một
junction còn lại từ checkout KHÁC vẫn được dùng lại im lặng: run.bat nằm
trong thư mục này nhưng ứng dụng chạy từ thư mục kia. Triệu chứng là "sửa
code xong chạy vẫn y nguyên", và không có gì báo lỗi.

- junction được tạo lại mỗi lần chạy, không chỉ khi thiếu;
- mỗi thư mục mã nguồn có junction RIÊNG (khoá SHA1 từ đường dẫn tuyệt đối).
  Bản trước dùng chung một đường dẫn cho cả máy, nên hai checkout tranh nhau:
  cái chạy sau trỏ junction về mình, và tiến trình con của cái chạy trước
  (máy chủ MCP MS365, sinh ra sau khi app đã mở) import mã nguồn của cái kia;
- install.bat dọn junction dùng chung của bản cũ — để lại là một cái bẫy;
- venv cũ phải CHẠY ĐƯỢC, không chỉ tồn tại file python.exe: venv dựng bằng
  bản Python đã bị nâng cấp hoặc xoá vẫn còn nguyên file đó;
- smoke test kiểm DANH TÍNH, không chỉ kiểm import được — có một "cowork_local"
  khác chen trên sys.path thì lệnh import vẫn chạy tốt và bước kiểm vẫn xanh
  trong khi ứng dụng đọc mã nguồn khác;
- run.bat chốt cowork_local/__main__.py thật sự nhìn thấy được trước khi khởi
  động, thay cho lỗi Python khó hiểu "'cowork_local' is a package and cannot
  be directly executed";
- thông báo lỗi rõ cho ổ mạng và ổ không phải NTFS — junction không trỏ sang
  được, ca hay gặp nhất khi mang sang máy khác.

Đã kiểm bằng cách chạy thật cả hai script.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Dòng "Made by QuanDH14" là widget thường trực ở góc dưới phải: chiếm một góc
màn hình trên MỌI màn, suốt cả phiên, cho một thông tin chỉ cần đọc một lần.
Chuyển vào Cài đặt ▸ Giới thiệu — vẫn tra được, không còn đứng thường trực.

Mục Giới thiệu đứng CUỐI danh sách: nó không có thiết lập nào để đổi, nên đặt
trước các mục thao tác được sẽ đẩy chúng xuống mà không được gì.

Đặc tả SettingsDialog đổi từ 5 lên 6 mục — thay đổi CÓ CHỦ Ý, không phải tách nhầm.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Kéo thanh chia sang trái hết cỡ thì thanh menu biến mất hẳn, và khi đã mất
thì không còn gì đủ rộng để nhận ra, nói gì tới bắt lại mà kéo ra.

_nav_wrap.setMinimumWidth(132) chỉ chi phối việc BỐ TRÍ, không chi phối thao
tác kéo: QSplitter mặc định cho phép người dùng kéo một ngăn vượt qua chính
minimum của nó rồi đóng sập về 0. Tắt bằng setChildrenCollapsible(False).

Thu gọn vẫn là việc của nút MENU, và nó dừng ở 54px chứ không về 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bốn thay đổi trên cùng một khối, gom vào presentation/workspace/project_editing.py:

- Project đã có mở ra ở dạng CHỈ ĐỌC; phải bấm "Sửa project" (nút vàng) mới gõ
  được, và nút Lưu chuyển xanh lá. Trước đây form luôn mở nên chỉ cần lỡ tay là
  sửa mất nội dung của một project đang dùng mà không có gì cảnh báo.
- Chặn TRÙNG TÊN khi tạo và khi lưu (không phân biệt hoa thường, bỏ khoảng
  trắng hai đầu). Danh sách bên trái chỉ hiện tên, nên trùng tên là không phân
  biệt được nữa. Chặn TRƯỚC khi ghi đĩa.
- Chuột phải một project: Mở / Sửa / Xoá. Trước đây bấm phải không làm gì cả.
- workspace.counts được format một lần lúc dựng dòng nên đổi ngôn ngữ xong nó
  vẫn nằm ở ngôn ngữ cũ, kể cả khi chọn tiếng Anh. ProjectRow giờ giữ SỐ và tự
  format lại — KHÔNG gọi refresh(), vì refresh() kết thúc bằng _load_current()
  và sẽ xoá mất nội dung người dùng đang gõ dở.

Ba nút về chung một hàng với "Project mới", và cùng theo một luật hiện/ẩn: chỉ
hiện trên sub-tab Project và khi có project đang mở. Hàng tiêu đề vắt ngang cả
màn Workspace nên thứ gì đặt lên đó cũng lọt sang Cowork/Co4E/Thư mục/GraphRAG
nếu không tự ẩn.

Màu vàng/xanh lá đi qua token warning/success. Hai nút dùng nền *_soft với chữ
và viền là token, KHÔNG phải nền đặc: hai token đó được chỉnh làm màu CHỮ (cùng
lý do accent và accent_solid là hai token riêng) — chữ trắng trên warning dark
#CCA700 chỉ đạt ~2.3:1, trượt AA.

ui/workspace_tab.py đang ở đúng trần bánh cóc nên phần mã mới bắt buộc ra module
riêng; chuyển _ProjectRow và CRUD sang đó làm file co từ 566 xuống 523 dòng mã,
và bánh cóc được siết theo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Landing được chốt SAU _restore_sessions(): hội thoại lần trước vẫn được nạp
lại (phục hồi sau khi thoát đột ngột là mục đích của nó), nhưng khung nhìn đầu
tiên là danh sách chứ không phải cuộc trò chuyện vừa đóng.

Phải đi qua _goto, không gọi thẳng show_history_pane(): _goto là chỗ duy nhất
dời vệt sáng trên thanh menu theo nội dung. Gọi tắt thì nội dung sang Cowork
còn vệt sáng ở lại Project — và vì QTreeWidget không phát currentItemChanged
khi bấm lại đúng dòng đang chọn, bấm "Project" sẽ KHÔNG có tác dụng gì cho tới
khi người dùng bấm sang mục khác rồi bấm về. Gom hai đường vào (link "Tất cả
project…" và màn mặc định) về một hàm để chúng không lệch nhau nữa.

Chưa chọn project thì cổng project đang giữ sub-tab Cowork đóng; ép mở bằng cửa
sau sẽ để nội dung ở Cowork trong khi cả cổng lẫn vệt sáng đều nói là Project.
Màn đúng cho người chưa có project chính là màn quản lý project.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Lịch sử hội thoại KHÔNG nằm chung một chỗ: _bind_project đặt
config._project_history_dir thành <workspace của project>/.cowork_history mỗi
lần chọn project khác — cố ý, để chia sẻ thư mục project là chia sẻ cả lịch sử.

Nhưng hai chỗ đọc lại chỉ đọc MỘT thư mục, gây hai triệu chứng cùng gốc:
- ui/sidebar.py đọc history_dir() (thư mục của project ĐANG mở), nên khung
  "Tất cả project…" dựng đủ tiêu đề nhóm mà mọi nhóm trừ một đều rỗng;
- _project_counts gọi list_conversations() KHÔNG tham số, tức đọc HISTORY_DIR
  toàn cục nơi không có hội thoại nào của project, nên mọi dòng project đều
  đếm "0 đoạn chat".

Thêm history_dirs() + list_conversations_by_project(), giữ đúng thứ tự cũ (ghim
trước, mới nhất trước) và chống trùng. Thư mục là chủ sở hữu có thẩm quyền: hội
thoại nằm trong workspace của project nào thì thuộc project đó, kể cả khi trường
project_id trong file đã cũ vì project bị đổi thư mục.

HISTORY_SUBDIR + project_history_dir() gom đường dẫn về một định nghĩa duy nhất
— chuỗi ".cowork_history" từng nằm rải trong ui/workspace_tab.py.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Hai lỗi riêng biệt cùng gây ra "chọn folder khác, sang tab khác rồi quay lại
thì về folder cũ":

1. WorkspaceFileTree.root_changed KHÔNG có ai lắng nghe trong toàn bộ repo.
   Người dùng chọn thư mục trong cây thì chỉ cái cây đổi gốc; _root, khung xem
   và terminal ở lại thư mục cũ — ba widget con lệch nhau ngay từ lúc bấm chọn.
2. _load_current gọi set_root(project.workspace_dir()) vô điều kiện, và _goto
   gọi workspace.refresh() mỗi lần vào lại màn Workspace, nên mỗi cú chuyển tab
   kéo thư mục về workspace của project.

set_project_root() bỏ qua khi project KHÔNG đổi. Đổi sang project khác thì vẫn
re-root — thư mục của màn này thuộc về project; chỉ lần refresh trong CÙNG một
project là không được đụng.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Cùng lớp lỗi với tab Thư mục: _bind_project gọi _structure.set_project(pid) vô
điều kiện, và chuỗi _goto -> refresh -> _load_current -> _bind_project chạy lại
mỗi lần vào lại màn Workspace, nên bộ chọn project của chính màn GraphRAG bị
kéo về giá trị của Workspace ở mỗi cú chuyển tab. set_workspace_project() bỏ
qua khi project không đổi (dùng None làm giá trị khởi tạo, không phải "", để
lần gọi đầu không bị bỏ qua khi chưa chọn project nào).

Chọn project xong phải tự quét: _on_project_changed cố ý hoãn lượt quét sang
"lần ghé tiếp", nhưng combo nằm ngay trên màn đó nên người dùng đang đứng ở đấy
và không có lần ghé tiếp nào — họ phải tự bấm Scan. Giờ quét ngay khi màn đang
mở; hoãn vẫn giữ cho trường hợp đổi project từ màn Workspace khi GraphRAG ẩn.

Panel "đang tải": prewarm() chỉ chạy 3 giây sau khi cửa sổ hiện, nên bấm
GraphRAG trong 3 giây đầu gặp _ensure_web() dựng QWebEngineView ĐỒNG BỘ trên
GUI thread — đóng băng 1-2 giây mà không có gì báo. Vì GUI thread bị chặn, panel
phải repaint() ngay chứ không update(): update() chỉ xếp hàng một lượt vẽ cho
vòng lặp sự kiện, mà vòng lặp đó sắp bị chặn, nên panel sẽ chỉ hiện ra sau khi
hết đóng băng — đúng lúc không còn cần tới nó nữa.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Prompt cũ chỉ liệt kê TÊN các màn hình, nên model không có cách nào biết trên
mỗi màn có gì và nó lấp khoảng trống bằng thứ nghe hợp lý: người dùng thật đã
được hướng dẫn vào "Dashboard → Add Project" và "Settings → Project Settings →
New Project". Không một thứ nào trong đó tồn tại. Người dùng đi tìm rồi mới
phát hiện ra — câu trả lời trôi chảy mà sai còn tệ hơn câu "tôi không biết".

Ba thứ ghép thêm vào prompt:
- docs/help/app_guide.md — sổ tay viết tay, bám mã nguồn thật, có test chốt
  rằng nó nhắc đủ mọi màn trong docs/screens/manifest.json;
- luật chống bịa, kèm ví dụ few-shot nêu đúng câu trả lời sai đã xảy ra cạnh
  câu đúng, và một ví dụ dạy nó NÓI KHÔNG BIẾT;
- ngữ cảnh sống: màn hình đang mở và nhãn các nút/tab ĐANG hiện.

Ngữ cảnh sống đọc từ cây widget thật, KHÔNG từ docs/screens/controls.json: file
đó trích tự động nhưng đã cũ — 5/41 file trong đó không còn tồn tại và nó không
có file nào trong presentation/ (chưa sinh lại sau refactor R08). Nạp nó vào
prompt là dạy trợ lý về nút của những file đã bị xoá.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Người dùng phản ánh dán nội dung vào chat cho câu trả lời tốt hơn đính kèm cùng
nội dung đó. Nội dung KHÔNG bị cắt (giới hạn 2 triệu ký tự) — nguyên nhân là
loãng và trùng: _augment luôn quét thêm [Workspace files] và [Project files],
và tệp đính kèm nếu nằm trong thư mục workspace sẽ đi vào prompt HAI lần. Với
tài liệu dài, bản thứ hai vừa nhân đôi ngữ cảnh vừa khiến model không biết bản
nào là bản được hỏi.

Lọc trùng theo đường dẫn đã giải quyết, và đổi nhãn để nói rõ tệp đính kèm là
CHỦ THỂ CHÍNH còn tệp thư mục chỉ là ngữ cảnh phụ. Dòng cảnh báo "N tệp không
nạp được" đếm TRƯỚC khi lọc trùng, nếu không nó báo sai.

Cùng lượt, hai chỗ bỏ thông tin không cần thiết:
- gỡ dòng "provider · model" ngay sau chữ "Cowork" — nó lặp lại thứ bộ chọn
  provider ở thanh trên đang hiển thị, mà chiếm chỗ đắt nhất trên thanh công cụ;
- không báo "đang dùng <provider>" ở thanh trạng thái khi đổi provider — bộ
  chọn nằm ngay trên màn hình và đã hiện thứ người dùng vừa tự chọn.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bấm "Cuộc trò chuyện mới" trước đây để lại một khung trắng: không có gì nói
người dùng đang làm trong project nào, thư mục có bao nhiêu tệp, hay bắt đầu từ
đâu. Đây là trạng thái RỖNG — một trong bốn trạng thái mà mọi khung dữ liệu phải
có, và là trạng thái duy nhất người dùng nhìn thấy trước khi gõ chữ đầu tiên.

Gồm lời chào theo tên, dòng bối cảnh (project · số tệp · số skill đang bật), và
bốn thẻ gợi ý.

Hai quyết định:
- Thẻ ĐIỀN câu gợi ý vào ô nhập chứ không gửi luôn. Câu gợi ý là điểm bắt đầu;
  người dùng gần như luôn cần thêm chi tiết của riêng họ, và gửi ngay sẽ tiêu
  một lượt gọi model cho một câu hỏi chung chung.
- Dấu phía trên lời chào không bấm được — nó là dấu hiệu thị giác. Một nút không
  làm gì tệ hơn không có nút.

Dòng bối cảnh phân biệt KHÔNG BIẾT với 0: đếm được 0 tệp thì hiện "0 tệp", còn
không đọc được thư mục thì bỏ hẳn mảnh đó — hiện "0 tệp" khi người dùng vừa thấy
có tệp trong thư mục còn tệ hơn là thiếu một mảnh.

show_welcome() được móc ở ba chỗ: new_session(), load_conversation() (theo số
tin nhắn đã lưu), và TRƯỚC mọi nhánh add_user trong turn runner — đặt sau từng
add_user() thì dễ sót đúng một nhánh, và nhánh đó sẽ hiện cả hai thứ cùng lúc.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ở 132px nhãn "Cuộc trò chuyện mới" bị cắt mất chữ. Sàn phải đủ rộng cho nhãn
DÀI NHẤT trên thanh, không phải cho nhãn trung bình. Bề rộng mở mặc định cũng
lên 232 để nhãn hiện đủ ngay từ lần mở đầu.

Ở cửa sổ nhỏ nhất mà app cho phép (1180px) trần vẫn là 259px nên thanh vẫn kéo
được; chỉ dưới ~1055px nó mới thành cố định.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
GraphRenderer.auto_scan_and_fit() thoát sớm khi ô đường dẫn rỗng, trong khi
chính nút Scan lại có đường lùi (path_edit.text() or Path.cwd()). Bất đối xứng
đó nghĩa là vào màn thì không làm gì, bấm Scan thì chạy.

Cố ý KHÔNG lấy cwd() làm đường lùi như nút Scan — quét thư mục làm việc của
tiến trình là quét một cây không liên quan gì tới project và có thể rất lớn.
Điền từ project đang hoạt động, không có project thì vẫn không quét.

LƯU Ý: chưa tái hiện được ca "lần đầu khởi động không tự nạp" mà người dùng báo.
Probe với project và thư mục thật cho thấy cả hai đường (gọi thẳng
auto_scan_and_fit và bấm GraphRAG trên thanh menu) đều khởi động lượt quét. Đây
là bất đối xứng duy nhất tìm được; cần biết ô đường dẫn và combo project đang
hiện gì lúc lỗi xảy ra để đi tiếp.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Trước đây rất nhiều widget gọi setText(tr(...)) một lần lúc dựng, nên sau khi
đổi ngôn ngữ một nửa màn hình vẫn giữ tiếng cũ. Thêm họ bind_text/bind_tip/
bind_placeholder/bind_items/bind_dynamic trong i18n: khoá dịch được gắn thẳng
vào widget (giữ tham chiếu yếu) và tự áp lại mỗi lần set_language.

- co4e sidebar, node property panel, run control, routing toggle, nav rail,
  top bar, filter scaffold, usage chart: chuyển sang binding hoặc tự đăng ký
  on_language_changed thay vì trông chờ nơi nhúng.
- RoutingToggle/AutoRunToggle tự đăng ký retranslate và dùng
  AdjustToContents để bản dịch dài không bị cắt.
- BusyOverlay mới: che cửa sổ trong lúc set_language chạy đồng bộ trên GUI
  thread, tránh cảm giác treo app.
- co4e sidebar chỉ đọc thư viện skill một lần (_skill_prefix_lookup) thay vì
  quét đĩa cho từng skill — trước đó mỗi lần reload tốn ~3.8s đứng GUI.
- Bổ sung bản dịch ja/vi còn để nguyên tiếng Anh; sửa chiều cao hàng
  Settings ở nav rail.
- Thêm 4 bộ test UI cho các thay đổi trên.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Thêm agent/roles/0_fix_dispatcher.md: phân tier/lane cho từng defect trước
  khi các agent khác chạy, kèm agent/commands/fix.md và hợp đồng đầu ra
  agent/output/dispatch_plan.md.
- Cập nhật system/guardrail, response_policy, security và các checklist
  ui/ux/pr_readiness cho khớp luồng mới.
- Mở rộng knowledge: i18n_rules, screen_map, theme_tokens,
  secrets_and_config; cập nhật workflow intake_to_fix và handoff_contract.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Thanh rail đặt setSpacing(0) nên nút Cài đặt dán sát Dashboard/Giám sát, ba
dòng đọc thành một khối. Thêm hằng _NAV_SETTINGS_GAP trong rail_metrics và
xin khe bằng nvl.addSpacing() — margin trong QSS vẽ BÊN TRONG hộp widget, mà
nút này bị _rebuild_nav ghim đúng chiều cao một dòng menu nên margin không
mua được pixel nào.

Rút gọn lại comment ở _rebuild_nav cho khớp.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Người dùng báo: chọn tiếng Nhật mà nhóm Sandbox Security, nút Save/Cancel và
nhiều chỗ khác vẫn tiếng Anh. Bộ test i18n cũ vẫn xanh vì nó chỉ bắt lỗi "có
dịch nhưng không ai áp lại" — hai lỗ thật nằm chỗ khác:

* Chuỗi HARDCODE không đi qua ``tr()`` bao giờ (``QPushButton("Unlock")``), nên
  phép đo "đổi ngôn ngữ rồi tìm chỗ không mang mốc" thấy nó đứng yên ở cả hai
  lần chụp và coi là bình thường.
* Nhãn nút do CHÍNH Qt vẽ. ``QDialogButtonBox``, ``QMessageBox.question`` và
  ``QInputDialog.get*`` lấy chữ từ bảng dịch riêng của Qt; ứng dụng không cài
  ``QTranslator`` nào và bản PySide6 đang dùng cũng không đóng gói file
  ``qtbase_*.qm`` nào để cài — nên chúng luôn rơi về tiếng Anh.

``ui/dialog_buttons.py`` gán nhãn của dự án đè lên nhãn Qt: ``dialog_buttons``
(10 hộp thoại), ``confirm`` (13 hộp Có/Không), ``ask_text``/``ask_multiline``/
``ask_item`` (16 hộp nhập liệu). Cùng với 17 chuỗi hardcode và 5 câu lỗi mà
``core/tasks.py`` trả thẳng ra hộp thoại — nay trả KHOÁ i18n, nơi hiển thị mới
gọi ``tr()`` — là 61 chỗ.

Ba chỗ nữa cùng lớp lỗi, phát hiện khi rà lại:

* ``_add_section`` nhận chuỗi ĐÃ dịch nên bốn tiêu đề mục của Step config đứng
  nguyên ở ngôn ngữ lúc dựng panel. Nay nhận khoá + ``bind_dynamic`` để không
  mất trạng thái gập/mở khi đổi ngôn ngữ.
* Thẻ tool ở Giám sát ▸ Công cụ hiện thẳng ``spec.description`` — chuỗi gửi cho
  MÔ HÌNH trong schema function-calling, phải giữ tiếng Anh. Thêm bộ mô tả hiển
  thị riêng cho 9 tool.
* Tên nhóm catalog ở tab Connector ("Other (any generic MCP server)").

Kèm theo, phần giao diện người dùng yêu cầu:

* ``__version__`` 2.26.0 -> 0.0.1, một nguồn cho tiêu đề cửa sổ, tab Giới thiệu
  và dòng mới ở góc phải thanh trạng thái (thay dòng ghi công tác giả).
* Tắt size grip: nó vẽ một vệt ngay bên phải dòng phiên bản. Cửa sổ vẫn kéo
  giãn được từ các cạnh.
* ``_NAV_SETTINGS_GAP`` 10 -> 4: hàng Cài đặt bớt xa nhóm Dashboard/Giám sát.

Hai test SẼ TREO nếu không sửa kèm: chúng patch ``QInputDialog.getText/getItem``
để tự trả lời, mà code nay gọi ``ask_text``/``ask_item`` — patch không còn chặn
được và hộp thoại thật sẽ mở ra chờ người bấm.

Ba cổng mới trong ``tests/ui/test_i18n_khong_hardcode_chu.py`` canh ở mức cấu
trúc (không ai được dựng lại kiểu cũ); đã kiểm chúng CẮN trên bản trước khi sửa:
10 + 13 + 17 vi phạm.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gitea-admin force-pushed delta-team/fix-comment-UI from ab04d68a22 to d2101e1f87 2026-09-09 16:44:09 +00:00 Compare
gitea-admin merged commit 1b8429e33a into main 2026-09-09 16:46:16 +00:00
Sign in to join this conversation.