Giao diện
Changelog
Thay đổi của các package @tasco/* bản 2.x, gộp từ CHANGELOG.md của từng package. Các package phát hành cùng một số phiên bản (lockstep), nên mỗi phiên bản dưới đây áp cho cả bộ; thay đổi chạm nhiều package chỉ ghi một lần, kèm tên các package đó. Nâng cấp từ major trước: Nâng cấp phiên bản.
Sắp phát hành
Đã merge vào main nhưng chưa có trong bản phát hành nào (changeset chưa version). Tài liệu trên site có thể đã mô tả các thay đổi này trước khi package lên registry.
Tính năng mới
ui —
CFormexpose API của form qua template ref:validate,validateFields,resetFields,clearValidate,scrollToField.- Trước đây
CFormkhôngdefineExposenênformRef.value.validate()làundefinedvà gọi sẽ némTypeError. Trang chỉ gửi form được bằng@finish+ núthtml-type="submit"— cách đó vẫn chạy, nhưng không làm được validate một phần (bước wizard),resetFields()sau khi lưu hayclearValidate()khi đổi giữa chế độ thêm/sửa. - Thêm kiểu public
CFormInstance(kèmCFormNamePath,CFormValidateOptions,CFormScrollOptions) để app khairef<CFormInstance>()mà không phải import từant-design-vue— ESLint chặn import đó, kể cảimport type. - Chỉ mở đúng 5 hàm trên; hàm khác của antd Form (vd
getFieldsValue) vẫn không expose để hợp đồng của@tasco/uikhông dính vào cây type của antd. Gọi API khi form chưa mount (hoặc đã bị gỡ) báo lỗi[@tasco/ui] …thay vìTypeError. - Tương thích ngược: không đổi prop, slot hay sự kiện nào.
- Trước đây
ui — Bổ sung JSDoc cho mọi prop/event và
defineSlotscho component có slot; build sinh thêmdist/component-meta.json.- Mỗi prop, event của 17 component
C*đều có mô tả tiếng Việt; component có slot khai báodefineSlotsđể slot vào được bảng API.CFormvàCTableforward slot động (tránh TS7022 của vue-tsc) nên chưa khaidefineSlots— slot#title,#toolbar,#filterFieldcủaCTablemô tả tay trong trang docs. - Thêm subpath
@tasco/ui/component-meta.json: props/events/slots kèm kiểu và mô tả, sinh bằngvue-component-metalúc build. Docs (bảng API) và@tasco/mcp(get_component) dùng chung file này thay vì mỗi nơi tự parse component (RFC 0003). - Chỉ thêm kiểu và dữ liệu, không đổi hành vi runtime.
- Mỗi prop, event của 17 component
mcp —
@tasco/mcpđọc chung dữ liệu với site docs: component lấy từ@tasco/ui/component-meta.json, docs tách theo mục và trả link đúng phiên bản/v2/(RFC 0003).get_componentcó đủ sự kiện kèm kiểu payload, slot (kèm props của slot) và link trang docs — trước đây parser regex chỉ đọc được props và tên sự kiện. Hỏi primitive antdv (CSelect,CInput…) nhận tên gốc antdv, cách import từ@tasco/uivà link trang nhóm primitive, thay vì "Không tìm thấy".list_componentsliệt kê thêm primitive antdv re-export qua@tasco/ui.search_docstrả từng mục##/###kèm linkhttps://web.docs.vtii.vn/v2/…#anchorvà đoạn đầu nội dung, thay vì cả trang với đoạn trích 180 ký tự. Gõ không dấu vẫn khớp chữ có dấu; từ khoá ngắn (IAM,ui) khớp nguyên từ.- Tool mới
read_doc: đọc đủ một mục (kèm mục con) hoặc cả trang theo link, route (/guide/auth/guard) hoặc đường dẫn file. - Nội dung docs của MCP có code demo, bảng API và token thật thay cho thẻ
<demo>,<ApiTable>,<TokenTable>. - Server gửi
instructionslúc kết nối: tool nào dùng cho việc gì, app không important-design-vue. - Kiểu export
ComponentMetacó thêmevents,slots,docsUrl;DocEntrycó thêmurl,level;Knowledgecó thêmprimitives. Trường cũ (emits,props[].optional) giữ nguyên.
theme · ui —
@tasco/themeexportluminanceOfvàcontrastRatioOf; meta của@tasco/uighi giá trị mặc định dễ đọc.- Hai hàm tính tương phản WCAG trước đây chỉ dùng nội bộ (test khoá cặp token) nay là API công khai — bảng token trong docs tính tỉ lệ tương phản bằng chính hàm này thay vì chép lại công thức.
component-meta.json: giá trị mặc định đổi từ literal JSON ("Xoá") sang dạng đọc được ('Xoá'); prop boolean không khai mặc định ghi rõfalseđúng như Vue xử lý.
ai · composables · theme · utils —
@tasco/themeexport danh sách cặp chữ/nền đã khoá tương phản; bổ sung TSDoc cho mọi export củatheme,composables,utils,ai.- Thêm
contrastPairs(cặp chữ/nền component đang dùng, kèm nơi dùng và ngoại lệ đã biết),pairContrastOf(pair, mode)(tương phản thấp nhất của cặp, nền gradient xét mọi điểm màu) vàAA_TEXT_CONTRAST(4.5). Test khoá tương phản và bảng "Màu & tương phản" của docs cùng đọc danh sách này thay vì mỗi nơi giữ một bản. - Mọi export, thuộc tính interface và giá trị trả về của composable
use*có TSDoc tiếng Việt — trang Tra cứu API sinh từ đây bằng TypeDoc, build docs báo lỗi khi export mới thiếu mô tả. - Chỉ thêm export và chú thích, không đổi hành vi runtime.
- Thêm
Sửa lỗi và cải tiến
cli — App scaffold ghim
@tasco/mcptheo major (npx -y @tasco/mcp@2) vàCLAUDE.mdcủa app hướng dẫn AI tra framework qua MCPtasco.- Trước đây
.mcp.jsongọi@tasco/mcpkhông kèm version nên AI có thể đọc props và link docs của bản khác với bản app đang cài. App đã scaffold từ bản cũ: đổiargscủa servertascothành["-y", "@tasco/mcp@2"]. CLAUDE.mdthêm mục "Tra cứu framework":get_component,search_docs/read_doc,list_tokens.
- Trước đây
cli — README của
@tasco/clibỏ lệnhpnpm create @tasco/app(không có package@tasco/create-appnên lệnh không chạy), hướng dẫnnpx @tasco/cli <tên>hoặcpnpm dlx @tasco/cli <tên>và liệt kê đủ file dự án sinh ra.ui —
CInputPercentnhận dấu phẩy thập phân khi gõ và hiển thị theo quy ước Việt Nam (12,5).- Trước đây ô chỉ giữ chữ số và dấu chấm: người dùng gõ
12,5theo thói quen thành125, rời ô thì bị kẹp vềmax(mặc định 100) mà không có báo lỗi nào. - Nay dấu phẩy và dấu chấm đều là dấu thập phân (gõ nhiều dấu thì giữ dấu đầu tiên); vẫn cắt theo
precisionkhi gõ và kẹp[min, max]khi rời ô như cũ. Ô hiển thị dấu phẩy, khớpCInputCurrency. - Tương thích ngược:
v-model:valuevẫn là chuỗi dấu chấm ('12.5'), code gọi API không phải đổi. Chỉ phần hiển thị trong ô đổi từ12.5sang12,5— test e2e đang kiểm giá trị hiển thị của ô thì cập nhật theo. - Khi ô phải sửa lại chuỗi vừa gõ (đổi
.thành,, bỏ ký tự lạ), con trỏ giữ đúng chỗ thay vì nhảy về cuối ô — chèn.vào giữa125rồi gõ tiếp vẫn ra đúng số. - Value truyền từ ngoài dùng dấu phẩy (
'12,5') được chuẩn hoá về'12.5'khi rời ô, thay vì mất phần lẻ. - Dán chuỗi có nhiều dấu thập phân (vd
1.2.345) giờ cũng bị cắt theoprecision; trước đây lọt thành1.2345.
- Trước đây ô chỉ giữ chữ số và dấu chấm: người dùng gõ
ui —
CInputPercentgõ được số âm khiminâm; sửa hướng dẫn đặt dấu%trong docs.- Trước đây ô bỏ dấu
-khi gõ, nênminâm (vd-100cho tỉ lệ tăng trưởng) chỉ có tác dụng với value truyền từ ngoài. Naymin < 0thì giữ một dấu-ở đầu số (gõ-12,5→'-12.5').mintừ0trở lên (mặc định) vẫn bỏ dấu-như cũ. - Ô có
minâm dùnginputmode="text"vì bàn phím số (decimal) của iOS không có phím-; ô khác giữinputmode="decimal". Truyềninputmodevẫn ghi đè được. - Rời ô khi mới gõ dấu mà chưa có chữ số (
-,,) thì ô về rỗng (chưa nhập), thay vì để model'-'hay'.'màNumber()đọc raNaN. - Docs: ô vốn có sẵn hậu tố
%, nhưng trang component lại khuyên đặt%ở nhãn hoặcaddon-after, làm%hiện hai lần. Đã sửa hướng dẫn và bỏ(%)khỏi nhãn trong demo, story, playground. App đang ghi(%)ở nhãn cạnhCInputPercentthì bỏ đi; cần đơn vị khác (vd%/năm) thì truyềnsuffix.
- Trước đây ô bỏ dấu
nuxt-layer-base — Layer đặt
<html lang="vi">và khai kiểupermissiontrongRouteMeta.- Trước đây thẻ
<html>không cólang: trình đọc màn hình đọc nội dung tiếng Việt bằng giọng mặc định của máy và trình duyệt gợi ý dịch trang. App cần ngôn ngữ khác thì ghi đèapp.head.htmlAttrs.langtrongnuxt.config.ts. definePageMeta({ permission: '/orders/search' })đã được middlewarepermission.globalhỗ trợ nhưng chưa có trongRouteMetanên không có gợi ý kiểu. Không đổi hành vi.
- Trước đây thẻ
cli · nuxt-layer-base — Sửa template và mock đang hướng dẫn sai registry và định dạng permission.
@tasco/cli:.npmrcvà.gitlab-ci.ymlcủa app scaffold trỏ group endpointhttps://gitlab.dnpwater.vn/api/v4/groups/tasco/-/packages/npm/thay cho instance endpoint — instance endpoint trả 404 với người không phải member của project core..npmrcđọc token qua${NPM_TOKEN}(Group Deploy Token, scoperead_package_registry). App đã scaffold từ bản cũ nên chép lại 2 file này.@tasco/nuxt-layer-base: dữ liệu mock củaPOST /auth/login(NUXT_AUTH_MOCK=true) đổi permission từorder.read/order.writesang URI/orders/search,/orders/create,/orders/updatecho khớp IAM thật. Dự án đang dev bằng mock với permission dạngorder.readsẽ bị 403 — đổidefinePageMeta({ permissions }),can()vàv-cansang URI tương ứng.
cli · nuxt-layer-base — Template app trỏ tới các trang tài liệu mới của GĐ2 (hướng dẫn từng bước, mẫu trang, phân quyền, xử lý sự cố) trong
README.mdvàCLAUDE.md. Sửa comment sai trongapp.config.tscủa layer: Nuxt nối mảng khi merge cấu hình giữa các layer (defu), không thay nguyên.
2.0.0
Thay đổi phá vỡ tương thích
cli · eslint-config — Nâng
@tasco/eslint-configlên ESLint 10 và yêu cầu Node >=20.19.0. App scaffold mới cũng dùng ESLint 10.composables · ui · utils — CTable có drawer Thiết lập: đổi thứ tự/ẩn cột, sắp xếp mặc định, lưu localStorage theo từng bảng
- @tasco/ui —
CTable: nút ⚙ (show-column-setting) mở drawer Thiết lập thay cho popover ẩn/hiện cột. Tab Hiển thị cột: kéo thả hoặc nút ↑↓ (bàn phím/cảm ứng), công tắc Hiện/Ẩn; cộtfixedchỉ đổi thứ tự trong nhóm, cộttitle: ''giữ nguyên vị trí. Tab Khác: sắp xếp mặc định (cột cósorter+ chiều). Làm việc trên bản nháp (Lưu lại / Đặt lại, đóng là bỏ nháp), focus trả về nút Thiết lập khi đóng.- Prop
settingsKey: lưulocalStorage['tasco:table:<settingsKey>']— mỗi bảng một khoá nên nhiều CTable trên một trang không ghi đè nhau; thiết lập cũ tự hợp nhất khicolumnsđổi; dữ liệu hỏng/khác version bị bỏ qua. v-model:settings(TableSettings) thay chov-model:hiddenColumns(chưa phát hành).- Lưu sắp xếp mặc định mới → CTable phát
@change(actionsort, về trang 1) như khi bấm tiêu đề cột.changenay là emit khai báo của CTable (có type) nên lỗi từ handler async (vduseTable.onChange) đi qua error handler của Vue. - Khi dùng thiết lập, CTable điều khiển
sortOrdercủa cộtsorterđể chỉ báo khớp sắp xếp đang áp dụng (trừ khi trang tự khai báosortOrder, dùngsorter.multiplehoặc có cột nhóm). - Có
settingsKey/settingsmà cột cấu hình được thiếukeylẫndataIndex→ ném lỗi[@tasco/ui]. - Export type
TableSettings,TableSort,TableSortOrder. Footer drawer Lọc đổi thành hai nút chia đôi cho đồng bộ.
- Prop
- @tasco/composables —
useTable:options.settingsKey(cùng khoá với CTable) — lần load đầu theo sắp xếp mặc định đã lưu.onChangenhận đúng dạng sorter của a-table: mảng (nhiều cột) lấy cột đầu,dataIndexlồng nối., bỏ sắp xếp (không cóorder) thì xoásortField/sortOrder— trước đây vẫn gửisortField. - @tasco/utils:
getTableSettings/setTableSettings/clearTableSettings(storage truyền vào được; storage bị chặn thì không ném lỗi),parseTableSettings(kiểm tra dữ liệu không tin cậy),tableSettingsKeyOf,isTableSortOrder; typeTableSettings,TableSort,TableSortOrder,TableSettingsStorage.
- @tasco/ui —
nuxt-layer-base · theme · ui — CTable thành khung trang danh sách chuẩn; bật locale vi_VN của antdv cho toàn app
- @tasco/ui —
CTable: bọc bảng trong khung card (dựa trênCCard) có tiêu đề (title/ slot#title) và toolbar tuỳ chọn:show-create(@create),show-search+v-model:searchValue(@searchkhi Enter hoặc bấm xoá),show-filter+filter-count(chấm đỏ,@filter),show-export(@export),show-reload(@reload, icon xoay khi loading),show-column-setting(nút Thiết lập — xem changeset drawer thiết lập), slot#toolbar; thêmstriped,collapsible,defaultOpen,borderless,type. Phân trang mặc định "Tổng số dòng N" + chọn số dòng/trang + cỡdefault(ghi đè từng key quapagination,falseđể tắt). Cột vẫn do trang định nghĩa; props/slot/sự kiệna-tableforward nguyên vẹn.- Thay đổi hành vi:
class/stylegắn vào khung ngoài (trước đây ởa-table); slot#titlelà tiêu đề khung (không còn forward làm title củaa-table); bảng không có tiêu đề/toolbar vẫn nằm trong khung viền — dùngborderlesskhi đặt CTable bên trong card khác. - Thêm dependency
@tabler/icons-vuecho icon toolbar.
- Thay đổi hành vi:
- @tasco/nuxt-layer-base:
app.vuetruyền localevi_VNcủa antdv cho<a-config-provider>(phân trang "/ trang", Empty, Modal, Popconfirm…). Dự án tự overrideapp.vuecần thêm:localetương ứng. - @tasco/theme (
base.css): sửa khoảng trống 16px ngay dưới header của bảng cóscroll/sticky (padding ô áp nhầm vào hàng đo cộtant-table-measure-row); vạch ngăn cột ở header rõ hơn.
- @tasco/ui —
nuxt-layer-base — Gỡ phần OIDC còn sót trong
@tasco/nuxt-layer-baseĐăng nhập OIDC đã gỡ từ 1.2.0, sau đó layer đăng nhập bằng form first-party qua IAM. Phần OIDC giữ lại không còn tác dụng: session IAM không lưu refresh token nên nhánh refresh ở
/api/**không bao giờ chạy, và không có phiên IdP nào để đăng xuất.- Xoá
server/utils/oidc.ts. Nitro không còn auto-importgetOidcMetadata,generatePkce,randomState,refreshAccessToken,mapUserInfovà các typeOidc*sang app. - Xoá
runtimeConfig.oidc, nên envNUXT_OIDC_*(kể cảNUXT_OIDC_MOCK) không còn tác dụng. /api/**: access token hết hạn thì bỏ token để backend trả 401, không thử refresh./auth/logout: xoá session rồi redirect về đường dẫn nội bộ, không chuyển tới end_session_endpoint của IdP.TascoSessionbỏ fieldrefreshToken.- App tự dùng các hàm OIDC auto-import, đọc
useRuntimeConfig().oidchoặc truyềnrefreshTokenvàosetTascoSessioncần bỏ phần đó. Trong monorepo không có nơi nào dùng; app sản phẩm xem Migration Guide v1 → v2. Đánh major ở Tasco vì đây là API đã phát hành cho team sản phẩm (RFC 0002).
- Xoá
theme · ui — Sửa tương phản WCAG AA (≥ 4.5:1) cho token
primary(theme tối) vàlink(theme sáng)- @tasco/theme — token
- Theme tối:
primary#4f76d1→#7090dc,primaryHover#7b98df→#94ade6.- Chữ primary trên
primary-soft(vdCTag color="primary") 3.44 → 4.77:1; trênsurface3.76 → 5.21:1; trênsurface-elevated(modal/popover) 3.44 → 4.77:1. gradientPrimaryđổi thành#35549e → #4466b8, để chữ trắng của CButton primary ≥ 4.5:1 dọc cả dải, kể cả khi hover.
- Chữ primary trên
- Theme sáng:
link#1576f4→#1068d6. Nút "Đặt lại" / "Xoá tất cả" của CTable trênsurfacetừ 4.26 → 5.29:1; đạt ≥ 4.5:1 cả trênsurface-muted,pagevàlink-soft.
- Theme tối:
- @tasco/theme —
base.css(chỉ theme tối)- Ở theme tối, primary là màu chữ sáng nên không làm nền cho chữ trắng được nữa (chỉ ~3:1). Các nền primary có chữ trắng chuyển sang
--tasco-color-primary-active(≥ 7:1):- header bảng; cột đang sort/hover phủ thêm lớp tối;
- header
CCard type="primary"; - nút primary thuần antdv (Modal.confirm của
useConfirm, nút Gửi củaCChat; không áp cho CButton); - ô ngày đang chọn của DatePicker / RangePicker.
- Vì sao phải override: antdv suy màu nền từ seed
colorPrimary(trộn 85% với#141414), và không có seed nào đạt ≥ 4.5:1 cho cả chữ primary lẫn chữ trắng trên nền primary. Các chỗ antdv khác còn nềncolorPrimary+ chữ trắng mà core chưa dùng (vd Steps, Radio solid, Tag checkable, Menu theme dark, Badge.Ribbon) chưa được override, tương phản khoảng 4:1. ::selectiontheme tối: chữprimary-active→primary-hover(2.07 → 6.66:1 trênprimary-soft).
- Ở theme tối, primary là màu chữ sáng nên không làm nền cho chữ trắng được nữa (chỉ ~3:1). Các nền primary có chữ trắng chuyển sang
- @tasco/ui —
CChatMessage: bubble của người dùng dùng nền--tasco-color-primary-activethay choprimary. Theme sáng hơi đậm hơn (#182544, chữ trắng 15:1); theme tối#35549e(7.21:1). - Dự án nào đặt chữ trắng lên
var(--tasco-color-primary)trong CSS riêng cần đổi sang--tasco-color-primary-active, nếu không theme tối chỉ còn ~3:1.
- @tasco/theme — token
theme · ui — Sửa tương phản WCAG AA (≥ 4.5:1) cho màu semantic, accent, nút danger và chữ màu antdv tự vẽ ở theme tối; thêm token màu chữ
accentText,successText,warningText- @tasco/theme — token mới:
accentText,successText,warningText(--tasco-color-{accent,success,warning}-text), là màu chữ/icon trên nền sáng.- Theme sáng:
accentText#9e5d02,successText#087e02,warningText#a55904. Đạt ≥ 4.5:1 trên nền*-soft,surface,pagevàinfo-soft(hàng đang chọn của bảng). Ở theme tối, ba token này trùng màu gốc. - Vì sao phải tách:
accent#ff9800là màu nền thương hiệu; dùng làm chữ chỉ đạt 1.93:1 trênaccent-soft.successvàwarningcòn là seed antdv. Seed tối làm tint nền antdv sinh ra bị xỉn (vdcolorWarningBg#fff7e6→#e6dfcf, ảnh hưởng Alert, Tag), nên giữ nguyên giá trị seed.
- Theme sáng:
- @tasco/theme — token đổi giá trị
- Theme sáng:
error#ee0033→#d71431.- Chữ trên
error-soft4.08 → 4.73:1, trênsurface4.48 → 5.21:1. - Chữ trắng trên nền
colorErrorcủa antdv (Badge, hover item danger của Dropdown) 4.48 → 5.21:1. - Tint nền antdv giữ gần như cũ (
colorErrorBg#ffe6e7→#ffe6e6).
- Chữ trên
gradientDanger(cả hai theme) →linear-gradient(135deg, #cf1444, #d71431). Chữ trắng của CButton danger ≥ 5.21:1 dọc cả dải, ≥ 4.56:1 khi hover (brightness(1.08)). Trước đây theme sáng 3.67 → 4.48:1, theme tối 2.77 → 3.67:1.
- Theme sáng:
- @tasco/theme —
base.css- CSideNav, item hover/đang chọn (kể cả submenu cha lúc thu gọn): giữ nền cam
accent, chữ và icon đổi từ trắng sang navy--tasco-color-sidebar-bottom. Theme sáng 2.16 → 7.03:1, theme tối 1.94 → 8.90:1. - Nút primary danger thuần antdv, cả hai theme (vd
okButtonProps: { danger: true }): nền--tasco-gradient-danger, hoverbrightness(1.08)như CButton danger. Theme tối 3.61 → 5.21:1; hover theme sáng 3.83 → 4.56:1. - Theme tối, chữ màu antdv tự trộn từ seed:
- Trang đang chọn của Pagination (CTable) và tab đang chọn dùng
primary(4.03 → 5.21:1 trênsurface); hover dùngprimary-hover. - Item danger của Dropdown (menu thao tác dòng): chữ dùng
error(4.14 → 5.39:1), hover nềngradient-danger(3.61 → 5.21:1).
- Trang đang chọn của Pagination (CTable) và tab đang chọn dùng
- Chưa phủ (core chưa dùng hoặc không nằm trong đợt này):
- Theme tối: chữ lỗi Form trong Modal/Drawer (4.14:1); số của Badge (3.61:1); nền
colorPrimarychữ trắng của Steps, Radio solid, Tag checkable (4.04:1); chữ của Radio.Button và item Dropdown đang chọn. - Theme sáng: chữ
colorWarning/colorSuccessdo antdv vẽ (Form warning/success: 3.19 và 3.68:1); chữ hover của nút danger viền (4.24:1).
- Theme tối: chữ lỗi Form trong Modal/Drawer (4.14:1); số của Badge (3.61:1); nền
- CSideNav, item hover/đang chọn (kể cả submenu cha lúc thu gọn): giữ nền cam
- @tasco/ui: CTag
accent/success/warning, iconaccent/success/warningcủa CStatistic, trend tăng của CStatistic và chữ trạng thái hoạt động của CStatus chuyển sang biến*-text. Chấm của CStatus vẫn giữ màu gốc, vì chấm là đồ hoạ, chỉ cần ≥ 3:1. - Dự án cần làm gì:
- CSS riêng đang đặt chữ
var(--tasco-color-{accent,success,warning})trên nền sáng → đổi sang biến*-text. - Chữ trắng trên
var(--tasco-color-accent)→ đổi sangvar(--tasco-color-sidebar-bottom). - Bảng cặp màu an toàn nằm ở
docs/guide/theming.md.
- CSS riêng đang đặt chữ
- @tasco/theme — token mới:
Tính năng mới
ai — Package mới
@tasco/ai— nền tảng AI dùng chung (Giai đoạn 2 lộ trình AI tooling):useAiChat(client): chat streaming SSE qua BFF, state messages/status/error, abort/reset.createAiChatProxy(@tasco/ai/server): Nitro handler proxy tới AI gateway OpenAI-compatible; whitelist body, allowlist model, giới hạn payload, key chỉ tồn tại server-side.- Parser SSE +
sanitizeChatRequesttách rời, test đầy đủ (coverage gate 90%).
ai — Thêm prompt library (@tasco/ai/prompts: definePrompt/renderPrompt validate biến, bộ prompt chuẩn tascoAssistant/summarize/extractJson có version) và eval harness (@tasco/ai/eval: assertion tất định contains/regex/json-valid/max-lines, runner gọi gateway OpenAI-compatible, report). Golden cases ở evals/cases.ts, chạy bằng
pnpm --filter @tasco/ai eval; CI thêm job ai-eval chạy khi MR đụng packages/ai/** (allow_failure, cần AI_GATEWAY_URL/AI_GATEWAY_KEY).cli — Scaffold thêm CI/CD deploy đầy đủ, dựa trên hạ tầng Docker + GitLab Runner shell executor đã triển khai thực tế cho một sản phẩm Tasco:
Dockerfileđa stage (check/build/runtime),docker-compose.yml/docker-compose.dev.yml/docker-compose.prod.yml,scripts/deploy-prod.sh,.dockerignore, và.gitlab-ci.ymlmới (stage check → build → deploy, tự deploy nhánhdevlên máy dev, prod deploy thủ công qua script). Thêm scriptcheck(lint+typecheck) vàopackage.jsonscaffold. Docs: mục mới CI/CD & Deploy.cli — Scaffold thêm sẵn
CLAUDE.mdcho project mới: context chuẩn cho AI coding assistant (quy ước C*/useApi/definePageMeta, gotchas CSR/theme/cookie/registry) — project tạo từcreate-tasco-appdùng được Claude Code/Cursor đúng chuẩn framework ngay từ đầu.cli — Scaffold thêm cấu hình AI tooling:
.mcp.json(GitLab self-host + Figma) và.claude/settings.jsonguardrail (deny đọc.env/~/.npmrc, deny force-push, allow sẵn các lệnh lint/typecheck/build/git read-only).cli · mcp — Thêm package @tasco/mcp — MCP server nội bộ phơi tri thức framework (component C*, design token, docs, package registry) cho AI của team sản phẩm qua 5 tool (list_components, get_component, list_tokens, search_docs, list_packages). Tri thức sinh lúc build vào data/*.json nên server chạy standalone trong repo sản phẩm. Scaffold @tasco/cli thêm sẵn server "tasco" vào .mcp.json.
composables · ui — CTable có bộ lọc dựng sẵn: drawer chứa form lọc + thanh điều kiện lọc phía trên bảng; useTable giữ bộ lọc khi đổi trang
- @tasco/ui —
CTable: propfilterFields(trang khai báo trường lọc nhưcolumns:input,select(options,multiple),date,dateRange,customqua slot#filterField;formatđể tuỳ chữ hiển thị) +v-model:filterValues. Bấm Lọc mở drawer làm việc trên bản nháp (Áp dụng / Đặt lại; đóng là bỏ nháp); điều kiện đang áp dụng hiện thành thẻ ngay trên bảng, bấm ✕ bỏ từng điều kiện hoặc Xoá tất cả. Chấm đỏ của nút Lọc tự đếm theo số điều kiện; focus trả về nút Lọc khi đóng drawer / bỏ thẻ cuối. Export typeTableFilterField,TableFilterOption,TableFilterValues.- Không truyền
filterFields→ hành vi cũ giữ nguyên (@filter,filterCount). - Trường
type: 'custom'thiếu slot#filterField→ ném lỗi[@tasco/ui]khi dựng bảng.
- Không truyền
- @tasco/ui —
CTag: thêmclosable+closeText(nhãn đọc màn hình), sự kiệnclose; nút ✕ là<button>thao tác được bằng bàn phím. - @tasco/composables —
useTable: thêmonFilter(values)(bind@update:filter-values, về trang 1 rồi tải lại),filterValues(bind:filter-values) vàoptions.filters(bộ lọc mặc định).query.filters= filter cột củaa-tablegộp với bộ lọc form (trùng key thì form thắng) — trước đâyonChangeghi đèquery.filtersbằng filter cột mỗi lần đổi trang.
- @tasco/ui —
nuxt-layer-base · ui — Thêm component chat AI: CChat (panel controlled — list auto-scroll + ô soạn, Enter gửi/Shift+Enter xuống dòng, chặn gửi giữa lúc gõ IME) và CChatMessage (bong bóng message + typing indicator). Thuần trình bày, wire với useAiChat của @tasco/ai qua props/emit.
Sửa lỗi và cải tiến
cli —
create-tasco-appvà README của template hướng dẫn bỏ qua đăng nhập khi dev bằngNUXT_AUTH_MOCK=truethay choNUXT_OIDC_MOCK=trueKhông đổi API và runtime. Layer đăng nhập qua IAM, cờ bỏ qua đăng nhập là
runtimeConfig.auth.mock(NUXT_AUTH_MOCK), khớp.env.example,CLAUDE.mdcủa template và docs Auth.NUXT_OIDC_MOCKkhông bỏ qua đăng nhập (phần OIDC còn sót đọc cờ này cũng đã gỡ khỏi layer), nên làm theo hướng dẫn cũ vẫn phải đăng nhập IAM thật.- Lời nhắc sau khi scaffold và mục "Bắt đầu" trong README template: cấu hình IAM/backend hoặc
NUXT_AUTH_MOCK=true. - README template: mục "Có sẵn từ layer" mô tả auth là đăng nhập IAM qua BFF thay vì OIDC.
- Lời nhắc sau khi scaffold và mục "Bắt đầu" trong README template: cấu hình IAM/backend hoặc
cli · nuxt-layer-base — Sửa app scaffold bằng
create-tasco-appfailpnpm installvàpnpm typecheckkhi đặt ngoài monorepo (pnpm 11+)Không đổi API và runtime. Lỗi có sẵn từ 1.3.0, làm script
check, stagecheckcủaDockerfilevà job check trong.gitlab-ci.ymlcủa app mới fail ngay từ đầu. Monorepo không gặp vì cóallowBuildsởpnpm-workspace.yamlgốc và@types/nodeđược hoist từ package khác.pnpm installthoát lỗiERR_PNPM_IGNORED_BUILDS(@tasco/cli): pnpm 11+ chặn build script của dependency và fail khi còn package chưa được quyết định (esbuild,core-js). Template thêmpnpm-workspace.yamlkhai báoallowBuilds(esbuild,@parcel/watcherđược chạy;core-jsbị chặn),Dockerfilecopy file này trướcpnpm install --frozen-lockfile.package.jsonpinpackageManagercùng bản pnpm của core, để corepack trong Docker/CI cài đúng bản thay vì bản mặc định của corepack.pnpm typecheckbáo TS2307node:cryptovà TS2591Buffer(@tasco/nuxt-layer-base): app typecheck thẳng sourceserver/utils/{session,iam,oidc}.tscủa layer nhưng không có type Node. Layer khai báo@types/nodetrongdependenciesvà các file dùng API Node có/// <reference types="node" />, nên app không cần tự cài@types/node. App tạo từ template cũ cũng hết lỗi sau khi nâng layer.- App tạo từ template cũ vẫn fail
pnpm installvới pnpm 11+ cho tới khi chéppnpm-workspace.yaml, fieldpackageManagervà dòngCOPYtrongDockerfiletừ template mới (sau đó chạypnpm installđể ghi bản pnpm vàopnpm-lock.yaml). - Test template của
@tasco/cli(pnpm --filter @tasco/cli test) chặn tái phát: template thiếuallowBuilds/packageManager,Dockerfilekhông copypnpm-workspace.yaml, hoặc server util dùng API Node mà không tham chiếu type Node.
cli — Template scaffold khai báo
engines.nodekhớp yêu cầu của Nuxt 4.5 (^22.19.0 || ^24.11.0 || >=26.0.0)Không đổi API và runtime. Template cài
nuxt@^4.0.0nên app mới nhận Nuxt 4.5, bản chỉ hỗ trợ Node 22.19+, 24.11+ và 26+. Giá trị cũ>=20.19.0ghi sai là chạy được trên Node 20, 23 và 25.- pnpm không chặn cài đặt theo
engines.nodecủa chính project khi chưa bậtengine-strict, nên đổi này không làm failpnpm installhay Docker build (node:24-alpine). - App tạo từ template cũ nên sửa
engines.nodetrongpackage.jsontheo cùng giá trị.
- pnpm không chặn cài đặt theo
nuxt-layer-base — Bổ sung
CFilterBarvào khai báoGlobalComponentstrongtasco.d.ts— component đã được đăng ký global qua@tasco/uinhưng thiếu type, khiến template dùng<CFilterBar>không có gợi ý type/IntelliSense.nuxt-layer-base — README và comment của
@tasco/nuxt-layer-basemô tả đúng đăng nhập IAM thay cho OIDCKhông đổi API và runtime.
- README: auth là form đăng nhập first-party, BFF đổi credential lấy token IAM. Liệt kê đúng các route
/auth/*(bỏ/auth/callbackkhông tồn tại). Khối env dùngNUXT_AUTH_BASE_URLvàNUXT_AUTH_MOCKthay choNUXT_OIDC_*vàNUXT_OIDC_MOCK. Access token hết hạn thì phải đăng nhập lại, vì session IAM không giữ refresh token. nuxt.config.ts: comment củaapiProxyTargetghi tên envNUXT_API_PROXY_TARGET.- Plugin
02.auth.ts: bỏ comment nhắc OIDC là mặc định và callback ở/auth/callback.
- README: auth là form đăng nhập first-party, BFF đổi credential lấy token IAM. Liệt kê đúng các route
nuxt-layer-base — Không publish file test trong
server/của@tasco/nuxt-layer-baseKhông đổi API và runtime. Trường
filesđang lấy cả thư mụcserver, nên tarball kèm các fileserver/utils/*.test.ts. Nuxt quétserver/utilsđể auto-import, và app sản phẩm không cần các file này.theme — Thêm Vitest cho
@tasco/theme; test khoá tương phản WCAG AA của các cặp token mà component đang dùngKhông đổi API và giá trị token.
pnpm --filter @tasco/theme testgiờ chạy thật (vitest run --coverage).- Hàm thuần
luminanceOf/contrastRatioOftính tương phản theo WCAG 2.x (src/contrast.ts). Hiện chỉ test dùng nên chưa export ra package. css-vars.test.tsđo trêncssVars('light')vàcssVars('dark'), yêu cầu ≥ 4.5:1 cho:- primary, primary-hover, link, text-muted trên các nền component đang dùng (primary-soft, surface, surface-elevated, surface-muted, page);
- chữ trắng trên primary-active, gradient-primary, gradient-danger (theme sáng thêm primary và error); chữ sidebar-bottom trên accent (CSideNav);
- toàn bộ bảng cặp AA trong
docs/guide/theming.md:*-textcủa accent/success/warning và primary/error/info, trên nền soft và surface; - error trên surface-elevated (item danger của Dropdown); success-text và error trên info-soft (CStatus trong hàng đang chọn của bảng).
- Cặp đang biết là chưa đạt được ghi thành ngoại lệ kèm lý do. Test khẳng định cặp đó vẫn dưới ngưỡng; khi sửa đạt, test sẽ đỏ để nhắc gỡ ngoại lệ và khoá cặp lại. Hiện còn một cặp: text-subtle trên surface (mô tả CEmpty), 2.54:1 ở theme sáng, 3.55:1 ở theme tối.
theme — Sửa tương phản WCAG AA cho chữ mục chưa chọn của CTopNav ở theme tối
- @tasco/theme —
base.css: mục chưa chọn của CTopNav (menu module ngang trên headerCAppLayout) đổi màu chữ từ navy#253264tô cứng sang--tasco-color-text.- Theme tối, trên
surface#162033: 1.33 → 15.58:1. Trước đây chữ gần như chìm vào nền header. - Theme sáng:
#10213ftrên#ffffff, 12.22 → 16.01:1. Vẫn là tông navy, chỉ đậm hơn một nấc. - Mục đang chọn vẫn dùng
primary+ gạch chân. Màu hover không đổi, vì antdv vốn tô chữ hover bằngcolorText(cùng giá trịtext); hover vẫn được báo bằng gạch chân primary.
- Theme tối, trên
- Test tương phản khoá thêm cặp
texttrênsurface(≥ 4.5:1 ở cả hai theme). Không đổi API và giá trị token.
- @tasco/theme —
theme — Bật ESLint cho
@tasco/theme—pnpm lintvà CI giờ lint cảsrc/*.tslẫn test Vitestsrc/*.test.tscủa themeKhông đổi API, giá trị token và file publish.
- Trước đây package không có script
lintnên Turbo bỏ qua theme khi chạypnpm lint. Giờ đã thêmeslint.config.mjstheo mẫu@tasco/utils(base+noDirectAntdv) và script"lint": "eslint .". noDirectAntdvkhoá nguyên tắc theme không important-design-vue(giữ zero runtime dep).- Thêm devDependencies
@tasco/eslint-config(workspace:*) vàeslint(^10.10.0), cùng version với các package khác. Code hiện có đã đạt lint, không phải sửa.
- Trước đây package không có script
ui — Sửa lỗi TS7022 (
'name' implicitly has type 'any') ở bước vite:dts khi build, do vòng suy luận type của vue-tsc vớiv-for="(_, name) in $slots"trong CForm/CTable. Chuyển sang lặpuseSlots()với type tường minh; hành vi forward slot (kể cả slotProps) giữ nguyên, có test kèm theo.ui — Sửa
index.d.tscủa@tasco/uilỗi TS2749 ($nextTick: nextTick); build Storybook không còn chạy plugin d.tsKhông đổi API và runtime. Type của
componentsgiữ nguyên ý nghĩa.- d.ts publish hỏng: map
components(install.ts) để TS tự suy luận nên d.ts in lại nguyên cây type từng component. Type củaCTablechứa instanceCButton(template ref), trong đó cótypeof import('vue').nextTick. api-extractor (rollupTypes) viết lại thành$nextTick: nextTick(mấttypeof), consumer đặtskipLibCheck: falsegặp TS2749. Naycomponentskhai báo type tường minh{ CTable: typeof CTable, … }:index.d.tshết lỗi và gọn từ 1815 xuống 1007 dòng. - Chốt chặn lúc build:
vite.config.tsbáo lỗi[@tasco/ui]khi d.ts sinh ra cótypeof import(...), không để phát hành d.ts hỏng. Test/story không còn sinh d.ts. - Storybook (build Storybook fail):
.storybook/main.tsbỏ pluginvite:dtskế thừa từvite.config.ts. Trước đây Storybook bỏbuild.libnên plugin chạy api-extractor lại trêndist/index.d.ts, ghi đè file đó và đổ.d.tsvàostorybook-static.
- d.ts publish hỏng: map
ui — Bỏ
eslint-disablekhỏi test của@tasco/ui; ngoại lệ lint cho stub antdv chuyển thành override có tên trongeslint.config.mjsKhông đổi API, component và file publish.
- 5 file test (
CChat,CFilterBar,CForm,CTable,CTableFilterDrawer) bỏ dòng/* eslint-disable vue/one-component-per-file */ở đầu file, đúng quy ước repo cấmeslint-disable. Directive trongCForm.test.tsvốn không dùng tới (ESLint báo "Unused eslint-disable directive"). - Override
tasco/ui-test-stubschỉ tắtvue/one-component-per-filecho**/*.test.ts, vì file test gom nhiều stub antdv trong factoryvi.mock('ant-design-vue'). Component.vue, story và code nguồn vẫn áp rule. - Stub
ATextarea/AAlerttrongCChat.test.tskhai báodefaultcho prop nên hết 2 cảnh báovue/require-default-prop.pnpm --filter @tasco/ui exec eslint . --max-warnings 0giờ sạch.
- 5 file test (