@fui-org/fui-cli 2.1.4 → 2.2.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 (217) hide show
  1. package/README.md +27 -7
  2. package/dist/fui-qrb1j9jz.js +397 -0
  3. package/dist/fui.js +1 -1
  4. package/package.json +1 -1
  5. package/skills/fui/SKILL.md +143 -38
  6. package/skills/fui/agents/fui-builder.md +62 -0
  7. package/skills/fui/agents/fui-tester.md +77 -0
  8. package/skills/{fui-skill → fui}/assets/projectdefaultstyle.css +489 -489
  9. package/skills/{fui-skill → fui}/examples/component.vue +162 -162
  10. package/skills/{fui-skill → fui}/references/INDEX.md +5 -4
  11. package/skills/{fui-skill → fui}/references/advanced-techniques.md +170 -168
  12. package/skills/{fui-skill → fui}/references/component-design.md +2 -2
  13. package/skills/{fui-skill → fui}/references/component-quickref.md +78 -78
  14. package/skills/{fui-skill → fui}/references/component-table.md +287 -287
  15. package/skills/{fui-skill → fui}/references/components-dialog.md +191 -191
  16. package/skills/{fui-skill → fui}/references/components-display.md +141 -141
  17. package/skills/{fui-skill → fui}/references/components-echart.md +316 -316
  18. package/skills/{fui-skill → fui}/references/components-input.md +335 -335
  19. package/skills/{fui-skill → fui}/references/controls-patterns.md +2 -2
  20. package/skills/{fui-skill → fui}/references/controls-styling-vocabulary.md +173 -173
  21. package/skills/{fui-skill → fui}/references/db-workflow.md +9 -9
  22. package/skills/{fui-skill → fui}/references/default-function.md +456 -456
  23. package/skills/{fui-skill → fui}/references/design-modes.md +57 -57
  24. package/skills/{fui-skill → fui}/references/echart-templates.md +489 -489
  25. package/skills/{fui-skill → fui}/references/fastproject.md +99 -99
  26. package/skills/{fui-skill → fui}/references/fullstack-workflow.md +314 -313
  27. package/skills/{fui-skill → fui}/references/module-data-patterns.md +117 -117
  28. package/skills/{fui-skill → fui}/references/module-json-anatomy.md +132 -132
  29. package/skills/{fui-skill → fui}/references/module-structure.md +144 -144
  30. package/skills/{fui-skill → fui}/references/new-session.md +1 -2
  31. package/skills/{fui-skill → fui}/references/pdfmake.md +60 -60
  32. package/skills/{fui-skill → fui}/references/permission-system.md +1 -1
  33. package/skills/{fui-skill → fui}/references/platform-architecture.md +269 -269
  34. package/skills/{fui-skill → fui}/references/project-config.md +303 -303
  35. package/skills/{fui-skill → fui}/references/project-provisioning.md +4 -4
  36. package/skills/{fui-skill → fui}/references/script-map.md +262 -262
  37. package/skills/{fui-skill → fui}/references/sql-clr-functions.md +225 -225
  38. package/skills/{fui-skill → fui}/references/system-design.md +2 -2
  39. package/skills/{fui-skill → fui}/references/tapi-permission-patterns.md +156 -156
  40. package/skills/{fui-skill → fui}/references/tapi-reference.md +474 -474
  41. package/skills/fui/references/team-run.md +141 -0
  42. package/skills/fui/references/tools-registry.md +211 -0
  43. package/skills/{fui-skill → fui}/references/ui-crosswindow-patterns.md +321 -321
  44. package/skills/{fui-skill → fui}/references/ui-dialog-patterns.md +255 -255
  45. package/skills/{fui-skill → fui}/references/ui-layout-patterns.md +176 -176
  46. package/skills/{fui-skill → fui}/references/ui-patterns.md +303 -303
  47. package/skills/{fui-skill → fui}/references/ui-screenshot-review.md +95 -95
  48. package/skills/{fui-skill → fui}/references/ui-table-cell-patterns.md +318 -318
  49. package/skills/{fui-skill → fui}/references/verification.md +11 -11
  50. package/skills/{fui-skill → fui}/references/watcher-patterns.md +163 -163
  51. package/skills/{fui-skill → fui}/references/websocket-realtime.md +271 -271
  52. package/skills/{fui-skill → fui}/scripts/component-3.0.js +2715 -2715
  53. package/skills/{fui-skill → fui}/scripts/component.js +3147 -3147
  54. package/skills/{fui-skill → fui}/scripts/componentTable-3.0.js +1038 -1038
  55. package/skills/{fui-skill → fui}/scripts/componentTable.js +891 -891
  56. package/skills/{fui-skill → fui}/scripts/defaultfunction-3.0.js +866 -866
  57. package/skills/{fui-skill → fui}/scripts/defaultfunction.js +1051 -1051
  58. package/skills/{fui-skill → fui}/scripts/fastproject-3.0.js +870 -870
  59. package/skills/{fui-skill → fui}/scripts/fastproject.js +828 -828
  60. package/dist/fui-h4z7zhk2.js +0 -381
  61. package/skills/fui-skill/SKILL.md +0 -139
  62. package/skills/fui-skill/references/tools-registry.md +0 -84
  63. /package/skills/{fui-skill → fui}/assets/projectdefaultstyle-3.0.css +0 -0
  64. /package/skills/{fui-skill → fui}/design-md/airbnb/DESIGN.md +0 -0
  65. /package/skills/{fui-skill → fui}/design-md/airbnb/README.md +0 -0
  66. /package/skills/{fui-skill → fui}/design-md/airtable/DESIGN.md +0 -0
  67. /package/skills/{fui-skill → fui}/design-md/airtable/README.md +0 -0
  68. /package/skills/{fui-skill → fui}/design-md/apple/DESIGN.md +0 -0
  69. /package/skills/{fui-skill → fui}/design-md/apple/README.md +0 -0
  70. /package/skills/{fui-skill → fui}/design-md/asu/DESIGN.md +0 -0
  71. /package/skills/{fui-skill → fui}/design-md/asu/README.md +0 -0
  72. /package/skills/{fui-skill → fui}/design-md/binance/DESIGN.md +0 -0
  73. /package/skills/{fui-skill → fui}/design-md/binance/README.md +0 -0
  74. /package/skills/{fui-skill → fui}/design-md/bmw/DESIGN.md +0 -0
  75. /package/skills/{fui-skill → fui}/design-md/bmw/README.md +0 -0
  76. /package/skills/{fui-skill → fui}/design-md/bmw-m/DESIGN.md +0 -0
  77. /package/skills/{fui-skill → fui}/design-md/bmw-m/README.md +0 -0
  78. /package/skills/{fui-skill → fui}/design-md/bugatti/DESIGN.md +0 -0
  79. /package/skills/{fui-skill → fui}/design-md/bugatti/README.md +0 -0
  80. /package/skills/{fui-skill → fui}/design-md/cal/DESIGN.md +0 -0
  81. /package/skills/{fui-skill → fui}/design-md/cal/README.md +0 -0
  82. /package/skills/{fui-skill → fui}/design-md/claude/DESIGN.md +0 -0
  83. /package/skills/{fui-skill → fui}/design-md/claude/README.md +0 -0
  84. /package/skills/{fui-skill → fui}/design-md/clay/DESIGN.md +0 -0
  85. /package/skills/{fui-skill → fui}/design-md/clay/README.md +0 -0
  86. /package/skills/{fui-skill → fui}/design-md/clickhouse/DESIGN.md +0 -0
  87. /package/skills/{fui-skill → fui}/design-md/clickhouse/README.md +0 -0
  88. /package/skills/{fui-skill → fui}/design-md/cohere/DESIGN.md +0 -0
  89. /package/skills/{fui-skill → fui}/design-md/cohere/README.md +0 -0
  90. /package/skills/{fui-skill → fui}/design-md/coinbase/DESIGN.md +0 -0
  91. /package/skills/{fui-skill → fui}/design-md/coinbase/README.md +0 -0
  92. /package/skills/{fui-skill → fui}/design-md/composio/DESIGN.md +0 -0
  93. /package/skills/{fui-skill → fui}/design-md/composio/README.md +0 -0
  94. /package/skills/{fui-skill → fui}/design-md/cursor/DESIGN.md +0 -0
  95. /package/skills/{fui-skill → fui}/design-md/cursor/README.md +0 -0
  96. /package/skills/{fui-skill → fui}/design-md/elevenlabs/DESIGN.md +0 -0
  97. /package/skills/{fui-skill → fui}/design-md/elevenlabs/README.md +0 -0
  98. /package/skills/{fui-skill → fui}/design-md/expo/DESIGN.md +0 -0
  99. /package/skills/{fui-skill → fui}/design-md/expo/README.md +0 -0
  100. /package/skills/{fui-skill → fui}/design-md/ferrari/DESIGN.md +0 -0
  101. /package/skills/{fui-skill → fui}/design-md/ferrari/README.md +0 -0
  102. /package/skills/{fui-skill → fui}/design-md/figma/DESIGN.md +0 -0
  103. /package/skills/{fui-skill → fui}/design-md/figma/README.md +0 -0
  104. /package/skills/{fui-skill → fui}/design-md/framer/DESIGN.md +0 -0
  105. /package/skills/{fui-skill → fui}/design-md/framer/README.md +0 -0
  106. /package/skills/{fui-skill → fui}/design-md/fui/DESIGN.md +0 -0
  107. /package/skills/{fui-skill → fui}/design-md/hashicorp/DESIGN.md +0 -0
  108. /package/skills/{fui-skill → fui}/design-md/hashicorp/README.md +0 -0
  109. /package/skills/{fui-skill → fui}/design-md/ibm/DESIGN.md +0 -0
  110. /package/skills/{fui-skill → fui}/design-md/ibm/README.md +0 -0
  111. /package/skills/{fui-skill → fui}/design-md/intercom/DESIGN.md +0 -0
  112. /package/skills/{fui-skill → fui}/design-md/intercom/README.md +0 -0
  113. /package/skills/{fui-skill → fui}/design-md/kraken/DESIGN.md +0 -0
  114. /package/skills/{fui-skill → fui}/design-md/kraken/README.md +0 -0
  115. /package/skills/{fui-skill → fui}/design-md/lamborghini/DESIGN.md +0 -0
  116. /package/skills/{fui-skill → fui}/design-md/lamborghini/README.md +0 -0
  117. /package/skills/{fui-skill → fui}/design-md/linear.app/DESIGN.md +0 -0
  118. /package/skills/{fui-skill → fui}/design-md/linear.app/README.md +0 -0
  119. /package/skills/{fui-skill → fui}/design-md/lovable/DESIGN.md +0 -0
  120. /package/skills/{fui-skill → fui}/design-md/lovable/README.md +0 -0
  121. /package/skills/{fui-skill → fui}/design-md/mastercard/DESIGN.md +0 -0
  122. /package/skills/{fui-skill → fui}/design-md/mastercard/README.md +0 -0
  123. /package/skills/{fui-skill → fui}/design-md/meta/DESIGN.md +0 -0
  124. /package/skills/{fui-skill → fui}/design-md/meta/README.md +0 -0
  125. /package/skills/{fui-skill → fui}/design-md/minimax/DESIGN.md +0 -0
  126. /package/skills/{fui-skill → fui}/design-md/minimax/README.md +0 -0
  127. /package/skills/{fui-skill → fui}/design-md/mintlify/DESIGN.md +0 -0
  128. /package/skills/{fui-skill → fui}/design-md/mintlify/README.md +0 -0
  129. /package/skills/{fui-skill → fui}/design-md/miro/DESIGN.md +0 -0
  130. /package/skills/{fui-skill → fui}/design-md/miro/README.md +0 -0
  131. /package/skills/{fui-skill → fui}/design-md/mistral.ai/DESIGN.md +0 -0
  132. /package/skills/{fui-skill → fui}/design-md/mistral.ai/README.md +0 -0
  133. /package/skills/{fui-skill → fui}/design-md/mongodb/DESIGN.md +0 -0
  134. /package/skills/{fui-skill → fui}/design-md/mongodb/README.md +0 -0
  135. /package/skills/{fui-skill → fui}/design-md/nike/DESIGN.md +0 -0
  136. /package/skills/{fui-skill → fui}/design-md/nike/README.md +0 -0
  137. /package/skills/{fui-skill → fui}/design-md/notion/DESIGN.md +0 -0
  138. /package/skills/{fui-skill → fui}/design-md/notion/README.md +0 -0
  139. /package/skills/{fui-skill → fui}/design-md/nvidia/DESIGN.md +0 -0
  140. /package/skills/{fui-skill → fui}/design-md/nvidia/README.md +0 -0
  141. /package/skills/{fui-skill → fui}/design-md/ollama/DESIGN.md +0 -0
  142. /package/skills/{fui-skill → fui}/design-md/ollama/README.md +0 -0
  143. /package/skills/{fui-skill → fui}/design-md/opencode.ai/DESIGN.md +0 -0
  144. /package/skills/{fui-skill → fui}/design-md/opencode.ai/README.md +0 -0
  145. /package/skills/{fui-skill → fui}/design-md/pinterest/DESIGN.md +0 -0
  146. /package/skills/{fui-skill → fui}/design-md/pinterest/README.md +0 -0
  147. /package/skills/{fui-skill → fui}/design-md/playstation/DESIGN.md +0 -0
  148. /package/skills/{fui-skill → fui}/design-md/playstation/README.md +0 -0
  149. /package/skills/{fui-skill → fui}/design-md/posthog/DESIGN.md +0 -0
  150. /package/skills/{fui-skill → fui}/design-md/posthog/README.md +0 -0
  151. /package/skills/{fui-skill → fui}/design-md/raycast/DESIGN.md +0 -0
  152. /package/skills/{fui-skill → fui}/design-md/raycast/README.md +0 -0
  153. /package/skills/{fui-skill → fui}/design-md/renault/DESIGN.md +0 -0
  154. /package/skills/{fui-skill → fui}/design-md/renault/README.md +0 -0
  155. /package/skills/{fui-skill → fui}/design-md/replicate/DESIGN.md +0 -0
  156. /package/skills/{fui-skill → fui}/design-md/replicate/README.md +0 -0
  157. /package/skills/{fui-skill → fui}/design-md/resend/DESIGN.md +0 -0
  158. /package/skills/{fui-skill → fui}/design-md/resend/README.md +0 -0
  159. /package/skills/{fui-skill → fui}/design-md/revolut/DESIGN.md +0 -0
  160. /package/skills/{fui-skill → fui}/design-md/revolut/README.md +0 -0
  161. /package/skills/{fui-skill → fui}/design-md/runwayml/DESIGN.md +0 -0
  162. /package/skills/{fui-skill → fui}/design-md/runwayml/README.md +0 -0
  163. /package/skills/{fui-skill → fui}/design-md/sanity/DESIGN.md +0 -0
  164. /package/skills/{fui-skill → fui}/design-md/sanity/README.md +0 -0
  165. /package/skills/{fui-skill → fui}/design-md/sentry/DESIGN.md +0 -0
  166. /package/skills/{fui-skill → fui}/design-md/sentry/README.md +0 -0
  167. /package/skills/{fui-skill → fui}/design-md/shopify/DESIGN.md +0 -0
  168. /package/skills/{fui-skill → fui}/design-md/shopify/README.md +0 -0
  169. /package/skills/{fui-skill → fui}/design-md/slack/DESIGN.md +0 -0
  170. /package/skills/{fui-skill → fui}/design-md/spacex/DESIGN.md +0 -0
  171. /package/skills/{fui-skill → fui}/design-md/spacex/README.md +0 -0
  172. /package/skills/{fui-skill → fui}/design-md/spotify/DESIGN.md +0 -0
  173. /package/skills/{fui-skill → fui}/design-md/spotify/README.md +0 -0
  174. /package/skills/{fui-skill → fui}/design-md/starbucks/DESIGN.md +0 -0
  175. /package/skills/{fui-skill → fui}/design-md/starbucks/README.md +0 -0
  176. /package/skills/{fui-skill → fui}/design-md/stripe/DESIGN.md +0 -0
  177. /package/skills/{fui-skill → fui}/design-md/stripe/README.md +0 -0
  178. /package/skills/{fui-skill → fui}/design-md/supabase/DESIGN.md +0 -0
  179. /package/skills/{fui-skill → fui}/design-md/supabase/README.md +0 -0
  180. /package/skills/{fui-skill → fui}/design-md/superhuman/DESIGN.md +0 -0
  181. /package/skills/{fui-skill → fui}/design-md/superhuman/README.md +0 -0
  182. /package/skills/{fui-skill → fui}/design-md/tesla/DESIGN.md +0 -0
  183. /package/skills/{fui-skill → fui}/design-md/tesla/README.md +0 -0
  184. /package/skills/{fui-skill → fui}/design-md/theverge/DESIGN.md +0 -0
  185. /package/skills/{fui-skill → fui}/design-md/theverge/README.md +0 -0
  186. /package/skills/{fui-skill → fui}/design-md/together.ai/DESIGN.md +0 -0
  187. /package/skills/{fui-skill → fui}/design-md/together.ai/README.md +0 -0
  188. /package/skills/{fui-skill → fui}/design-md/uber/DESIGN.md +0 -0
  189. /package/skills/{fui-skill → fui}/design-md/uber/README.md +0 -0
  190. /package/skills/{fui-skill → fui}/design-md/vercel/DESIGN.md +0 -0
  191. /package/skills/{fui-skill → fui}/design-md/vercel/README.md +0 -0
  192. /package/skills/{fui-skill → fui}/design-md/vodafone/DESIGN.md +0 -0
  193. /package/skills/{fui-skill → fui}/design-md/vodafone/README.md +0 -0
  194. /package/skills/{fui-skill → fui}/design-md/voltagent/DESIGN.md +0 -0
  195. /package/skills/{fui-skill → fui}/design-md/voltagent/README.md +0 -0
  196. /package/skills/{fui-skill → fui}/design-md/warp/DESIGN.md +0 -0
  197. /package/skills/{fui-skill → fui}/design-md/warp/README.md +0 -0
  198. /package/skills/{fui-skill → fui}/design-md/webflow/DESIGN.md +0 -0
  199. /package/skills/{fui-skill → fui}/design-md/webflow/README.md +0 -0
  200. /package/skills/{fui-skill → fui}/design-md/wired/DESIGN.md +0 -0
  201. /package/skills/{fui-skill → fui}/design-md/wired/README.md +0 -0
  202. /package/skills/{fui-skill → fui}/design-md/wise/DESIGN.md +0 -0
  203. /package/skills/{fui-skill → fui}/design-md/wise/README.md +0 -0
  204. /package/skills/{fui-skill → fui}/design-md/x.ai/DESIGN.md +0 -0
  205. /package/skills/{fui-skill → fui}/design-md/x.ai/README.md +0 -0
  206. /package/skills/{fui-skill → fui}/design-md/zapier/DESIGN.md +0 -0
  207. /package/skills/{fui-skill → fui}/design-md/zapier/README.md +0 -0
  208. /package/skills/{fui-skill → fui}/examples/f-table-patterns.json +0 -0
  209. /package/skills/{fui-skill → fui}/examples/module-patterns.json +0 -0
  210. /package/skills/{fui-skill → fui}/examples/project-patterns.json +0 -0
  211. /package/skills/{fui-skill → fui}/references/coding-standards.md +0 -0
  212. /package/skills/{fui-skill → fui}/references/db-table-design.md +0 -0
  213. /package/skills/{fui-skill → fui}/references/fsheet.md +0 -0
  214. /package/skills/{fui-skill → fui}/references/tapi-file-api.md +0 -0
  215. /package/skills/{fui-skill → fui}/references/ui-templates.md +0 -0
  216. /package/skills/{fui-skill → fui}/scripts/fechart.js +0 -0
  217. /package/skills/{fui-skill → fui}/scripts/fsheet.js +0 -0
@@ -30,21 +30,21 @@ Tiers differ ~100× in cost per run:
30
30
  | `Unknown custom element: <f-…>` / empty component | **1** | wrong name or missing required import |
31
31
  | Chart draws nothing | **1** (missing `echarts.min.js`/`fechart.js`, most cases) → **5** (0-size canvas) | |
32
32
  | Misaligned columns, cramped row | **2** (Σw > 12) | |
33
- | Dialog won't open | **2** (`⚠ không thấy lệnh mở`) → **3** | |
33
+ | Dialog won't open | **2** (`⚠ no opener`) → **3** | |
34
34
  | Wrong action order · infinite watch loop · CONFIRM/MESS wrong branch | **3** | |
35
35
  | Wrong numbers · `undefined`/`NaN` on page | **3** | almost always `OUT`/mapData on the wrong key, not display |
36
36
  | API returns unexpected shape | `fui sp verify` (static, **calls no endpoint**) | infers from the real SP body + generates `--mocks` for tier 3. Only metadata reads: `OBJECT_DEFINITION` when no local `.sql`, `sys.database_permissions` with `--check-grant`; never runs the SP |
37
37
  | One `uc-*.vue` wrong | **4** | once proven, remaining bugs are assembly |
38
38
  | Horizontal overflow · wrong height · leaking style · bad spacing · broken on mobile | **5** | only tier seeing geometry |
39
- | Import/asset not loaded | **5**, section `⛔ MÔI TRƯỜNG THIẾU FILE` | environment error — **don't** edit the module for it |
40
- | Suspected "platform library bug" | **5**, check `⛔ ASSET CŨ` FIRST | stale asset ⇒ on-disk copy ran; CDN conclusions worthless |
39
+ | Import/asset not loaded | **5**, section `⛔ MISSING ENV FILES` | environment error — **don't** edit the module for it |
40
+ | Suspected "platform library bug" | **5**, check `⛔ STALE, UNVERIFIED ASSETS` FIRST | stale asset ⇒ on-disk copy ran; CDN conclusions worthless |
41
41
  | **Permission-gated** button/menu hidden for real users but shown in preview | **1** after `fui right function list` | undefined code ⇒ `rightTest()` always false, no JS error. Tiers 3/5 **pre-grant** scanned codes so they always show — [permission-system.md](permission-system.md) |
42
42
 
43
- **Stale assets:** `--render` revalidates every asset per run (conditional GET; trace shows `N revalidate 304`). `⛔ ASSET CŨ, KHÔNG XÁC MINH ĐƯỢC` = network failed, on-disk copy ran ⇒ "library X has a bug" from that run is **unusable**; reconnect and re-render, or `--refresh-assets`. `ℹ️ ASSET PLATFORM ĐÃ ĐỔI` = platform file changed between runs — behavior changed without a `module.json` edit ⇒ look there first.
43
+ **Stale assets:** `--render` revalidates every asset per run (conditional GET; trace shows `N revalidate 304`). `⛔ STALE, UNVERIFIED ASSETS` = network failed, on-disk copy ran ⇒ "library X has a bug" from that run is **unusable**; reconnect and re-render, or `--refresh-assets`. `ℹ️ PLATFORM ASSETS CHANGED` = platform file changed between runs — behavior changed without a `module.json` edit ⇒ look there first.
44
44
 
45
- **Read the `--render` report top-down:** `═══ CHẨN ĐOÁN ═══` groups failures by **where to fix** — `[MÔI TRƯỜNG]` (fix first; the others may be consequences) · `[MODULE]` (module.json/script.js/component) · `[HIỂN THỊ]` (DOM audit + image). `↪` = cheaper tier that should have caught it.
45
+ **Read the `--render` report top-down:** `═══ DIAGNOSIS ═══` groups failures by **where to fix** — `[ENV]` (fix first; the others may be consequences) · `[MODULE]` (module.json/script.js/component) · `[VIEW]` (DOM audit + image). `↪` = cheaper tier that should have caught it.
46
46
 
47
- **STOP re-rendering** when `Δ so với lượt trước` shows _no metric changed_: the fix missed the bug. Drop to tiers 1–3 to learn why, then return.
47
+ **STOP re-rendering** when `Δ vs previous run` shows _no metric changed_, or the report carries a `🛑 STUCK` block: the fix missed the bug. Drop to tiers 1–3 to learn why, then return.
48
48
 
49
49
  ---
50
50
 
@@ -115,10 +115,10 @@ Tiers differ ~100× in cost per run:
115
115
  - [ ] Read each warning — may be intentional (unlisted component, optional param...), never skip unread
116
116
  - [ ] **`[design]`** warnings (colored dialog toolbar, red delete button, odd dialog width, no floating label, delete without CONFIRM, redundant Close button, hand-built v-snackbar/v-alert instead of MESS/MESSBOX, main f-table/f-sheet without `":height": -16` so the page scrolls instead of the table...) — **must fix** unless the user explicitly asked otherwise this session
117
117
  - [ ] Strict mode (`FUI_STRICT=1`): errors BLOCK publish
118
- - [ ] `fui module outline`, READ the tree: row Σw ≤12? `⚠ không thấy lệnh mở`? `⚠ không thấy nơi gọi`? `⚠ không thấy dùng`?
118
+ - [ ] `fui module outline`, READ the tree: row Σw ≤12? `⚠ no opener`? `⚠ never called`? `⚠ unused in controls/watch/actions`?
119
119
  - [ ] Complex runtime logic (action chain, watch cascade, dialog flow): `fui module simulate` with main-flow `--scenario` + `--mocks` (shape from `fui sp verify` snippet) + asserts — fix every `⚠` and ✘ first. Your `--mocks` always win; a static API with NO mock whose SP is confirmed read-only (`fui sp verify`/local `.sql`) is called for real (max 8 calls/run, cached in `_testdata/`) instead of `{data:[]}` — see the "Live/Cache data" line. SPs with writes are never called
120
- - [ ] **`--mocks` for existing SPs come from `fui sp verify`, never guessed** — on `🚨 CẤU TRÚC MOCK CHƯA ĐƯỢC KIỂM CHỨNG`, stop, run `fui sp verify`, copy the snippet verbatim (no hand edits). Invented shapes only for SPs not yet deployed
121
- - [ ] **Final visual QA (Playwright available)**: `fui module simulate --render --vue-data '{..demo data..}' --mocks '{...}'`. READ the screenshot (always inline; big PNG → JPEG) + DOM audit (empty app, overflow, "undefined"/"NaN", broken image, 0-size chart canvas) + JS errors/`[Vue warn]` + final vueData; fix and re-render until clean. **`⛔ MÔI TRƯỜNG THIẾU FILE` first**: environment error — run its suggested command (`fui project sync`/`fui module get`); errors below may be consequences, don't edit module.json for them. How to read screenshots: `ui-screenshot-review.md`. Chart/animation missing → raise `--settle-ms` (e.g. 1500). Open dialog captured as `render-dialog.jpg`. Mobile use → extra run `--device mobile`. No Playwright → skip (command returns install steps)
120
+ - [ ] **`--mocks` for existing SPs come from `fui sp verify`, never guessed** — on `🚨 UNVERIFIED MOCK SHAPE`, stop, run `fui sp verify`, copy its `--mocks snippet` verbatim (no hand edits). Invented shapes only for SPs not yet deployed
121
+ - [ ] **Final visual QA (Playwright available)**: `fui module simulate --render --vue-data '{..demo data..}' --mocks '{...}'`. OPEN and READ the image files the report lists (`Screenshot:`, `Full-page:`; nothing is attached to the output) + DOM audit (empty app, overflow, "undefined"/"NaN", broken image, 0-size chart canvas) + JS errors/`[Vue warn]` + final vueData; fix and re-render until clean. **`⛔ MISSING ENV FILES` first**: environment error — run its suggested command (`fui project sync`/`fui module get`); errors below may be consequences, don't edit module.json for them. How to read screenshots: `ui-screenshot-review.md`. Chart/animation missing → raise `--settle-ms` (e.g. 1500). Open dialog captured as `render-dialog.jpg`. Mobile use → extra run `--device mobile`. No Playwright → skip (command returns install steps)
122
122
  - [ ] **Changed project level + module level** (project component/import, `project.json`/menu): publish **project level FIRST**, then render — `--render` loads `$projectData`, project components and project CSS from the server (`_aset`); module-level parts render locally (iterate freely, publish later). **Project components are hard-blocked** (no bypass flag) if a local `uc-*.vue` differs from the server bundle: `fui component preview` → `fui component new`/`update` → render. Blocked because server is newer (web IDE edit) → `fui project sync`, never push over
123
123
 
124
124
  ### Publish — only what changed
@@ -151,7 +151,7 @@ For `fui module publish html`: preview ran and diff reviewed · user explicitly
151
151
  - [ ] Permission check at top: `IF @sys_SystemRight < N BEGIN RAISERROR(N'[Unauthorized]Bạn không có quyền...', 16, 1) RETURN END` — severity always `16`; `[Unauthorized]` prefix required so tAPI returns HTTP 401 ([tapi-reference.md](tapi-reference.md) §5)
152
152
  - [ ] No SQL string concatenation (parameterize)
153
153
  - [ ] `.sql` has **NO `GRANT EXECUTE`** — tAPI sends one batch, no `GO`, so a GRANT after `END` is **swallowed into the SP body** and runs on every call, silently. `fui sp deploy` grants new procedures and **re-queries `sys.database_permissions` to confirm**
154
- - [ ] `fui sp deploy` shows `✓ [public] đã có quyền EXECUTE`; on `❗` run exactly the printed GRANT (**don't redeploy**). `fui exec` deploys have no auto-grant (fui warns with the command). Later doubts: `fui sp verify <name> --check-grant`
154
+ - [ ] `fui sp deploy` shows `GRANT EXECUTE ... TO [public]: applied` (new SP) or `[public] has EXECUTE on "<sp>"`; on `grant FAILED` or `[public] lacks EXECUTE` run exactly the printed `fui exec "GRANT ..."` (**don't redeploy**). `fui exec` deploys have no auto-grant (fui warns with the command). Later doubts: `fui sp verify <name> --check-grant`
155
155
 
156
156
  ### Logic
157
157
 
@@ -216,7 +216,7 @@ For `fui module publish html`: preview ran and diff reviewed · user explicitly
216
216
 
217
217
  1. **Re-read every file changed in that publish first**; don't jump elsewhere
218
218
  2. Looks right ⇒ `fui module preview` for server vs local diff
219
- 3. Preview shows `Controls: 0` / `Data keys: 0` ⇒ server holds a broken old version — push again
219
+ 3. Preview shows `controls 0, data keys 0` ⇒ server holds a broken old version — push again
220
220
  4. Only when local is confirmed correct: check other files (header.html, script.js, imports)
221
221
 
222
222
  ---
@@ -1,163 +1,163 @@
1
- # Watcher Patterns
2
-
3
- > Owns: **`watch`: `v_old`, deep-watch, cascading, race control**. Action syntax: [controls-patterns.md](controls-patterns.md).
4
-
5
- Use for multi-level dependent forms (Province → District → Ward) and attribute filters (status, type, date, keyword...). Goals: correct FUI structure (`data`, `watch`, `controls`, `set`), no watcher loops, fewer redundant API calls, maintainable large modules.
6
-
7
- ## 1. Rules
8
-
9
- 1. **Watch the smallest input key** — an ID or filter field (`provinceID`, `districtID`, `filter.status`), not a large object unless needed.
10
- 2. **Split actions by level** — `handleProvinceChange` only resets dependents and calls `apiLoadDistricts`; `handleDistrictChange` only resets the level below and calls `apiLoadWards`; `applyFilters` holds the final list-loading logic.
11
- 3. **`deep-watch` selectively** — only small filter objects (e.g. `filter`). Never deep-watch large arrays like `items`, `tableData`.
12
- 4. **No self-made loops** — never watch the action's own output var; never watch `items` and write `items` in the callback.
13
- 5. **Guard before API calls** — `IF/THEN/ELSE` to validate input (e.g. no province selected → don't load districts).
14
- 6. **Debounce/race control in `script.js`** — watchers only orchestrate; debounce and stale-response-overwrite protection live in JS helpers.
15
- 7. **Force literal strings only via `IN`** — in an action with `IN`, a string without spaces may be read as an expression → use `` `value`` or `"'value'"`. Full rules: [controls-patterns.md §0](controls-patterns.md#0-value-resolution-rules-fundamental).
16
-
17
- ## 2. Pattern A: administrative-area cascade
18
-
19
- ```json
20
- {
21
- "data": [
22
- {
23
- "formData": {
24
- "provinceID": null,
25
- "districtID": null,
26
- "wardID": null
27
- },
28
- "provinceList": [],
29
- "districtList": [],
30
- "wardList": []
31
- },
32
- {
33
- "apiLoadProvinces": {
34
- "API": "/api/location/provinces",
35
- "OUT": "provinceList"
36
- },
37
- "apiLoadDistricts": {
38
- "API": "/api/location/districts",
39
- "IN": { "ProvinceID": "formData.provinceID" },
40
- "OUT": "districtList"
41
- },
42
- "apiLoadWards": {
43
- "API": "/api/location/wards",
44
- "IN": { "DistrictID": "formData.districtID" },
45
- "OUT": "wardList"
46
- }
47
- },
48
- {
49
- "handleProvinceChange": [
50
- {
51
- "districtList": [],
52
- "wardList": [],
53
- "formData.districtID": null,
54
- "formData.wardID": null
55
- },
56
- {
57
- "IF": "formData.provinceID",
58
- "THEN": { "CALL": "apiLoadDistricts" }
59
- }
60
- ],
61
- "handleDistrictChange": [
62
- {
63
- "wardList": [],
64
- "formData.wardID": null
65
- },
66
- {
67
- "IF": "formData.districtID",
68
- "THEN": { "CALL": "apiLoadWards" }
69
- }
70
- ]
71
- },
72
- {
73
- "CALL": "apiLoadProvinces"
74
- }
75
- ],
76
- "watch": {
77
- "formData.provinceID": { "CALL": "handleProvinceChange" },
78
- "formData.districtID": { "CALL": "handleDistrictChange" }
79
- }
80
- }
81
- ```
82
-
83
- Key points: watch specific keys; reset lower levels before calling their API; guard against calling with null.
84
-
85
- ## 3. Pattern B: filter object + deep-watch
86
-
87
- ```json
88
- {
89
- "data": [
90
- {
91
- "filter": {
92
- "keyword": "",
93
- "status": null,
94
- "fromDate": null,
95
- "toDate": null
96
- },
97
- "items": []
98
- },
99
- {
100
- "apiLoadItems": {
101
- "API": "/api/items/search",
102
- "IN": {
103
- "Keyword": "filter.keyword",
104
- "Status": "filter.status",
105
- "FromDate": "filter.fromDate",
106
- "ToDate": "filter.toDate"
107
- },
108
- "OUT": "items"
109
- }
110
- },
111
- {
112
- "handleFilterChanged": {
113
- "CALL": "apiLoadItems"
114
- }
115
- }
116
- ],
117
- "watch": {
118
- "deep-watch": {
119
- "filter": { "CALL": "handleFilterChanged" }
120
- }
121
- }
122
- }
123
- ```
124
-
125
- Keep `filter` small and stable. Continuous text-search input → debounce in `script.js`.
126
-
127
- ## 4. Recommended debounce (script.js)
128
-
129
- ```javascript
130
- var filterTimer = null;
131
-
132
- function debounceFilter(input) {
133
- var callActionName = input && input.callActionName;
134
- var waitMs = input && input.waitMs;
135
- clearTimeout(filterTimer);
136
- filterTimer = setTimeout(function () {
137
- CALL(vueData[callActionName]);
138
- }, waitMs || 350);
139
- }
140
- ```
141
-
142
- Action in `module.json`:
143
-
144
- ```json
145
- {
146
- "handleFilterChanged": {
147
- "FUN": "debounceFilter",
148
- "IN": {
149
- "callActionName": "'apiLoadItems'",
150
- "waitMs": 350
151
- }
152
- }
153
- }
154
- ```
155
-
156
- ## 5. Watcher QA checklist
157
-
158
- 1. Watch key small enough (ID/field, not a large object)?
159
- 2. Dependents reset before calling the API?
160
- 3. `IF` guard before API when input is null/empty?
161
- 4. Any watcher loop risk?
162
- 5. Text input needs debounce?
163
- 6. Action with `IN`: literal string rule applied to space-free text values?
1
+ # Watcher Patterns
2
+
3
+ > Owns: **`watch`: `v_old`, deep-watch, cascading, race control**. Action syntax: [controls-patterns.md](controls-patterns.md).
4
+
5
+ Use for multi-level dependent forms (Province → District → Ward) and attribute filters (status, type, date, keyword...). Goals: correct FUI structure (`data`, `watch`, `controls`, `set`), no watcher loops, fewer redundant API calls, maintainable large modules.
6
+
7
+ ## 1. Rules
8
+
9
+ 1. **Watch the smallest input key** — an ID or filter field (`provinceID`, `districtID`, `filter.status`), not a large object unless needed.
10
+ 2. **Split actions by level** — `handleProvinceChange` only resets dependents and calls `apiLoadDistricts`; `handleDistrictChange` only resets the level below and calls `apiLoadWards`; `applyFilters` holds the final list-loading logic.
11
+ 3. **`deep-watch` selectively** — only small filter objects (e.g. `filter`). Never deep-watch large arrays like `items`, `tableData`.
12
+ 4. **No self-made loops** — never watch the action's own output var; never watch `items` and write `items` in the callback.
13
+ 5. **Guard before API calls** — `IF/THEN/ELSE` to validate input (e.g. no province selected → don't load districts).
14
+ 6. **Debounce/race control in `script.js`** — watchers only orchestrate; debounce and stale-response-overwrite protection live in JS helpers.
15
+ 7. **Force literal strings only via `IN`** — in an action with `IN`, a string without spaces may be read as an expression → use `` `value`` or `"'value'"`. Full rules: [controls-patterns.md §0](controls-patterns.md#0-value-resolution-rules-fundamental).
16
+
17
+ ## 2. Pattern A: administrative-area cascade
18
+
19
+ ```json
20
+ {
21
+ "data": [
22
+ {
23
+ "formData": {
24
+ "provinceID": null,
25
+ "districtID": null,
26
+ "wardID": null
27
+ },
28
+ "provinceList": [],
29
+ "districtList": [],
30
+ "wardList": []
31
+ },
32
+ {
33
+ "apiLoadProvinces": {
34
+ "API": "/api/location/provinces",
35
+ "OUT": "provinceList"
36
+ },
37
+ "apiLoadDistricts": {
38
+ "API": "/api/location/districts",
39
+ "IN": { "ProvinceID": "formData.provinceID" },
40
+ "OUT": "districtList"
41
+ },
42
+ "apiLoadWards": {
43
+ "API": "/api/location/wards",
44
+ "IN": { "DistrictID": "formData.districtID" },
45
+ "OUT": "wardList"
46
+ }
47
+ },
48
+ {
49
+ "handleProvinceChange": [
50
+ {
51
+ "districtList": [],
52
+ "wardList": [],
53
+ "formData.districtID": null,
54
+ "formData.wardID": null
55
+ },
56
+ {
57
+ "IF": "formData.provinceID",
58
+ "THEN": { "CALL": "apiLoadDistricts" }
59
+ }
60
+ ],
61
+ "handleDistrictChange": [
62
+ {
63
+ "wardList": [],
64
+ "formData.wardID": null
65
+ },
66
+ {
67
+ "IF": "formData.districtID",
68
+ "THEN": { "CALL": "apiLoadWards" }
69
+ }
70
+ ]
71
+ },
72
+ {
73
+ "CALL": "apiLoadProvinces"
74
+ }
75
+ ],
76
+ "watch": {
77
+ "formData.provinceID": { "CALL": "handleProvinceChange" },
78
+ "formData.districtID": { "CALL": "handleDistrictChange" }
79
+ }
80
+ }
81
+ ```
82
+
83
+ Key points: watch specific keys; reset lower levels before calling their API; guard against calling with null.
84
+
85
+ ## 3. Pattern B: filter object + deep-watch
86
+
87
+ ```json
88
+ {
89
+ "data": [
90
+ {
91
+ "filter": {
92
+ "keyword": "",
93
+ "status": null,
94
+ "fromDate": null,
95
+ "toDate": null
96
+ },
97
+ "items": []
98
+ },
99
+ {
100
+ "apiLoadItems": {
101
+ "API": "/api/items/search",
102
+ "IN": {
103
+ "Keyword": "filter.keyword",
104
+ "Status": "filter.status",
105
+ "FromDate": "filter.fromDate",
106
+ "ToDate": "filter.toDate"
107
+ },
108
+ "OUT": "items"
109
+ }
110
+ },
111
+ {
112
+ "handleFilterChanged": {
113
+ "CALL": "apiLoadItems"
114
+ }
115
+ }
116
+ ],
117
+ "watch": {
118
+ "deep-watch": {
119
+ "filter": { "CALL": "handleFilterChanged" }
120
+ }
121
+ }
122
+ }
123
+ ```
124
+
125
+ Keep `filter` small and stable. Continuous text-search input → debounce in `script.js`.
126
+
127
+ ## 4. Recommended debounce (script.js)
128
+
129
+ ```javascript
130
+ var filterTimer = null;
131
+
132
+ function debounceFilter(input) {
133
+ var callActionName = input && input.callActionName;
134
+ var waitMs = input && input.waitMs;
135
+ clearTimeout(filterTimer);
136
+ filterTimer = setTimeout(function () {
137
+ CALL(vueData[callActionName]);
138
+ }, waitMs || 350);
139
+ }
140
+ ```
141
+
142
+ Action in `module.json`:
143
+
144
+ ```json
145
+ {
146
+ "handleFilterChanged": {
147
+ "FUN": "debounceFilter",
148
+ "IN": {
149
+ "callActionName": "'apiLoadItems'",
150
+ "waitMs": 350
151
+ }
152
+ }
153
+ }
154
+ ```
155
+
156
+ ## 5. Watcher QA checklist
157
+
158
+ 1. Watch key small enough (ID/field, not a large object)?
159
+ 2. Dependents reset before calling the API?
160
+ 3. `IF` guard before API when input is null/empty?
161
+ 4. Any watcher loop risk?
162
+ 5. Text input needs debounce?
163
+ 6. Action with `IN`: literal string rule applied to space-free text values?