@fui-org/fui-cli 1.3.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +19 -3
  2. package/dist/fui-bmg5pnmq.js +379 -0
  3. package/dist/fui.js +1 -1
  4. package/package.json +7 -6
  5. package/skills/fui/SKILL.md +9 -41
  6. package/skills/fui-skill/SKILL.md +95 -225
  7. package/skills/fui-skill/assets/projectdefaultstyle-3.0.css +555 -0
  8. package/skills/fui-skill/assets/projectdefaultstyle.css +207 -235
  9. package/skills/fui-skill/references/INDEX.md +105 -137
  10. package/skills/fui-skill/references/advanced-techniques.md +76 -68
  11. package/skills/fui-skill/references/coding-standards.md +56 -56
  12. package/skills/fui-skill/references/component-design.md +166 -173
  13. package/skills/fui-skill/references/component-quickref.md +61 -60
  14. package/skills/fui-skill/references/component-table.md +128 -117
  15. package/skills/fui-skill/references/components-dialog.md +55 -56
  16. package/skills/fui-skill/references/components-display.md +24 -30
  17. package/skills/fui-skill/references/components-echart.md +186 -261
  18. package/skills/fui-skill/references/components-input.md +72 -96
  19. package/skills/fui-skill/references/controls-patterns.md +196 -342
  20. package/skills/fui-skill/references/controls-styling-vocabulary.md +130 -97
  21. package/skills/fui-skill/references/db-table-design.md +24 -28
  22. package/skills/fui-skill/references/db-workflow.md +191 -390
  23. package/skills/fui-skill/references/default-function.md +169 -128
  24. package/skills/fui-skill/references/design-modes.md +35 -63
  25. package/skills/fui-skill/references/echart-templates.md +204 -196
  26. package/skills/fui-skill/references/fastproject.md +62 -60
  27. package/skills/fui-skill/references/fsheet.md +109 -124
  28. package/skills/fui-skill/references/fullstack-workflow.md +90 -128
  29. package/skills/fui-skill/references/module-data-patterns.md +31 -40
  30. package/skills/fui-skill/references/module-json-anatomy.md +47 -52
  31. package/skills/fui-skill/references/module-structure.md +80 -196
  32. package/skills/fui-skill/references/new-session.md +49 -51
  33. package/skills/fui-skill/references/pdfmake.md +17 -17
  34. package/skills/fui-skill/references/permission-system.md +89 -108
  35. package/skills/fui-skill/references/platform-architecture.md +128 -153
  36. package/skills/fui-skill/references/project-config.md +102 -134
  37. package/skills/fui-skill/references/project-provisioning.md +139 -244
  38. package/skills/fui-skill/references/script-map.md +208 -242
  39. package/skills/fui-skill/references/sql-clr-functions.md +98 -97
  40. package/skills/fui-skill/references/system-design.md +63 -88
  41. package/skills/fui-skill/references/tapi-file-api.md +46 -52
  42. package/skills/fui-skill/references/tapi-permission-patterns.md +51 -53
  43. package/skills/fui-skill/references/tapi-reference.md +132 -207
  44. package/skills/fui-skill/references/tools-registry.md +84 -460
  45. package/skills/fui-skill/references/ui-crosswindow-patterns.md +79 -75
  46. package/skills/fui-skill/references/ui-dialog-patterns.md +98 -72
  47. package/skills/fui-skill/references/ui-layout-patterns.md +26 -26
  48. package/skills/fui-skill/references/ui-patterns.md +71 -83
  49. package/skills/fui-skill/references/ui-screenshot-review.md +63 -62
  50. package/skills/fui-skill/references/ui-table-cell-patterns.md +61 -59
  51. package/skills/fui-skill/references/ui-templates.md +16 -23
  52. package/skills/fui-skill/references/verification.md +236 -246
  53. package/skills/fui-skill/references/watcher-patterns.md +30 -63
  54. package/skills/fui-skill/references/websocket-realtime.md +83 -66
  55. package/skills/fui-skill/scripts/component-3.0.js +298 -131
  56. package/skills/fui-skill/scripts/component.js +277 -271
  57. package/skills/fui-skill/scripts/componentTable-3.0.js +182 -53
  58. package/skills/fui-skill/scripts/componentTable.js +171 -49
  59. package/skills/fui-skill/scripts/defaultfunction-3.0.js +88 -3
  60. package/skills/fui-skill/scripts/defaultfunction.js +88 -3
  61. package/skills/fui-skill/scripts/fsheet.js +38 -0
  62. package/dist/fui-x7kph78r.js +0 -420
  63. package/skills/fui-skill/README.md +0 -112
  64. package/skills/fui-skill/metadata.json +0 -75
  65. package/skills/fui-skill-cli/SKILL.md +0 -139
  66. package/skills/fui-skill-cli/references/INDEX.md +0 -110
  67. package/skills/fui-skill-cli/references/advanced-techniques.md +0 -168
  68. package/skills/fui-skill-cli/references/coding-standards.md +0 -112
  69. package/skills/fui-skill-cli/references/component-design.md +0 -448
  70. package/skills/fui-skill-cli/references/component-quickref.md +0 -78
  71. package/skills/fui-skill-cli/references/component-table.md +0 -248
  72. package/skills/fui-skill-cli/references/components-dialog.md +0 -191
  73. package/skills/fui-skill-cli/references/components-display.md +0 -141
  74. package/skills/fui-skill-cli/references/components-echart.md +0 -316
  75. package/skills/fui-skill-cli/references/components-input.md +0 -335
  76. package/skills/fui-skill-cli/references/controls-patterns.md +0 -701
  77. package/skills/fui-skill-cli/references/controls-styling-vocabulary.md +0 -137
  78. package/skills/fui-skill-cli/references/db-table-design.md +0 -73
  79. package/skills/fui-skill-cli/references/db-workflow.md +0 -288
  80. package/skills/fui-skill-cli/references/default-function.md +0 -425
  81. package/skills/fui-skill-cli/references/design-modes.md +0 -57
  82. package/skills/fui-skill-cli/references/echart-templates.md +0 -489
  83. package/skills/fui-skill-cli/references/fastproject.md +0 -99
  84. package/skills/fui-skill-cli/references/fsheet.md +0 -203
  85. package/skills/fui-skill-cli/references/fullstack-workflow.md +0 -313
  86. package/skills/fui-skill-cli/references/module-data-patterns.md +0 -117
  87. package/skills/fui-skill-cli/references/module-json-anatomy.md +0 -132
  88. package/skills/fui-skill-cli/references/module-structure.md +0 -141
  89. package/skills/fui-skill-cli/references/new-session.md +0 -85
  90. package/skills/fui-skill-cli/references/pdfmake.md +0 -60
  91. package/skills/fui-skill-cli/references/permission-system.md +0 -150
  92. package/skills/fui-skill-cli/references/platform-architecture.md +0 -269
  93. package/skills/fui-skill-cli/references/project-config.md +0 -303
  94. package/skills/fui-skill-cli/references/project-provisioning.md +0 -278
  95. package/skills/fui-skill-cli/references/script-map.md +0 -262
  96. package/skills/fui-skill-cli/references/sql-clr-functions.md +0 -225
  97. package/skills/fui-skill-cli/references/system-design.md +0 -89
  98. package/skills/fui-skill-cli/references/tapi-file-api.md +0 -185
  99. package/skills/fui-skill-cli/references/tapi-permission-patterns.md +0 -156
  100. package/skills/fui-skill-cli/references/tapi-reference.md +0 -474
  101. package/skills/fui-skill-cli/references/tools-registry.md +0 -84
  102. package/skills/fui-skill-cli/references/ui-crosswindow-patterns.md +0 -321
  103. package/skills/fui-skill-cli/references/ui-dialog-patterns.md +0 -255
  104. package/skills/fui-skill-cli/references/ui-layout-patterns.md +0 -176
  105. package/skills/fui-skill-cli/references/ui-patterns.md +0 -303
  106. package/skills/fui-skill-cli/references/ui-screenshot-review.md +0 -95
  107. package/skills/fui-skill-cli/references/ui-table-cell-patterns.md +0 -318
  108. package/skills/fui-skill-cli/references/ui-templates.md +0 -22
  109. package/skills/fui-skill-cli/references/verification.md +0 -236
  110. package/skills/fui-skill-cli/references/watcher-patterns.md +0 -163
  111. package/skills/fui-skill-cli/references/websocket-realtime.md +0 -271
@@ -1,63 +1,61 @@
1
1
  # FUI Vue Component Design Rules
2
2
 
3
- > File này sở hữu: **quy tắc viết `uc-*.vue`: scope module/project, cấm `name:`/`components:{}`, props/emits/slots, cấm `_injectStyle`, quy tắc scope thủ công cho `<style scoped>`, quy trình kiểm chứng component (`component_preview` + luật push component cấp project trước khi render module), và quy ước `{projectId}/COMPONENTS.md` (tra component project-scope đã có + vòng tích luỹ)**. Bộ class/prop được duyệt xem [controls-styling-vocabulary.md](controls-styling-vocabulary.md).
3
+ > Owns: **`uc-*.vue` rules: module/project scope, no `name:`/`components:{}`, props/emits/slots, no `_injectStyle`, manual `<style scoped>` scoping, verification (`fui component preview`, push project components before module render), `{projectId}/COMPONENTS.md`**. Approved classes/props: [controls-styling-vocabulary.md](controls-styling-vocabulary.md).
4
4
 
5
- Quy tắc bắt buộc khi viết bất kỳ Vue component nào (`uc-*.vue`) trong hệ thống FUI. Áp dụng cho cả component thông thường lẫn dialog component.
5
+ Mandatory for every `uc-*.vue`, dialog components included.
6
6
 
7
7
  ---
8
8
 
9
- ## 1. Hai cấp component: module-scope vs project-scope
9
+ ## 1. Two scopes: module vs project
10
10
 
11
- | | Module-scope | Project-scope (global) |
12
- |---|---|---|
13
- | **Vị trí file** | `{projectId}/modules/{moduleId}/components/uc-*.vue` | `{projectId}/components/uc-*.vue` |
14
- | **Registry** | `modules/{moduleId}/components/_components.json` | `{projectId}/components/_components.json` |
15
- | **Phạm vi** | Chỉ trong module đó | **Tất cả modules trong project** |
16
- | **Auto-load** | Trong module đã có component | Tự động — **không cần khai báo gì thêm** |
17
- | **Khi nào dùng** | Component chỉ dùng ở một module | Component dùng lại ở nhiều module |
11
+ | | Module-scope | Project-scope (global) |
12
+ | -------------- | ---------------------------------------------------- | ----------------------------------------- |
13
+ | **File** | `{projectId}/modules/{moduleId}/components/uc-*.vue` | `{projectId}/components/uc-*.vue` |
14
+ | **Registry** | `modules/{moduleId}/components/_components.json` | `{projectId}/components/_components.json` |
15
+ | **Visible in** | That module only | **All modules in the project** |
16
+ | **Auto-load** | In its module | Automatic — **nothing to declare** |
17
+ | **Use when** | Used by one module | Reused across modules |
18
18
 
19
- > **Project-scope component được FUI tự động load ở cấp global** — bất kỳ module nào trong project đều dùng được `<uc-*>` ngay, không cần tạo lại hay khai báo thêm.
20
-
21
- ### Quy tắc chọn scope
19
+ ### Choosing scope
22
20
 
23
21
  ```
24
22
  Component này sẽ dùng ở nhiều module? → project-scope
25
23
  Component chỉ dùng trong một module? → module-scope
26
24
  ```
27
25
 
28
- ### Bảng thao tác theo scope
26
+ ### Commands per scope
29
27
 
30
- | Thao tác | Module-scope | Project-scope (global) |
31
- |---|---|---|
32
- | **Tạo mới** (chưa có trên server) | `component_new(componentName)` — dùng session.moduleId | `component_new(componentName, projectId=<pid>)` — KHÔNG có moduleId |
33
- | **Cập nhật** (đã có trên server) | `component_update(componentName)` — dùng session.moduleId | `component_update(componentName, projectId=<pid>)` — KHÔNG có moduleId |
34
- | **Xóa** | `component_delete(componentName)` — dùng session.moduleId | `component_delete(componentName, projectId=<pid>)` — KHÔNG có moduleId |
35
- | **Đọc source** | `component_get(projectId, moduleId, componentName)` | `component_get(projectId, componentName)` — bỏ moduleId |
36
- | **Sync danh sách** | `component_list(projectId, moduleId)` | `component_list(projectId)` — bỏ moduleId |
37
- | **File trong workspace** | `{moduleDir}/components/uc-foo.vue` | `{projectDir}/components/uc-foo.vue` |
28
+ | Operation | Module-scope | Project-scope (global) |
29
+ | -------------------------- | --------------------------------------------------- | -------------------------------------------------- |
30
+ | **Create** (not on server) | `fui component new <name>` — uses session module | `fui component new <name> -p <pid>` — NO module |
31
+ | **Update** (on server) | `fui component update <name>` — uses session module | `fui component update <name> -p <pid>` — NO module |
32
+ | **Delete** | `fui component delete <name>` — uses session module | `fui component delete <name> -p <pid>` — NO module |
33
+ | **Read source** | `fui component get <name> -p <pid> -m <mid>` | `fui component get <name> -p <pid>` — omit module |
34
+ | **Sync list** | `fui component list -p <pid> -m <mid>` | `fui component list -p <pid>` — omit module |
35
+ | **Workspace file** | `{moduleDir}/components/uc-foo.vue` | `{projectDir}/components/uc-foo.vue` |
38
36
 
39
- **Quy tắc duy nhất:** `moduleId` có → module-scope · `moduleId` vắng, chỉ `projectId` → project-scope
37
+ **Single rule:** module given → module-scope · no module, only project → project-scope. Flags: `fui component --help`.
40
38
 
41
- **Tạo và cập nhật là HAI tool riêng — không có upsert ngầm.** Cả hai đọc file `.vue` **local** (file phải tồn tại sẵn trên đĩa) và tự suy `ComID` từ `_components.json`, không truyền tay:
39
+ **Create ≠ update — no implicit upsert.** Both read the **local** `.vue` (must exist on disk) and derive `ComID` from `_components.json` (never pass it):
42
40
 
43
- - `component_new` — chỉ INSERT. Làm mới `_components.json` **từ server trước** rồi mới kiểm tra tên; trùng tên = **lỗi cứng**. (Cache cũ mà cứ INSERT thì server sinh **hai record cùng tên**, không sửa được từ MCP.) Sau khi tạo tự sync lại để lấy `ComID` thật.
44
- - `component_update` — chỉ UPDATE. Không tra được `ComID` = **lỗi cứng**, không âm thầm tạo mới: hoặc component chưa từng được tạo (→ `component_new`), hoặc cache local cũ (→ `component_list` rồi thử lại).
41
+ - `fui component new` — INSERT only. Refreshes `_components.json` **from the server first**, then checks the name; duplicate = **hard error** (INSERT on a stale cache creates **two same-name records**, unfixable from fui). Re-syncs after to get the real `ComID`.
42
+ - `fui component update` — UPDATE only. No `ComID` = **hard error**, never creates: never created (→ `fui component new`) or stale cache (→ `fui component list`, retry).
45
43
 
46
- **`component_delete` tự lookup ComID** từ `_components.json` local. Nếu danh sách lệch, chạy `component_list` trước.
44
+ `fui component delete` looks up ComID in local `_components.json`; stale → `fui component list` first.
47
45
 
48
- **`module_publish_html` không bao gồm project-scope component** — nếu có thay đổi component global, phải gọi `component_new`/`component_update` riêng trước hoặc sau.
46
+ **`fui module publish html` excludes project-scope components** — push them separately (`fui component new`/`update`).
49
47
 
50
48
  ---
51
49
 
52
- ## 1a. Tra cứu component project-scope đã có — `{projectId}/COMPONENTS.md`
50
+ ## 1a. Existing project components — `{projectId}/COMPONENTS.md`
53
51
 
54
- Trước khi thiết kế một component cấp project mới, **phải kiểm tra `{projectId}/COMPONENTS.md`** (nếu có) — file này liệt kê các `uc-*.vue` cấp project đã tồn tại, kèm tóm tắt mục đích và props chính. Dựng lại component đã có chỉ gây trùng lặp và khó bảo trì.
52
+ Before designing a project-scope component, **check `{projectId}/COMPONENTS.md`** (if present): existing project `uc-*.vue` with purpose and main props. Don't rebuild one that exists.
55
53
 
56
54
  ```
57
- {FUI_MCP_WORKDIR}/{projectId}/COMPONENTS.md ← kiểm tra TRƯỚC khi tạo component project-scope mới
55
+ {workspace}/{projectId}/COMPONENTS.md ← kiểm tra TRƯỚC khi tạo component project-scope mới
58
56
  ```
59
57
 
60
- **Vòng tích luỹ:** sau khi tạo/cập nhật một component cấp project có ý định dùng lại ở nhiều module, nối thêm một mục vào `{projectId}/COMPONENTS.md`. Chưa có file thì tạo mới theo khuôn:
58
+ **Accumulation:** after creating/updating a reusable project component, append an entry; no file → create with:
61
59
 
62
60
  ```markdown
63
61
  # {projectId} — Danh sách component project-scope
@@ -70,24 +68,21 @@ Trước khi thiết kế một component cấp project mới, **phải kiểm t
70
68
  **Ghi chú:** điều gì cần biết khi dùng (nếu có)
71
69
  ```
72
70
 
73
- > Khuôn này theo đúng `{projectId}/DESIGN.md` (vòng tích luỹ quy tắc thẩm mỹ — xem [design-modes.md](design-modes.md) §2-3). File `COMPONENTS.md` **không bao giờ tự xoá mục** — xoá khi đã dùng `component_delete` bỏ component đó khỏi server.
71
+ > Mirrors `{projectId}/DESIGN.md` ([design-modes.md](design-modes.md) §2-3). **Never auto-remove entries** — remove only after `fui component delete` removed it from the server.
74
72
 
75
73
  ---
76
74
 
77
- ## 2. Đặt tên và cấu trúc file
75
+ ## 2. Naming and file layout
78
76
 
79
- - **Prefix bắt buộc**: `uc-` (User Component) — phân biệt với `f-*` (FUI runtime) và `v-*` (Vuetify)
80
- - **Format**: kebab-case — ví dụ `uc-trangthai-sukien.vue`, `uc-user-profile.vue`
81
- - **Vị trí**: chỉ đặt trong `components/` ở root module hoặc root project, không tạo subfolder
82
- - **Registry**: khai báo trong `components/_components.json` (chỉ cần `comName`, không tự đặt `comID`)
77
+ - Prefix `uc-` (User Component; vs `f-*` FUI runtime, `v-*` Vuetify), kebab-case: `uc-trangthai-sukien.vue`, `uc-user-profile.vue`
78
+ - Only in `components/` at module or project root; no subfolders
79
+ - Registry `components/_components.json`: only `comName`, never set `comID`
83
80
 
84
81
  ---
85
82
 
86
- ## 3. Đăng ký component — KHÔNG dùng `components: {}`, KHÔNG dùng `name:`
87
-
88
- FUI tự động scan và đăng ký toàn bộ `uc-*.vue` ở cấp **global**. Tuyệt đối không khai báo `components: { ... }` bên trong một component — nó không hoạt động trong môi trường FUI.
83
+ ## 3. Registration — NO `components: {}`, NO `name:`
89
84
 
90
- Không khai báo thuộc tính `name:` trong `export default` — FUI tự xử lý tên component từ tên file khi tạo script, khai báo thêm là thừa và có thể gây xung đột:
85
+ FUI auto-registers every `uc-*.vue` **globally**. Never declare `components: { ... }` (doesn't work in FUI) or `name:` (FUI derives it from the filename; may conflict):
91
86
 
92
87
  ```js
93
88
  // ❌ SAI — không cần và có thể gây xung đột
@@ -105,8 +100,8 @@ export default {
105
100
  ```js
106
101
  // ❌ SAI — không dùng trong FUI
107
102
  export default {
108
- components: { 'uc-user-card': UcUserCard },
109
- }
103
+ components: { "uc-user-card": UcUserCard },
104
+ };
110
105
 
111
106
  // ✅ ĐÚNG — dùng trực tiếp tag trong template
112
107
  // <uc-user-card :data="data" />
@@ -116,9 +111,8 @@ export default {
116
111
 
117
112
  ## 4. Props
118
113
 
119
- - Khai báo trong `script`: camelCase (`userName`, `itemList`)
120
- - Dùng trong `template`: kebab-case (`:user-name="userName"`)
121
- - Default của Array/Object phải dùng `function()`, không dùng arrow function:
114
+ - `script`: camelCase (`userName`, `itemList`); `template`: kebab-case (`:user-name="userName"`)
115
+ - Array/Object defaults use `function()`, not arrow functions:
122
116
 
123
117
  ```js
124
118
  // ❌ SAI
@@ -128,24 +122,23 @@ props: { items: { default: () => [] } }
128
122
  props: { items: { default: function() { return [] } } }
129
123
  ```
130
124
 
131
- - Ưu tiên tên props trung lập và tái sử dụng được: `items`, `value`, `label`, `loading`, `readonly`, `disabled`, `options`, `config`
132
- - Không đặt tên props gắn chặt với một màn hình cụ thể nếu component có thể dùng lại
125
+ - Prefer neutral reusable names (`items`, `value`, `label`, `loading`, `readonly`, `disabled`, `options`, `config`), not screen-specific ones
133
126
 
134
127
  ---
135
128
 
136
129
  ## 5. Emits
137
130
 
138
- - Luôn emit lên parent thay vì mutate trực tiếp state của parent
139
- - Các event chuẩn: `input`, `change`, `select`, `submit`, `remove`, `action`
140
- - Dialog component luôn emit `('input', false)` để đóng — tương thích với `v-model` Vue 2
131
+ - Emit to the parent; never mutate parent state
132
+ - Standard: `input`, `change`, `select`, `submit`, `remove`, `action`
133
+ - Dialog components always close with `('input', false)` — Vue 2 `v-model` compatible
141
134
 
142
135
  ---
143
136
 
144
- ## 6. Template syntax — các ràng buộc bắt buộc
137
+ ## 6. Template syntax constraints
145
138
 
146
- ### Không dùng backtick trong `<template>`
139
+ ### No backticks in `<template>`
147
140
 
148
- Template strings (backtick) không hoạt động đúng trong Vue 2 template. Dùng string concatenation:
141
+ Breaks in Vue 2 templates; concatenate:
149
142
 
150
143
  ```html
151
144
  <!-- ❌ SAI -->
@@ -155,11 +148,11 @@ Template strings (backtick) không hoạt động đúng trong Vue 2 template. D
155
148
  :label="'Total (' + items.length + ')'"
156
149
  ```
157
150
 
158
- ### Không dùng `@` shorthand cho events trong JSON
151
+ ### No `@` shorthand in JSON
159
152
 
160
- Trong `module.json` luôn dùng `v-on:click`, không dùng `@click`. Trong `.vue` template thì `@click` vẫn hợp lệ.
153
+ `module.json`: always `v-on:click`, never `@click`. `.vue` templates: `@click` is fine.
161
154
 
162
- ### Vue 2 yêu cầu một root element duy nhất
155
+ ### Vue 2 needs a single root element
163
156
 
164
157
  ```html
165
158
  <!-- ✅ ĐÚNG -->
@@ -172,13 +165,13 @@ Trong `module.json` luôn dùng `v-on:click`, không dùng `@click`. Trong `.vue
172
165
 
173
166
  ---
174
167
 
175
- ## 7. Style & class — ƯU TIÊN class Vuetify có sẵn, KHÔNG tự chế
168
+ ## 7. Style & class — use existing Vuetify classes, DON'T invent
176
169
 
177
- **Quy tắc: dùng class tiện ích Vuetify có sẵn cho MỌI nhu cầu spacing/flex/text/màu/kích thước. Chỉ định nghĩa class mới khi thật đặc biệt** (hiệu ứng/layout Vuetify không có). AI hay lạm dụng class tự chế → lãng phí, phá tính nhất quán, khó bảo trì.
170
+ **Use Vuetify utility classes for ALL spacing/flex/text/color/size. New class only when truly special** (effect/layout Vuetify lacks) — invented classes break consistency.
178
171
 
179
- ### ❌ TUYỆT ĐỐI KHÔNG inject CSS bằng JavaScript
172
+ ### ❌ NEVER inject CSS via JavaScript
180
173
 
181
- Không tạo `<style>` bằng JS rồi chèn vào `document.head` (kiểu `_injectStyle`) — đây là anti-pattern nặng (vừa lách luật no-`<style>`, vừa tạo hàng chục class tự chế):
174
+ No `<style>` created in JS and appended to `document.head` (`_injectStyle`) — severe anti-pattern (dodges the no-`<style>` rule, spawns invented classes):
182
175
 
183
176
  ```js
184
177
  // ❌ SAI NGHIÊM TRỌNG — không bao giờ làm thế này trong .vue
@@ -191,10 +184,10 @@ methods: {
191
184
  }
192
185
  ```
193
186
 
194
- `<style scoped>` trong `.vue` được phép dùng cho CSS tùy chỉnh (hiếm khi cần) — nhưng `scoped` **không có tác dụng scoping thật**: FUI chỉ nối text `ComCSS` của mọi component trong module thành MỘT stylesheet chung (`ComponentCSSBuild()`), không compile SFC, không gắn `data-v-xxxx`. Vì vậy **bắt buộc tự scope thủ công**:
187
+ `<style scoped>` is allowed for custom CSS (rarely needed), but **does not scope**: FUI concatenates every module component's `ComCSS` into ONE stylesheet (`ComponentCSSBuild()`) — no SFC compile, no `data-v-xxxx`. **Scope manually**:
195
188
 
196
- 1. Thẻ gốc trong `<template>` mang một class định danh duy nhất theo tên component (kebab-case) — ví dụ component `uc-invoice-list` thì thẻ gốc `class="uc-invoice-list"`.
197
- 2. MỌI selector trong `<style scoped>` phải là descendant bắt đầu từ class đó — **không viết selector trần** (`.title`, `.header`...) vì sẽ leak sang mọi component khác cùng module.
189
+ 1. Template root gets a class = component name (`uc-invoice-list` → `class="uc-invoice-list"`).
190
+ 2. EVERY selector starts from that class — **no bare selectors** (`.title`, `.header`...); they leak into every component in the module.
198
191
 
199
192
  ```vue
200
193
  <template>
@@ -207,58 +200,57 @@ methods: {
207
200
  </template>
208
201
 
209
202
  <style scoped>
210
- .uc-invoice-list__toolbar { display: flex; align-items: center; justify-content: space-between; }
211
- .uc-invoice-list__title { font-weight: 600; font-size: 14px; }
212
- .uc-invoice-list__table { margin-top: 8px; }
203
+ .uc-invoice-list__toolbar {
204
+ display: flex;
205
+ align-items: center;
206
+ justify-content: space-between;
207
+ }
208
+ .uc-invoice-list__title {
209
+ font-weight: 600;
210
+ font-size: 14px;
211
+ }
212
+ .uc-invoice-list__table {
213
+ margin-top: 8px;
214
+ }
213
215
  </style>
214
216
  ```
215
217
 
216
218
  ```vue
217
219
  <!-- ❌ SAI — selector trần, đụng với component khác cùng module có class .title/.toolbar -->
218
220
  <style scoped>
219
- .title { font-weight: 600; }
220
- .toolbar { display: flex; }
221
+ .title {
222
+ font-weight: 600;
223
+ }
224
+ .toolbar {
225
+ display: flex;
226
+ }
221
227
  </style>
222
228
  ```
223
229
 
224
- CSS toàn-module (không gắn riêng component nào) → đặt trong `style.css` (publish qua `module_publish_css`), không phải `header.html`. `header.html` vẫn hợp lệ nhưng không còn là lựa chọn duy nhất.
225
-
226
- ### Trước khi đặt một class mới → tra bộ từ vựng đã duyệt
230
+ Module-wide CSS → `style.css` (`fui module publish css`); `header.html` still works but isn't the only option.
227
231
 
228
- Bảng đầy đủ class/prop Vuetify được phép dùng (spacing, flex, text, màu, bo góc, elevation,
229
- breakpoint, kích thước) + thứ tự ưu tiên + điều kiện được định nghĩa class riêng:
230
- → [controls-styling-vocabulary.md](controls-styling-vocabulary.md) §3.
232
+ ### Before adding a class → check the approved vocabulary
231
233
 
232
- Bộ từ vựng đó áp dụng y nguyên trong `<template>` của `.vue` — **khác biệt duy nhất**: trong `.vue`
233
- không có `w`/`col` của grid FUI (đó là cú pháp của `module.json`, runtime FUI dịch ra class), nên
234
- phải viết grid Vuetify trực tiếp.
234
+ Allowed Vuetify classes/props (spacing, flex, text, color, radius, elevation, breakpoint, size), priority, when a custom class is allowed → [controls-styling-vocabulary.md](controls-styling-vocabulary.md) §3. Applies unchanged in `.vue`, **except** no FUI grid `w`/`col` (`module.json` syntax translated by the runtime) — write the Vuetify grid directly.
235
235
 
236
- **Cú pháp grid KHÁC NHAU giữa hai framework** — FUI V2 dùng Vuetify 1.5, FUI V3 dùng Vuetify 3
237
- (canonical: [controls-patterns.md](controls-patterns.md) §Vuetify version):
236
+ **Grid syntax DIFFERS by framework** — FUI V2 = Vuetify 1.5, FUI V3 = Vuetify 3 (canonical: [controls-patterns.md](controls-patterns.md) §Vuetify version):
238
237
 
239
- | | V2 (Vuetify 1.5) | V3 (Vuetify 3) |
240
- |---|---|---|
241
- | Cách chia cột | `v-layout` > `v-flex xs8` | `v-row` > `v-col cols="8"` |
242
- | `v-flex` | ✅ | ❌ **không tồn tại** — thẻ rơi xuống thành phần tử HTML trơ: không lỗi, không `[Vue warn]`, chỉ là **layout mất hẳn** |
243
- | `v-layout` | ✅ hàng của grid | ⚠️ **có tồn tại nhưng nghĩa khác hẳn**: là wrapper bố cục ứng dụng (đi với `v-main`, như `fastproject-3.0.js` dùng), KHÔNG phải hàng của grid. Dùng nó thay `v-row` thì được một flex container không có gutter/breakpoint của grid |
244
- | prop trần `shrink` | ✅ (prop của `v-flex`) | ❌ không có prop đó → rơi thành attribute DOM trơ. Dùng `class="shrink"` |
238
+ | | V2 (Vuetify 1.5) | V3 (Vuetify 3) |
239
+ | ------------------ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
240
+ | Columns | `v-layout` > `v-flex xs8` | `v-row` > `v-col cols="8"` |
241
+ | `v-flex` | ✅ | ❌ **does not exist** — renders as an inert HTML element: no error, no `[Vue warn]`, just **layout gone** |
242
+ | `v-layout` | ✅ grid row | ⚠️ **exists with a different meaning**: app layout wrapper (with `v-main`, as `fastproject-3.0.js` uses), NOT a grid row. Using it instead of `v-row` gives a flex container without grid gutter/breakpoints |
243
+ | bare prop `shrink` | ✅ (`v-flex` prop) | ❌ no such prop → inert DOM attribute. Use `class="shrink"` |
245
244
 
246
- Template chuẩn ở mục 8 ngay dưới viết theo **V3** (`v-row`/`v-col`). Component viết cho project V2 thì
247
- đổi sang `v-layout`/`v-flex` tương ứng.
245
+ §8 is **V3** (`v-row`/`v-col`); V2 projects use `v-layout`/`v-flex`. Silent breakage both ways: `v-flex` in V3 **shows content with wrong layout**, nothing reports it. Fixing an old component → `fui component preview` on **the project's framework** before pushing.
248
246
 
249
- Đây là lỗi hỏng-âm-thầm theo cả hai chiều: component dùng `v-flex` đưa sang project V3 sẽ **hiện đủ
250
- nội dung nhưng sai bố cục**, và không tầng nào báo. Sửa component cũ thì kiểm bằng `component_preview`
251
- ở **đúng framework của project** trước khi push.
252
-
253
- CSS tùy chỉnh riêng component (nếu thật sự cần) đặt trong `<style scoped>` của chính `.vue` đó, tự
254
- scope thủ công theo quy tắc ở mục 7 phía trên — không đặt trong `header.html` trừ khi CSS đó dùng
255
- chung cho nhiều component/toàn module.
247
+ Component-specific CSS → its own `<style scoped>`, manually scoped — not `header.html` unless shared by several components/the module.
256
248
 
257
249
  ---
258
250
 
259
- ## 8. Dialog chuẩn trong Vue component (`uc-*.vue`) — TEMPLATE CHUẨN HOÁ
251
+ ## 8. Standard dialog in `uc-*.vue` — STANDARD TEMPLATE
260
252
 
261
- Dùng `v-dialog` + `v-card` với **quy ước spacing/size cố định** dưới đây cho MỌI dialog form. Chỉ dùng class tiện ích Vuetify — không tự chế class, không `style`.
253
+ EVERY form dialog: `v-dialog` + `v-card` with these **fixed conventions**. Vuetify utility classes only — no invented classes, no `style`.
262
254
 
263
255
  ```html
264
256
  <v-dialog v-model="editDialog" max-width="600">
@@ -274,12 +266,17 @@ Dùng `v-dialog` + `v-card` với **quy ước spacing/size cố định** dư
274
266
  <v-card-text class="px-3 py-1">
275
267
  <v-row>
276
268
  <v-col cols="8">
277
- <v-text-field v-model="editItem.RoundName" label="Tên vòng thi"
278
- hide-details outlined dense></v-text-field>
269
+ <v-text-field v-model="editItem.RoundName" label="Tên vòng thi" hide-details outlined dense></v-text-field>
279
270
  </v-col>
280
271
  <v-col cols="4">
281
- <v-text-field v-model.number="editItem.RoundOrder" label="Thứ tự" type="number"
282
- hide-details outlined dense></v-text-field>
272
+ <v-text-field
273
+ v-model.number="editItem.RoundOrder"
274
+ label="Thứ tự"
275
+ type="number"
276
+ hide-details
277
+ outlined
278
+ dense
279
+ ></v-text-field>
283
280
  </v-col>
284
281
  </v-row>
285
282
  </v-card-text>
@@ -294,51 +291,51 @@ Dùng `v-dialog` + `v-card` với **quy ước spacing/size cố định** dư
294
291
  </v-dialog>
295
292
  ```
296
293
 
297
- ### Quy ước bắt buộc (không đổi tuỳ tiện)
294
+ ### Mandatory conventions (don't change arbitrarily)
298
295
 
299
- | Phần | Class/prop chuẩn | Ghi chú |
300
- |---|---|---|
301
- | `v-dialog` | `max-width="600"` | 600 cho form thường; 400 nhỏ; 800–900 form rộng |
302
- | `v-card-title` | `d-flex align-center px-3 pt-2 pb-3 subtitle-1 font-weight-bold` | Tiêu đề gọn + nút X (`v-btn icon` → `mdi-close`) bên phải qua `v-spacer` |
303
- | `v-card-text` | `px-3 py-1` | Padding nội dung nhất quán |
304
- | Field nhập | `outlined dense hide-details` | Gọn, cùng chiều cao; chia cột bằng `v-row` + `v-col cols="N"` |
305
- | `v-card-actions` | Là **hàng cuối**; `pa-3` + `v-spacer` trước nhóm nút | Toàn bộ action căn phải |
306
- | Chiều ngang nút | Không đặt `block`, `width`, class grow hay bọc trong cột giãn | Giữ chiều ngang tự nhiên theo nhãn |
307
- | Nút chính | Tối đa một nút dùng `color="primary" elevation="0"` + `:loading="saving"` | Có nền để thể hiện ưu tiên; `:loading` khi đang gọi API |
308
- | Nút phụ | `text`, không `color` nền | Dùng cho action ít quan trọng; giao diện nhã nhặn, tối giản |
296
+ | Part | Class/prop | Note |
297
+ | ---------------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
298
+ | `v-dialog` | `max-width="600"` | 600 normal form; 400 small; 800–900 wide form |
299
+ | `v-card-title` | `d-flex align-center px-3 pt-2 pb-3 subtitle-1 font-weight-bold` | Compact title + X button (`v-btn icon` → `mdi-close`) on the right via `v-spacer` |
300
+ | `v-card-text` | `px-3 py-1` | Consistent content padding |
301
+ | Input fields | `outlined dense hide-details` | Compact, equal height; columns via `v-row` + `v-col cols="N"` |
302
+ | `v-card-actions` | **Last row**; `pa-3` + `v-spacer` before buttons | All actions right-aligned |
303
+ | Button width | No `block`, `width`, grow class or stretched column wrapper | Natural width from label |
304
+ | Primary button | At most one with `color="primary" elevation="0"` + `:loading="saving"` | Filled to show priority; `:loading` during API call |
305
+ | Secondary button | `text`, no background `color` | Minor actions; understated |
309
306
 
310
- - Tiêu đề động theo chế độ: `{{ editItem.ID ? 'Chỉnh sửa' : 'Thêm mới' }}`.
311
- - Số nhập dùng `v-model.number` + `type="number"`.
312
- - Đóng dialog: `@click="dialogVar = false"`; sau khi lưu thành công cũng set `false` trong callback.
313
- - Icon giữ kích thước mặc định; không tự thêm `small`/`:small` cho `v-icon` hoặc nút icon.
307
+ - Mode-dependent title: `{{ editItem.ID ? 'Chỉnh sửa' : 'Thêm mới' }}`.
308
+ - Numbers: `v-model.number` + `type="number"`.
309
+ - Close: `@click="dialogVar = false"`; also set `false` in the callback after a successful save.
310
+ - Keep default icon size; never add `small`/`:small` to `v-icon` or icon buttons.
314
311
 
315
- ### Biến thể
312
+ ### Variants
316
313
 
317
- - **Toolbar header** (khi cần màu/icon nổi bật): thay `v-card-title` bằng `v-toolbar dense flat` + `v-toolbar-title`.
318
- - **Fullscreen** (form lớn/nhiều vùng): `<v-dialog v-model="..." fullscreen>` + `v-toolbar` header, nội dung trong `v-container class="grid-list-md"`.
314
+ - **Toolbar header** (color/icon emphasis): replace `v-card-title` with `v-toolbar dense flat` + `v-toolbar-title`.
315
+ - **Fullscreen** (large/multi-area form): `<v-dialog v-model="..." fullscreen>` + `v-toolbar` header, content in `v-container class="grid-list-md"`.
319
316
 
320
317
  ---
321
318
 
322
- ## 9. Thiết kế cho tái sử dụng
319
+ ## 9. Design for reuse
323
320
 
324
- - Kiểm tra xem component có thể là: list, card, dialog body, filter panel, summary block, form section không
325
- - Expose loading/empty/disabled/readonly qua props hoặc slots thay vì hardcode một workflow
326
- - Giữ API calls, routing, permissions trong `module.json` hoặc parent — component chỉ lo presentation
327
- - Khi module.json `controls` vượt ~200 dòng: extract UI sang Vue component, giữ module.json chỉ có data/API
321
+ - Shape as list, card, dialog body, filter panel, summary block or form section where possible
322
+ - Expose loading/empty/disabled/readonly via props/slots, don't hardcode a workflow
323
+ - API calls, routing, permissions stay in `module.json` or parent — component = presentation
324
+ - module.json `controls` > ~200 lines → extract UI to a Vue component; module.json keeps data/API
328
325
 
329
326
  ---
330
327
 
331
- ## 10. Không nhúng `f-dialog` vào template component
328
+ ## 10. No `f-dialog` inside a component template
332
329
 
333
- `f-dialog` chỉ hoạt động khi khai báo trực tiếp trong `controls` JSON của module. Nếu cần dialog trong một `uc-*.vue`, dùng cấu trúc v-dialog ở mục 7 thay thế.
330
+ `f-dialog` works only directly in the module's `controls` JSON. In `uc-*.vue` use the §8 v-dialog.
334
331
 
335
332
  ---
336
333
 
337
- ## 11. Giới hạn cú pháp trong `<script>` của component
334
+ ## 11. `<script>` syntax limits
338
335
 
339
- ### Không dùng `async / await / finally`
336
+ ### No `async / await / finally`
340
337
 
341
- FUI parser không hỗ trợ các keyword này bên trong file `.vue`. Dùng callback thay thế:
338
+ Unsupported by the FUI `.vue` parser; use callbacks:
342
339
 
343
340
  ```js
344
341
  // ❌ SAI
@@ -352,27 +349,27 @@ mounted() {
352
349
  }
353
350
  ```
354
351
 
355
- ### Không dùng template literals (backtick) trong `<script>`
352
+ ### No template literals (backticks) in `<script>`
356
353
 
357
- Backtick chỉ hoạt động trong `script.js` độc lập, không dùng được trong file `.vue`:
354
+ Only work in standalone `script.js`:
358
355
 
359
356
  ```js
360
357
  // ❌ SAI — trong .vue <script>
361
- var url = `/api/Student/${this.studentID}`
358
+ var url = `/api/Student/${this.studentID}`;
362
359
 
363
360
  // ✅ ĐÚNG
364
- var url = '/api/Student/' + this.studentID
361
+ var url = "/api/Student/" + this.studentID;
365
362
  ```
366
363
 
367
364
  ---
368
365
 
369
- ## 12. Tận dụng hàm có sẵn trong `defaultfunction.js` — KHÔNG viết lại
366
+ ## 12. Reuse `defaultfunction.js` — DON'T rewrite
370
367
 
371
- FUI đã cung cấp sẵn nhiều hàm global. **Trước khi tự viết logic (gọi API, xác nhận, thông báo, format, group, copy...) → kiểm tra [default-function.md](default-function.md) xem đã có chưa.** Chỉ tự viết khi hàm có sẵn không đáp ứng được. Viết lại thứ đã có = lãng phí + không nhất quán.
368
+ **Before writing logic (API call, confirm, notify, format, group, copy...) → check [default-function.md](default-function.md).** Write your own only if nothing fits.
372
369
 
373
- ### Gọi API — dùng `ajaxCALL` (KHÔNG `fetch`/`axios`/`$.ajax` thủ công)
370
+ ### API calls — `ajaxCALL` (NOT hand-rolled `fetch`/`axios`/`$.ajax`)
374
371
 
375
- `fetch` bị lỗi CORS/credentials sang domain khác. `ajaxCALL` tự xử lý credentials, token (Bearer), và base domain (`v_Set.apiDomain`) — không cần ghép domain.
372
+ `fetch` fails cross-domain (CORS/credentials). `ajaxCALL` handles credentials, Bearer token and base domain (`v_Set.apiDomain`) — don't prepend it.
376
373
 
377
374
  ```js
378
375
  // ❌ SAI
@@ -387,7 +384,7 @@ ajaxCALL('spAPI_RoundList', { ContestID: this.contestID }, function(res) {
387
384
  })
388
385
  ```
389
386
 
390
- ### Xác nhận — dùng `confirm` (KHÔNG `window.confirm`)
387
+ ### Confirm — `confirm` (NOT `window.confirm`)
391
388
 
392
389
  ```js
393
390
  confirm({
@@ -397,59 +394,55 @@ confirm({
397
394
  });
398
395
  ```
399
396
 
400
- ### Thông báo — dùng `showMessage` hoặc `Vue.$toast`
397
+ ### Notify — `showMessage` or `Vue.$toast`
401
398
 
402
399
  ```js
403
- showMessage({ title: 'Đã lưu thành công' }); // hộp thoại
404
- showMessage({ title: 'Lỗi', message: '...', type: 'red' });
405
- Vue.$toast.success('Đã lưu', { position: 'top' }); // toast góc màn hình
400
+ showMessage({ title: "Đã lưu thành công" }); // hộp thoại
401
+ showMessage({ title: "Lỗi", message: "...", type: "red" });
402
+ Vue.$toast.success("Đã lưu", { position: "top" }); // toast góc màn hình
406
403
  ```
407
404
 
408
- ### Các hàm hay dùng khác (đừng viết lại)
405
+ ### Other common helpers (don't rewrite)
409
406
 
410
- `groupBy` · `findInArray` · `jsonToExcel` (xuất Excel) · `printPDF` · `copyToClipboard` · `openWindow` · `pushRouter` · `rightTest` (kiểm tra quyền) · `generateID` · `capacityText` · `buildHeader` (tự sinh headers f-table). Chi tiết: [default-function.md](default-function.md).
407
+ `groupBy` · `findInArray` · `jsonToExcel` (Excel export) · `printPDF` · `copyToClipboard` · `openWindow` · `pushRouter` · `rightTest` (permission check) · `generateID` · `capacityText` · `buildHeader` (auto f-table headers). Details: [default-function.md](default-function.md).
411
408
 
412
- > Thư viện toàn cục cũng có sẵn — dùng thay vì tự cài: `_` (Lodash: `_.groupBy`, `_.sumBy`, `_.cloneDeep`...), `moment` (ngày giờ), `numeral` (format số). Không thêm CDN cho các thư viện này.
409
+ > Globals — never add a CDN for them: `_` (Lodash: `_.groupBy`, `_.sumBy`, `_.cloneDeep`...), `moment` (dates), `numeral` (numbers).
413
410
 
414
411
  ---
415
412
 
416
- ## 13. Kiểm chứng component — `component_preview` TRƯỚC khi ráp vào module
413
+ ## 13. Verify with `fui component preview` BEFORE assembling into the module
417
414
 
418
- Một `uc-*.vue` là đơn vị đầy đủ (template + script + style) nhưng nếu chỉ nhìn thấy nó sau khi đã ráp vào module thì lỗi của component và lỗi lắp ráp trộn lẫn — mất thời gian khoanh vùng. `component_preview` render **đúng một component** trên một trang riêng, bằng file `.vue` **local** (chưa cần push), với dữ liệu mẫu bạn truyền thẳng trong tham số.
415
+ Seen only after assembly, component bugs and assembly bugs mix. `fui component preview` renders **one component** on its own page from the **local** `.vue` (no push needed) with sample data passed directly.
419
416
 
420
417
  ```
421
- component_preview({
422
- componentName: "uc-invoice-list",
423
- projectId: "...", // chỉ projectId → project-scope
424
- // moduleId: "..." // có moduleId → module-scope (suy luận y hệt component_new/component_update)
425
- cases: [
426
- { name: "danh sách rỗng", props: { items: [] } },
427
- { name: "có dữ liệu", props: { items: [ {...}, {...} ], title: "Hoá đơn" } },
428
- { name: "click chọn dòng", props: { items: [...] },
429
- scenario: [ { click: "Xem" }, { assert: "vueData.selected != null" } ] }
430
- ],
431
- devices: ["desktop", "mobile"] // mặc định
432
- })
418
+ fui component preview uc-invoice-list -p <pid> --cases '[
419
+ { "name": "danh sách rỗng", "props": { "items": [] } },
420
+ { "name": "có dữ liệu", "props": { "items": [ {...}, {...} ], "title": "Hoá đơn" } },
421
+ { "name": "click chọn dòng", "props": { "items": [...] },
422
+ "scenario": [ { "click": "Xem" }, { "assert": "vueData.selected != null" } ] }
423
+ ]'
424
+ # -p only → project-scope; add -m <mid> → module-scope (same inference as fui component new/update)
425
+ # devices default: desktop + mobile
433
426
  ```
434
427
 
435
- - **Props**: mảng/object cứ truyền **giá trị thật** — tool tự đẩy vào `data[]` và bind `:prop`. Chuỗi thành attr tĩnh; key đã bắt đầu bằng `:`/`v-` giữ nguyên văn (dùng `v-on:`/`v-slot:`, **không** `@`/`#`).
436
- - **`cases × devices` ≤ 8**. Mỗi lần render là một phiên browser thật.
437
- - `slot` (nội dung `innerHTML`), `vueData`, `apiMocks`, `user` (test permission gate), `viewport` (ép chiều cao thấp để lộ component tự đo chiều cao) đều truyền được.
438
- - `scenario` dùng **đúng grammar renderUI trên DOM thật**: `set`/`click`/`call`/`exe`/`assert`, chọn dòng bằng `nth` (không có `item`/`index`).
439
- - **`style.css` của module KHÔNG được nạp** — đó chính là ý nghĩa "không gian riêng": component phải tự đứng được bằng `<style>` của chính nó + theme project.
440
- - Trả về: ảnh chụp (tối đa 4 inline, còn lại ghi đường dẫn) + JS error + `[Vue warn]` + DOM audit. **Đọc ảnh rồi tự sửa** trước khi push.
428
+ - **Props**: arrays/objects as **real values** (pushed into `data[]`, bound `:prop`). Strings → static attrs; keys starting `:`/`v-` kept verbatim (use `v-on:`/`v-slot:`, **not** `@`/`#`).
429
+ - **`cases × devices` ≤ 8** (each render = real browser session).
430
+ - Also accepted: `slot` (`innerHTML` content), `vueData`, mocks, `user` (test permission gates), `viewport` (force low height to expose self-measuring components). Flags: `fui component preview --help`.
431
+ - `scenario` uses **the exact `--render` grammar on the real DOM**: `set`/`click`/`call`/`exe`/`assert`; pick rows with `nth` (no `item`/`index`).
432
+ - **Module `style.css` NOT loaded** — component must stand on its own `<style>` + project theme.
433
+ - Returns: screenshots (max 4 inline, rest as paths) + JS errors + `[Vue warn]` + DOM audit. **Read the images and fix** before pushing.
441
434
 
442
- ### Component cấp project: BẮT BUỘC push trước khi render module dùng nó
435
+ ### Project components: MUST push before rendering a module that uses them
443
436
 
444
- `module_simulate({ renderUI: true })` dựng tầng project bằng bundle **tải từ server**. Bản local chưa push sẽ **không xuất hiện** — ảnh render là bản cũ trên server, tức ảnh nói dối. Vì vậy renderUI **chặn cứng** (không có cờ bỏ qua) khi component cấp project ở local lệch với bundle server.
437
+ `fui module simulate --render` builds the project layer from the **server** bundle; unpushed local edits **don't appear** (screenshot lies). So `--render` **hard-blocks** (no override) when a local project component differs from the server bundle.
445
438
 
446
439
  ```
447
440
  sửa uc-*.vue cấp project
448
- → component_preview (kiểm chứng bản local, chưa push)
449
- → component_new (lần đầu) hoặc component_update (các lần sau) — push lên server
450
- → module_simulate({ renderUI: true }) ← chỉ chạy được sau khi đã push
441
+ → fui component preview (kiểm chứng bản local, chưa push)
442
+ → fui component new (lần đầu) hoặc fui component update (các lần sau) — push lên server
443
+ → fui module simulate --render ← chỉ chạy được sau khi đã push
451
444
  ```
452
445
 
453
- Nếu bị chặn mà **bản trên server mới hơn** (ai đó vừa sửa trên web IDE): chạy `project_sync` để lấy về, **đừng push đè** — sẽ mất bài của người khác.
446
+ Blocked and **server is newer** (edited in web IDE) → `fui project sync`; **don't push over it** (erases their work).
454
447
 
455
- Component **cấp module** không bị luật này: renderUI dựng tầng module từ file local nên sửa tới đâu render thấy tới đó.
448
+ **Module-level** components are exempt: the module layer renders from local files.