diff --git a/docs/plugin-sdk/05-external-install.md b/docs/plugin-sdk/05-external-install.md index a346e73..eb2f692 100644 --- a/docs/plugin-sdk/05-external-install.md +++ b/docs/plugin-sdk/05-external-install.md @@ -146,6 +146,72 @@ shasum -a 256 bundle.mjs # hoặc: python3 -c "import hashlib; print(hashlib.sha256(open('bundle.mjs','rb').read()).hexdigest())" ``` +### CSS: bundle plugin KHÔNG mang stylesheet của riêng nó + +Bundle plugin chỉ là **JavaScript** — không có file CSS nào đi kèm, và toàn +bộ giao diện ăn theo stylesheet Tailwind mà app chủ đã biên dịch sẵn. +Stylesheet đó sinh ra bằng cách quét `src/**` của **app chủ** +(`tailwind.config.js` → `content`), vốn không bao giờ chứa mã nguồn plugin +(plugin được cài lúc chạy từ một URL, không có mặt lúc app build). Hệ quả: +một class chỉ xuất hiện trong mã plugin sẽ nằm trong DOM mà **không có rule +nào phía sau** — im lặng không làm gì cả. + +- **Utility có tên** (`flex`, `p-2`, `text-xs`, `h-ctl`, token design-system): + an toàn trên thực tế — app chủ dùng chung bộ kit nên rule đã có sẵn. +- **Giá trị tuỳ ý (arbitrary value)** — `h-[68vh]`, `max-h-[70vh]`, + `grid-cols-[1fr_auto]`… — **không an toàn**: mỗi class là một rule riêng, + chỉ được phát sinh nếu tình cờ có file nào trong `src/**` của app chủ viết + y hệt từng ký tự. + +Vì vậy kích thước theo viewport hoặc theo pixel phải viết bằng inline +`style`, thứ không build step nào bỏ đi được: + +```tsx +// có thể không có rule nào trong stylesheet của app chủ +
+ +// luôn áp dụng + +``` + +**Đã có một cơ chế cho việc này từ trước:** +`src/styles/externalPluginClassnamesSafelist.ts` — bản chụp class trích ra MỘT +LẦN lúc bốn tool tách khỏi repo app chủ, tồn tại chỉ để Tailwind quét thấy. Nó +không hỏng, nó **cũ**: chính comment của nó đã báo trước "no automated +re-sync", và mã plugin đi tiếp sau ngày đó. Nếu bạn thêm class mới vào một +plugin, đừng cho rằng nó đã được phủ. + +**Utility có tên cũng không miễn nhiễm.** Rà soát cả bốn plugin bằng đúng +file CSS app chủ biên dịch ra cho thấy `bottom-3`, `pl-1.5`, `-ml-1.5`, +`py-5`, `h-64`, `m-4`, `mx-5`, `min-h-16`, `min-h-20`, `-mt-2.5`, +`align-top`, `translate-x-full` đều KHÔNG có rule — trong khi `max-h-64` nằm +ngay cạnh `h-64` thì có. Vì thế `src/styles/plugin-utilities.css` dùng +`@source inline(...)` ép Tailwind phát sinh sẵn các thang đo chung (spacing, +kích thước, vị trí, lưới, canh dọc) cho plugin dựa vào, dù không file nào +trong `src/**` dùng tới. Nó liệt kê theo THANG ĐO chứ không theo cách dùng, +nên không cần đọc mã plugin và không cũ đi được — bù đúng điểm yếu của bản +chụp. Hai file bổ sung nhau: bản chụp vẫn là thứ duy nhất phủ được các giá trị +tuỳ ý plugin đang dùng. + +Safelist đó chỉ có ở bản app chủ từ đây trở đi. **Plugin cài vào bản app chủ +nào người dùng đang có**, nên mã plugin vẫn phải chạy đúng trên bản cũ — và +không safelist nào phủ nổi giá trị tuỳ ý. Repo plugin +(`developer-desktop-miniapp`) kiểm bằng `scripts/check-host-classes.mjs`: +nó phân giải mọi class plugin viết ra dựa trên một file CSS app chủ thật, +chạy với bản phát hành cũ nhất còn hỗ trợ. + +```bash +# trong checkout app chủ +npm run build +# trong repo plugin +node scripts/check-host-classes.mjs ../developer-desktop-utils/dist/assets/*.css +``` + +Đây không phải lo xa: dialog xem log của Container Manager đã mất đúng hai +class kích thước, hộp log rơi về `height: auto`, phình theo từng dòng log đổ +về và đẩy dialog tràn khỏi cả trên lẫn dưới màn hình — thanh công cụ của +chính nó không với tới được. + ## Build một sidecar đúng chuẩn để publish Xem [04-tier-b-sidecars.md](./04-tier-b-sidecars.md) cho cách viết sidecar. diff --git a/src/styles/globals.css b/src/styles/globals.css index e702465..86a5bff 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -2,6 +2,8 @@ @import "../design-system/tokens.css"; @import "tailwindcss"; +/* Thang utility bảo đảm cho plugin cài lúc chạy — xem file đó. */ +@import "./plugin-utilities.css"; /* Keep the JS config as the source of truth for now: it carries the portable design-kit preset chain (design/tailwind-preset.cjs), the colour vocabulary and the animate plugin. Migrating it to CSS `@theme` is a separate step. */ diff --git a/src/styles/plugin-utilities.css b/src/styles/plugin-utilities.css new file mode 100644 index 0000000..f1c63d5 --- /dev/null +++ b/src/styles/plugin-utilities.css @@ -0,0 +1,61 @@ +/* ============================================================================ + Bề mặt utility bảo đảm cho plugin cài từ bên ngoài + ---------------------------------------------------------------------------- + Plugin (Settings → Extensions) được build thành MỘT file ESM và **không mang + CSS của riêng nó** — xem `docs/plugin-sdk/05-external-install.md`. Toàn bộ + giao diện plugin ăn theo stylesheet này, vốn sinh ra bằng cách quét `src/**` + của app chủ. Mã nguồn plugin không có mặt lúc app build (nó được tải lúc + chạy từ một URL), nên trước file này, một class chỉ có trong plugin sẽ nằm + trong DOM mà **không có rule nào phía sau** — im lặng không làm gì cả. + + Điều đó biến "app chủ có tình cờ dùng class này ở đâu đó không" thành hợp + đồng API ngầm, và nó đã gãy thật: Container Manager mất `bottom-3` (nút + "Jump to latest" rơi lên đỉnh khung log), `pl-1.5`/`-ml-1.5`/`border-bad/60` + (vạch đỏ đánh dấu dòng stderr biến mất); Kafka Explorer mất `h-64`, `m-4`, + `py-5`, `translate-x-full`; Redis mất `min-h-16`, `mx-5`, `align-top`; + RabbitMQ mất `py-5`, `min-h-16`, `min-h-20`, `-mt-2.5`. + + ĐÃ CÓ MỘT CƠ CHẾ TRƯỚC FILE NÀY: `externalPluginClassnamesSafelist.ts` — + một bản chụp class được trích ra MỘT LẦN lúc bốn tool tách khỏi repo này + (commit 1b59500 / 5ad6ba2), tồn tại chỉ để Tailwind quét thấy. Nó không hỏng; + nó CŨ. Chính comment của nó đã báo trước: "no automated re-sync". Mã plugin + đi tiếp sau ngày đó, thêm class mới, và không có gì đồng bộ lại — đó là lý do + chính xác vì sao `bottom-3`, `py-5`, `align-top`, `translate-x-full`, + `min-h-16` không có rule, trong khi `max-w-[9rem]` hay + `grid-cols-[3rem_minmax(0,1fr)_6.5rem_5.5rem]` (có trong bản chụp) thì có. + + `@source inline(...)` dưới đây bù đúng điểm yếu đó cho phần THANG ĐO CHUNG: + nó liệt kê theo thang, không theo cách dùng, nên không cần đọc mã plugin và + không bao giờ cũ đi. Hai file bổ sung nhau, không thay thế nhau — bản chụp + kia vẫn là thứ duy nhất phủ được các giá trị tuỳ ý plugin đang dùng. + Giữ danh sách ở mức THANG ĐO CHUNG (khoảng cách, kích thước, vị trí) — + những thứ một layout bất kỳ cũng cần. Không thêm class trang trí vào đây: + màu, bóng, bo góc của plugin phải đến từ từ vựng design-system, thứ app chủ + vốn đã dùng khắp nơi. + + KHÔNG thang đo nào liệt kê nổi giá trị tuỳ ý (`max-w-[12rem]`, + `grid-cols-[minmax(...)]`) — mỗi cái là một rule riêng. Chúng vẫn phụ thuộc + bản chụp kia, tức vẫn cũ đi được; nên plugin phải viết chúng bằng inline + `style`, và repo plugin có test chặn lại (`ui/hostCssClasses.test.ts`). + ========================================================================== */ + +/* Thang spacing: margin/padding mọi phía, gap, và bù âm cho margin. */ +@source inline("{,-}m{,x,y,s,e,t,r,b,l}-{0,0.5,1,1.5,2,2.5,3,3.5,4,5,6,7,8,9,10,11,12,14,16,20,24}"); +@source inline("p{,x,y,s,e,t,r,b,l}-{0,0.5,1,1.5,2,2.5,3,3.5,4,5,6,7,8,9,10,11,12,14,16,20,24}"); +@source inline("gap{,-x,-y}-{0,0.5,1,1.5,2,2.5,3,3.5,4,5,6,8,10,12}"); + +/* Thang kích thước + các từ khoá nội tại hay dùng để dựng pane cuộn. */ +@source inline("{w,h,size,min-w,min-h,max-w,max-h}-{0,px,0.5,1,1.5,2,2.5,3,3.5,4,5,6,7,8,9,10,11,12,14,16,20,24,28,32,36,40,44,48,56,64,72,80,96}"); +@source inline("{w,h,min-w,min-h,max-w,max-h}-{full,screen,fit,min,max,none,auto}"); + +/* Vị trí cho overlay/badge/nút nổi — chỗ `bottom-3` đã gãy. */ +@source inline("{,-}{inset,inset-x,inset-y,top,right,bottom,left,start,end}-{0,px,0.5,1,1.5,2,2.5,3,3.5,4,5,6,8,10,12,full,auto}"); +@source inline("{,-}{inset,inset-x,inset-y,top,right,bottom,left}-{1/2,1/3,2/3,1/4,3/4}"); + +/* Biến đổi dùng cho panel trượt và canh giữa tuyệt đối. */ +@source inline("{,-}translate-{x,y}-{0,px,0.5,1,1.5,2,2.5,3,4,6,8,full,1/2}"); + +/* Lưới và canh dọc — `grid-cols-N` tuỳ ý và `align-top` đều từng thiếu. */ +@source inline("grid-{cols,rows}-{1,2,3,4,5,6,7,8,9,10,11,12,none}"); +@source inline("col-span-{1,2,3,4,5,6,full}"); +@source inline("align-{baseline,top,middle,bottom,text-top,text-bottom,sub,super}");