Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions docs/plugin-sdk/05-external-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Comment thread
pullfrog[bot] marked this conversation as resolved.
- **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ủ
<div className="flex h-[68vh] max-h-[calc(100vh-13rem)] flex-col" />

// luôn áp dụng
<div className="flex flex-col" style={{ height: '68vh', maxHeight: 'calc(100vh - 13rem)' }} />
```

**Đã 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.
Expand Down
2 changes: 2 additions & 0 deletions src/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
61 changes: 61 additions & 0 deletions src/styles/plugin-utilities.css
Original file line number Diff line number Diff line change
@@ -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}");
Loading