@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
@@ -1,6 +1,6 @@
1
1
  # Controls Patterns & Logic
2
2
 
3
- > Owns: value resolution (backtick / `{{ }}` / JS eval), action engine (API/IN/OUT/CALLBACK/CONFIRM/MESS/CALL/IF/EXE), key notation, grid wrapper `container > rows > cols`, control object, layout rules. Classes/styles: [controls-styling-vocabulary.md](controls-styling-vocabulary.md); module.json overview: [module-json-anatomy.md](module-json-anatomy.md). _(Tier A — loaded with `fui skill get`.)_
3
+ > Owns: value resolution (backtick / `{{ }}` / JS eval), action engine (API/IN/OUT/CALLBACK/CONFIRM/MESS/CALL/IF/EXE), key notation, grid wrapper `container > rows > cols`, control object, layout rules. Classes/styles: [controls-styling-vocabulary.md](controls-styling-vocabulary.md); module.json overview: [module-json-anatomy.md](module-json-anatomy.md). _(Tier A — load with `fui skill ref references/controls-patterns.md references/module-structure.md` before any module/UI work; `fui skill get` does not print it.)_
4
4
 
5
5
  ---
6
6
 
@@ -65,7 +65,7 @@ Defaults auto-injected (`defaultControlAttr` in `scripts/component.js`) — decl
65
65
 
66
66
  `"val1"` = value of `val1`, not the string; literal needs ``"`val1"``.
67
67
 
68
- > ⚠️ **Not for `:attr`.** Bindings (`:option`, `:config`, any `v-bind:`) go straight to Vue's compiler: one pure expression; no `function(){}`/`var`/`return`/`;`. Violation ⇒ blank page, 0 JS error, 0 `[Vue warn]`. Rules + moving logic to `script.js` + `EXE`: [advanced-techniques.md](advanced-techniques.md) §1.
68
+ > ⚠️ **Not for `:attr`.** Bindings (`:option`, `:config`, any `v-bind:`) go straight to Vue's compiler as `return (<value>)`: one expression. A `;` (even at the end) or a leading `var`/`return` ⇒ blank page; a function inside the expression is fine. Rules + moving logic to `script.js` + `EXE`: [advanced-techniques.md](advanced-techniques.md) §1.
69
69
 
70
70
  `{{ }}` takes any JS expression:
71
71
 
@@ -1,173 +1,173 @@
1
- # Controls Styling — Approved Layout Vocabulary (use only this)
2
-
3
- > Owns: **approved class/prop/style set for `controls` and `.vue`, and presentation priority order**. Aesthetic mode + `border-radius` limits: [design-modes.md](design-modes.md).
4
-
5
- **Rule #1: do NOT add `style` or `class` to "beautify" UI.** Use only the vocabulary below (extracted from 78 real `module.json` files across 5 projects: contest-score, security-manager, fp, lhu-test, lhu-lib). If the user didn't ask for a specific effect/spacing/color → **add nothing**. FUI layout (grid + Vuetify + project theme) is already consistent.
6
-
7
- > Stats: of 1,284 control objects only ~12% use `attr.style` (almost only for `width`), and **zero custom classes** — 100% standard Vuetify utilities. Common AI mistake: adding `style="padding/margin/border/box-shadow/border-radius/background"` and invented classes → **don't.**
8
-
9
- ---
10
-
11
- ## Principles
12
-
13
- 1. **Default: no style, no class.** Set only `el`, `attr` (v-model/label/:items/:action…), `w`, and `col` when splitting columns.
14
- 2. Spacing/alignment → **Vuetify utility classes** from the tables below, NOT `style`.
15
- 3. `style` only for **fixed `width`** of a filter field (`style: "width:200px"`) — never padding/margin/border/color/background.
16
- 4. Color: component prop (`color="primary"`) or class `primary--text`/`error--text` — no `style="color:#..."`.
17
- 5. **Never** add `border`, `border-radius`, `box-shadow`, `background`, `padding`, custom classes unless the user explicitly asks.
18
- 6. **Compact padding — default `pa-2`** (or `pa-1`/`pa-0`). **NO large padding** (`pa-4`, `pa-6`, `pa-8`...) — wastes screen space. Margin likewise: prefer `-1`/`-2`, avoid `-6`/`-8`.
19
- 7. **When the user _does_ ask for specific presentation — follow this priority**, moving down only when the previous level can't do it:
20
- `component prop` (`color`, `outlined`, `dense`, `elevation`) → `Vuetify utility class` (`pa-*`, `ma-*`, `d-flex`, `text-*`, `primary--text`) → `mw-*` for width → custom class → inline `style` (**last**).
21
-
22
- ---
23
-
24
- ## 1. Container / row `prop` (grid wrapper)
25
-
26
- Outer container and each row use only these tokens (by real frequency):
27
-
28
- | Token | Purpose | Frequency |
29
- | ------------------------------------------------ | --------------------------------------------------------------------- | --------- |
30
- | `grid-list-md` | Standard column gap (container) | very high |
31
- | `fluid` | Full-width container | high |
32
- | `row` | Marks a row | very high |
33
- | `align-center` | **Vertically center** controls in a row, esp. different types/heights | high |
34
- | `justify-center` | Horizontal centering | very high |
35
- | `justify-left` / `justify-end` / `justify-start` | Left / right align | medium |
36
- | `text-center` / `text-left` | Text align | medium |
37
- | `mw-800` `mw-1000` `mw-1200` `mw-1400` | **Limit content width** (FUI class) — centered form/data-entry pages | medium |
38
- | `hidden-container` | Wraps dialogs (v-dialog/f-dialog) — takes no layout space | dialogs |
39
- | `grid-list-lg` | Wider gap than grid-list-md | low |
40
- | `pa-0` / `py-0` | Remove container padding to touch edges | low |
41
- | `page-container` / `page-navigation` | Sticky filter-bar layout (see ui-layout-patterns.md) | rare |
42
-
43
- > **Default formula**: container `"fluid grid-list-md"`, each row `"row"`. Rows mixing control types/heights: `"row wrap align-center"` for a shared vertical axis; `justify-center` is horizontal only. Form pages: add `mw-1000`/`mw-1200`.
44
-
45
- ## 2. `col` (grid column wrapping a control)
46
-
47
- `col` only for width splitting or aligning/shrinking the column:
48
-
49
- | Token | Purpose |
50
- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
51
- | `shrink` | **Fixed-width filter field** (doesn't grow) — most common. **Required whenever `w` is px**; write `"class": "shrink"`, NOT `"shrink": true` (inert on V3) |
52
- | `text-end` / `text-start` / `text-center` | Align content in column |
53
- | `pa-0` | Remove column padding |
54
- | `align-self-center` | Vertical centering |
55
-
56
- Short string `"text-end"` or object `{ "class": "shrink" }`. Add `"v-if": "..."` to `col` to show/hide the whole column by right/condition.
57
-
58
- > Prefer `align-center` on the row to align the whole row. Use `align-self-center` on `col` only when a single control needs fixing. Never compensate vertical offset with margin or inline style.
59
-
60
- ## 3. `attr.class` (on the control itself)
61
-
62
- Vuetify utility classes only:
63
-
64
- | Group | Tokens |
65
- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
66
- | Margin | `mt-1 mt-2 mt-3` · `mb-1 mb-2 mb-4` · `my-2` · `ml-1 ml-2` · `mr-1 mr-2` |
67
- | Padding | `pa-0 pa-2` · `pt-3` · `py-1` — **avoid `pa-4/6/8`** |
68
- | Flex | `d-flex` · `flex-column` · `align-center` · `justify-center` · `justify-end` · `flex-grow-0` · `flex-wrap` · `flex-md-grow-0` |
69
- | Text | `text-center` · `text-left` · `text-right` |
70
- | Size / weight | `text-h6` · `subtitle-1` · `body-2` · `caption` · `font-weight-bold` · `font-weight-medium` |
71
- | Text color (V2) | `primary--text` · `error--text` · `grey--text` · `white--text` — **FUI V2 (Vuetify 1.5) only** (`--text` suffix) |
72
- | Text color (V3) | `text-primary` · `text-error` · `text-grey` · `text-white` — **Vuetify 3** `text-{color}`. Wrong version = no effect, no error |
73
- | Background | prop `color="primary"`, or Vuetify bg classes `primary` · `grey lighten-4` |
74
- | Rounding | `rounded` · `rounded-lg` · `rounded-0` |
75
- | Shadow / flat | prop `elevation="0"` / `flat` / `outlined` on `v-card`/`v-btn` |
76
- | Breakpoint visibility | `d-none d-md-flex`... |
77
- | Size | component props (`max-width`, `width`, `dense`, `small`, `x-large`) — no `style="width:..."` unless fixed px needed. Icons: keep default size, **don't add `small`/`:small`** |
78
- | Scrollbar | `fpScrollbar` — **the only class** for any area with its own scrollbar; see §Scrollbar right below |
79
-
80
- ### Scrollbar — always the built-in `fpScrollbar`
81
-
82
- `fpScrollbar` is predefined in the platform's `projectdefaultstyle.css` (V2 and V3): a thin bar, **hidden until hover**, fading in ~300ms on hover and out ~600ms on leave (thumb `rgba(0,0,0,.25)`, transparent track), working in Firefox and Chrome/Edge/Safari. The class sets `overflow-y: auto` itself. The width is the browser's `thin` (~10px on Chrome ≥121, which ignores `::-webkit-scrollbar` once `scrollbar-color` is set), not the 5px the webkit rule says. The left menu of `HeaderBar` (`.v-navigation-drawer__content`) **already has** this rule; add nothing there.
83
-
84
- **Default — the element that REALLY scrolls gets `fpScrollbar`**, without the user asking: a list box with `max-height`, a side panel, the content area of a card/dialog, a long tag/chip list...
85
-
86
- **Exactly ONE layer — never nest `fpScrollbar`.** Hovering the innermost area also `:hover`s every outer layer and runs their effect too ⇒ the bar shows late and drops frames (measured on a real project: 2 nested layers slowed show/hide from ~300/600ms to ~850/2000ms). Before adding it, ask:
87
-
88
- - Does this element **have a height limit and content that exceeds it**? If not, don't add it; a panel/splitter that only divides the layout (height follows content) is not a scroll area.
89
- - Does an ancestor already scroll (another `fpScrollbar`, **or `.v-navigation-drawer__content`**)? Then pick one layer: usually recompute the inner layer's height to fit (subtracting the tab bar/toolbar above) so the outer layer no longer overflows, and drop scrolling on the outer layer. An outer layer overflowing by a few dozen px means the inner height is miscalculated, not a reason to keep two scroll layers.
90
-
91
- ```json
92
- { "el": "div", "attr": { "class": "fpScrollbar", "style": "max-height: 400px" }, "innerHTML": [ ... ] }
93
- { "el": "v-list", "attr": { "class": "fpScrollbar", "max-height": "300" }, "innerHTML": [ ... ] }
94
- ```
95
-
96
- - The class only styles **the scrollbar**; the element still needs a height limit (`max-height`/`height`, preferably a component prop like `max-height` of `v-list`/`v-card`) to have anything to scroll. No need to add `overflow-y: auto`.
97
- - Same in `.vue`: `class="fpScrollbar"` on the scrolling element.
98
-
99
- **User asks to "redo / prettify / restyle the scrollbar"** → still `fpScrollbar`; **NEVER** hand-write `::-webkit-scrollbar`, `scrollbar-width`, `scrollbar-color`. Each module drawing its own style breaks consistency, and hand-made rules easily break the shared class's show/hide effect. Only when the user explicitly asks for **another color** (e.g. a scrollbar on a dark background) override it, and only the **color variable**, in `header.html`:
100
-
101
- ```css
102
- /* dark background: translucent white thumb, keeps fpScrollbar's show/hide effect */
103
- .my-dark-panel.fpScrollbar:hover {
104
- --fp-scrollbar: rgba(255, 255, 255, 0.3);
105
- }
106
- ```
107
-
108
- The override does **not** touch `transition`. In particular, never add `scrollbar-color` to the transition: the color already goes through the `--fp-scrollbar` variable (which is transitioning), and transitioning `scrollbar-color` too restarts it every frame ⇒ the color changes in steps, 2-3 times slower.
109
-
110
- Do not use `fpScrollbar` for:
111
-
112
- - **`f-table`/`f-sheet`**: the table scrolls internally by `:height` (see [ui-table-cell-patterns.md](ui-table-cell-patterns.md)); putting it on an outer wrapper only adds a nested scrollbar.
113
- - **`HTMLOnly: true` modules**: they do not load `projectdefaultstyle.css`, so the class does not exist (see [module-structure.md](module-structure.md) §HTMLOnly=true). There, writing scrollbar CSS in the module is fine.
114
-
115
- ### Icon size
116
-
117
- - Icons use the component/theme default size.
118
- - **Don't set `small` or `:small`** on `v-icon`, icon buttons, or buttons with icons just to look "compact".
119
- - Shrink only when the user explicitly asks or a specialised component/pattern prescribes it; then set the prop on exactly that component — don't shrink the whole button unintentionally.
120
-
121
- **Before adding a class → check the table above.** Define a custom class only for what Vuetify lacks: complex grid-template, animation, pseudo-element (`::before`), specific child selectors. Put it in `header.html`, clearly prefixed, **minimal count** — don't recreate card/badge/spacing Vuetify already has.
122
-
123
- > `flex-md-grow-0` + `col: { class: "shrink" }` + `style: "width:200px"` = **fixed-width filter field** — standard toolbar pattern ([ui-patterns.md](ui-patterns.md) §4). The only valid place for `style` (width only).
124
-
125
- ---
126
-
127
- ## 4. Width: `w` (NOT style/class)
128
-
129
- | `w` | Meaning |
130
- | -------------------- | -------------------------------------------------------------------------------------- |
131
- | `1`–`12` | Grid columns (12 = full row) |
132
- | `>= 25` (e.g. `250`) | Fixed px width — **requires `col: { "class": "shrink" }`**, otherwise px has no effect |
133
- | `13`–`24` | **Don't use** — dead zone: V2 yields px, V3 yields a non-existent class |
134
- | `""` (empty) | Size to content |
135
-
136
- Always split columns with `w` — no `style="width:..%"`, no invented `col-6`.
137
-
138
- Measurements and reasons: [controls-patterns.md](controls-patterns.md) §Pixel `w`.
139
-
140
- ---
141
-
142
- ## 5. RIGHT / WRONG
143
-
144
- ```json
145
- // ✅ ĐÚNG — gọn, dùng w + prop chuẩn, không style thừa
146
- { "el": "v-text-field", "w": 6, "attr": { "v-model": "hoTen", "label": "Họ tên" } }
147
-
148
- // ❌ SAI — style/class tự chế không cần thiết
149
- { "el": "v-text-field", "w": 6, "attr": {
150
- "v-model": "hoTen", "label": "Họ tên",
151
- "style": "padding:8px; border:1px solid #ddd; border-radius:8px; margin-bottom:12px",
152
- "class": "my-custom-input rounded-lg shadow" } }
153
- ```
154
-
155
- ```json
156
- // ✅ ĐÚNG — card mặc định của Vuetify, không tự style
157
- { "el": "v-card", "attr": { "class": "pa-2" }, "innerHTML": [ ... ] }
158
-
159
- // ❌ SAI — tự vẽ lại card
160
- { "el": "v-card", "attr": { "style": "background:#fff; box-shadow:0 4px 12px rgba(0,0,0,.1); border-radius:16px; padding:24px" } }
161
- ```
162
-
163
- ---
164
-
165
- ## 6. When style/class IS allowed
166
-
167
- Only when the user **explicitly requests** specific presentation (e.g. "grey background for this block", "limit width to 800px", "this text red"). Even then:
168
-
169
- - Prop → Vuetify utility class → `mw-*` for width → `style` last.
170
- - `border-radius` on request: obey limits in [design-modes.md](design-modes.md) (large frame ≤12px, medium ≤8px, small ≤6px).
171
- - No `<style>` in `.vue`; CSS goes in `header.html`.
172
-
173
- > **About to type `style` or name a new class → stop and ask "did the user request this?". If not → drop it.**
1
+ # Controls Styling — Approved Layout Vocabulary (use only this)
2
+
3
+ > Owns: **approved class/prop/style set for `controls` and `.vue`, and presentation priority order**. Aesthetic mode + `border-radius` limits: [design-modes.md](design-modes.md).
4
+
5
+ **Rule #1: do NOT add `style` or `class` to "beautify" UI.** Use only the vocabulary below (extracted from 78 real `module.json` files across 5 projects: contest-score, security-manager, fp, lhu-test, lhu-lib). If the user didn't ask for a specific effect/spacing/color → **add nothing**. FUI layout (grid + Vuetify + project theme) is already consistent.
6
+
7
+ > Stats: of 1,284 control objects only ~12% use `attr.style` (almost only for `width`), and **zero custom classes** — 100% standard Vuetify utilities. Common AI mistake: adding `style="padding/margin/border/box-shadow/border-radius/background"` and invented classes → **don't.**
8
+
9
+ ---
10
+
11
+ ## Principles
12
+
13
+ 1. **Default: no style, no class.** Set only `el`, `attr` (v-model/label/:items/:action…), `w`, and `col` when splitting columns.
14
+ 2. Spacing/alignment → **Vuetify utility classes** from the tables below, NOT `style`.
15
+ 3. `style` only for **fixed `width`** of a filter field (`style: "width:200px"`) — never padding/margin/border/color/background.
16
+ 4. Color: component prop (`color="primary"`) or class `primary--text`/`error--text` — no `style="color:#..."`.
17
+ 5. **Never** add `border`, `border-radius`, `box-shadow`, `background`, `padding`, custom classes unless the user explicitly asks.
18
+ 6. **Compact padding — default `pa-2`** (or `pa-1`/`pa-0`). **NO large padding** (`pa-4`, `pa-6`, `pa-8`...) — wastes screen space. Margin likewise: prefer `-1`/`-2`, avoid `-6`/`-8`.
19
+ 7. **When the user _does_ ask for specific presentation — follow this priority**, moving down only when the previous level can't do it:
20
+ `component prop` (`color`, `outlined`, `dense`, `elevation`) → `Vuetify utility class` (`pa-*`, `ma-*`, `d-flex`, `text-*`, `primary--text`) → `mw-*` for width → custom class → inline `style` (**last**).
21
+
22
+ ---
23
+
24
+ ## 1. Container / row `prop` (grid wrapper)
25
+
26
+ Outer container and each row use only these tokens (by real frequency):
27
+
28
+ | Token | Purpose | Frequency |
29
+ | ------------------------------------------------ | --------------------------------------------------------------------- | --------- |
30
+ | `grid-list-md` | Standard column gap (container) | very high |
31
+ | `fluid` | Full-width container | high |
32
+ | `row` | Marks a row | very high |
33
+ | `align-center` | **Vertically center** controls in a row, esp. different types/heights | high |
34
+ | `justify-center` | Horizontal centering | very high |
35
+ | `justify-left` / `justify-end` / `justify-start` | Left / right align | medium |
36
+ | `text-center` / `text-left` | Text align | medium |
37
+ | `mw-800` `mw-1000` `mw-1200` `mw-1400` | **Limit content width** (FUI class) — centered form/data-entry pages | medium |
38
+ | `hidden-container` | Wraps dialogs (v-dialog/f-dialog) — takes no layout space | dialogs |
39
+ | `grid-list-lg` | Wider gap than grid-list-md | low |
40
+ | `pa-0` / `py-0` | Remove container padding to touch edges | low |
41
+ | `page-container` / `page-navigation` | Sticky filter-bar layout (see ui-layout-patterns.md) | rare |
42
+
43
+ > **Default formula**: container `"fluid grid-list-md"`, each row `"row"`. Rows mixing control types/heights: `"row wrap align-center"` for a shared vertical axis; `justify-center` is horizontal only. Form pages: add `mw-1000`/`mw-1200`.
44
+
45
+ ## 2. `col` (grid column wrapping a control)
46
+
47
+ `col` only for width splitting or aligning/shrinking the column:
48
+
49
+ | Token | Purpose |
50
+ | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
51
+ | `shrink` | **Fixed-width filter field** (doesn't grow) — most common. **Required whenever `w` is px**; write `"class": "shrink"`, NOT `"shrink": true` (inert on V3) |
52
+ | `text-end` / `text-start` / `text-center` | Align content in column |
53
+ | `pa-0` | Remove column padding |
54
+ | `align-self-center` | Vertical centering |
55
+
56
+ Short string `"text-end"` or object `{ "class": "shrink" }`. Add `"v-if": "..."` to `col` to show/hide the whole column by right/condition.
57
+
58
+ > Prefer `align-center` on the row to align the whole row. Use `align-self-center` on `col` only when a single control needs fixing. Never compensate vertical offset with margin or inline style.
59
+
60
+ ## 3. `attr.class` (on the control itself)
61
+
62
+ Vuetify utility classes only:
63
+
64
+ | Group | Tokens |
65
+ | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
66
+ | Margin | `mt-1 mt-2 mt-3` · `mb-1 mb-2 mb-4` · `my-2` · `ml-1 ml-2` · `mr-1 mr-2` |
67
+ | Padding | `pa-0 pa-2` · `pt-3` · `py-1` — **avoid `pa-4/6/8`** |
68
+ | Flex | `d-flex` · `flex-column` · `align-center` · `justify-center` · `justify-end` · `flex-grow-0` · `flex-wrap` · `flex-md-grow-0` |
69
+ | Text | `text-center` · `text-left` · `text-right` |
70
+ | Size / weight | `text-h6` · `subtitle-1` · `body-2` · `caption` · `font-weight-bold` · `font-weight-medium` |
71
+ | Text color (V2) | `primary--text` · `error--text` · `grey--text` · `white--text` — **FUI V2 (Vuetify 1.5) only** (`--text` suffix) |
72
+ | Text color (V3) | `text-primary` · `text-error` · `text-grey` · `text-white` — **Vuetify 3** `text-{color}`. Wrong version = no effect, no error |
73
+ | Background | prop `color="primary"`, or Vuetify bg classes `primary` · `grey lighten-4` |
74
+ | Rounding | `rounded` · `rounded-lg` · `rounded-0` |
75
+ | Shadow / flat | prop `elevation="0"` / `flat` / `outlined` on `v-card`/`v-btn` |
76
+ | Breakpoint visibility | `d-none d-md-flex`... |
77
+ | Size | component props (`max-width`, `width`, `dense`, `small`, `x-large`) — no `style="width:..."` unless fixed px needed. Icons: keep default size, **don't add `small`/`:small`** |
78
+ | Scrollbar | `fpScrollbar` — **the only class** for any area with its own scrollbar; see §Scrollbar right below |
79
+
80
+ ### Scrollbar — always the built-in `fpScrollbar`
81
+
82
+ `fpScrollbar` is predefined in the platform's `projectdefaultstyle.css` (V2 and V3): a thin bar, **hidden until hover**, fading in ~300ms on hover and out ~600ms on leave (thumb `rgba(0,0,0,.25)`, transparent track), working in Firefox and Chrome/Edge/Safari. The class sets `overflow-y: auto` itself. The width is the browser's `thin` (~10px on Chrome ≥121, which ignores `::-webkit-scrollbar` once `scrollbar-color` is set), not the 5px the webkit rule says. The left menu of `HeaderBar` (`.v-navigation-drawer__content`) **already has** this rule; add nothing there.
83
+
84
+ **Default — the element that REALLY scrolls gets `fpScrollbar`**, without the user asking: a list box with `max-height`, a side panel, the content area of a card/dialog, a long tag/chip list...
85
+
86
+ **Exactly ONE layer — never nest `fpScrollbar`.** Hovering the innermost area also `:hover`s every outer layer and runs their effect too ⇒ the bar shows late and drops frames (measured on a real project: 2 nested layers slowed show/hide from ~300/600ms to ~850/2000ms). Before adding it, ask:
87
+
88
+ - Does this element **have a height limit and content that exceeds it**? If not, don't add it; a panel/splitter that only divides the layout (height follows content) is not a scroll area.
89
+ - Does an ancestor already scroll (another `fpScrollbar`, **or `.v-navigation-drawer__content`**)? Then pick one layer: usually recompute the inner layer's height to fit (subtracting the tab bar/toolbar above) so the outer layer no longer overflows, and drop scrolling on the outer layer. An outer layer overflowing by a few dozen px means the inner height is miscalculated, not a reason to keep two scroll layers.
90
+
91
+ ```json
92
+ { "el": "div", "attr": { "class": "fpScrollbar", "style": "max-height: 400px" }, "innerHTML": [ ... ] }
93
+ { "el": "v-list", "attr": { "class": "fpScrollbar", "max-height": "300" }, "innerHTML": [ ... ] }
94
+ ```
95
+
96
+ - The class only styles **the scrollbar**; the element still needs a height limit (`max-height`/`height`, preferably a component prop like `max-height` of `v-list`/`v-card`) to have anything to scroll. No need to add `overflow-y: auto`.
97
+ - Same in `.vue`: `class="fpScrollbar"` on the scrolling element.
98
+
99
+ **User asks to "redo / prettify / restyle the scrollbar"** → still `fpScrollbar`; **NEVER** hand-write `::-webkit-scrollbar`, `scrollbar-width`, `scrollbar-color`. Each module drawing its own style breaks consistency, and hand-made rules easily break the shared class's show/hide effect. Only when the user explicitly asks for **another color** (e.g. a scrollbar on a dark background) override it, and only the **color variable**, in `header.html`:
100
+
101
+ ```css
102
+ /* dark background: translucent white thumb, keeps fpScrollbar's show/hide effect */
103
+ .my-dark-panel.fpScrollbar:hover {
104
+ --fp-scrollbar: rgba(255, 255, 255, 0.3);
105
+ }
106
+ ```
107
+
108
+ The override does **not** touch `transition`. In particular, never add `scrollbar-color` to the transition: the color already goes through the `--fp-scrollbar` variable (which is transitioning), and transitioning `scrollbar-color` too restarts it every frame ⇒ the color changes in steps, 2-3 times slower.
109
+
110
+ Do not use `fpScrollbar` for:
111
+
112
+ - **`f-table`/`f-sheet`**: the table scrolls internally by `:height` (see [ui-table-cell-patterns.md](ui-table-cell-patterns.md)); putting it on an outer wrapper only adds a nested scrollbar.
113
+ - **`HTMLOnly: true` modules**: they do not load `projectdefaultstyle.css`, so the class does not exist (see [module-structure.md](module-structure.md) §HTMLOnly=true). There, writing scrollbar CSS in the module is fine.
114
+
115
+ ### Icon size
116
+
117
+ - Icons use the component/theme default size.
118
+ - **Don't set `small` or `:small`** on `v-icon`, icon buttons, or buttons with icons just to look "compact".
119
+ - Shrink only when the user explicitly asks or a specialised component/pattern prescribes it; then set the prop on exactly that component — don't shrink the whole button unintentionally.
120
+
121
+ **Before adding a class → check the table above.** Define a custom class only for what Vuetify lacks: complex grid-template, animation, pseudo-element (`::before`), specific child selectors. Put it in `header.html`, clearly prefixed, **minimal count** — don't recreate card/badge/spacing Vuetify already has.
122
+
123
+ > `flex-md-grow-0` + `col: { class: "shrink" }` + `style: "width:200px"` = **fixed-width filter field** — standard toolbar pattern ([ui-patterns.md](ui-patterns.md) §4). The only valid place for `style` (width only).
124
+
125
+ ---
126
+
127
+ ## 4. Width: `w` (NOT style/class)
128
+
129
+ | `w` | Meaning |
130
+ | -------------------- | -------------------------------------------------------------------------------------- |
131
+ | `1`–`12` | Grid columns (12 = full row) |
132
+ | `>= 25` (e.g. `250`) | Fixed px width — **requires `col: { "class": "shrink" }`**, otherwise px has no effect |
133
+ | `13`–`24` | **Don't use** — dead zone: V2 yields px, V3 yields a non-existent class |
134
+ | `""` (empty) | Size to content |
135
+
136
+ Always split columns with `w` — no `style="width:..%"`, no invented `col-6`.
137
+
138
+ Measurements and reasons: [controls-patterns.md](controls-patterns.md) §Pixel `w`.
139
+
140
+ ---
141
+
142
+ ## 5. RIGHT / WRONG
143
+
144
+ ```json
145
+ // ✅ ĐÚNG — gọn, dùng w + prop chuẩn, không style thừa
146
+ { "el": "v-text-field", "w": 6, "attr": { "v-model": "hoTen", "label": "Họ tên" } }
147
+
148
+ // ❌ SAI — style/class tự chế không cần thiết
149
+ { "el": "v-text-field", "w": 6, "attr": {
150
+ "v-model": "hoTen", "label": "Họ tên",
151
+ "style": "padding:8px; border:1px solid #ddd; border-radius:8px; margin-bottom:12px",
152
+ "class": "my-custom-input rounded-lg shadow" } }
153
+ ```
154
+
155
+ ```json
156
+ // ✅ ĐÚNG — card mặc định của Vuetify, không tự style
157
+ { "el": "v-card", "attr": { "class": "pa-2" }, "innerHTML": [ ... ] }
158
+
159
+ // ❌ SAI — tự vẽ lại card
160
+ { "el": "v-card", "attr": { "style": "background:#fff; box-shadow:0 4px 12px rgba(0,0,0,.1); border-radius:16px; padding:24px" } }
161
+ ```
162
+
163
+ ---
164
+
165
+ ## 6. When style/class IS allowed
166
+
167
+ Only when the user **explicitly requests** specific presentation (e.g. "grey background for this block", "limit width to 800px", "this text red"). Even then:
168
+
169
+ - Prop → Vuetify utility class → `mw-*` for width → `style` last.
170
+ - `border-radius` on request: obey limits in [design-modes.md](design-modes.md) (large frame ≤12px, medium ≤8px, small ≤6px).
171
+ - No `<style>` in `.vue`; CSS goes in `header.html`.
172
+
173
+ > **About to type `style` or name a new class → stop and ask "did the user request this?". If not → drop it.**
@@ -72,9 +72,9 @@ Without `--sql-server`/`--database`, `fui db add` reads both from the connection
72
72
 
73
73
  **Naming:** pass `--name <connectionName>` for a specific name. Omitted, fui suggests `apiName` first; if taken, adds the zone from `apiDomain` (`docs` → `docs_lhbs`); still taken, adds the database and a number. Names take letters, digits, `.`, `_`, `-` and are lowercased. The same server + database added again keeps its existing name.
74
74
 
75
- **v1 compatibility, no silent moves:** an old record without `apiName` reads as `apiName = connectionName = key`. fui never renames old SQL folders. A new connection can take its own name, so an alias repeated across zones can sit next to the old record.
75
+ **v1 compatibility, no folder moves:** an old record without `apiName` reads as `apiName = connectionName = key`, and the file is rewritten as version 2 the first time fui reads it (old file kept in `_history/`). fui never renames old SQL folders. A new connection can take its own name, so an alias repeated across zones can sit next to the old record.
76
76
 
77
- > **CRITICAL — `apiDomain` has two meanings:** `_connections.json` → bare host (`api.example3.vn`); `project.json` `data.apiDomain` → full base URL with apiName + trailing `/` (`https://tapi.example.vn/acc/`). Top bug: bare host + apiName without `/` → `tapi.lhu.edu.vnts` instead of `tapi.lhu.edu.vn/ts`. See [tapi-reference.md](tapi-reference.md) §3. Don't build URLs; copy the `Wiring URL` line from `fui sp verify`/`fui sp help`.
77
+ > **CRITICAL — `apiDomain` has two meanings:** `_connections.json` → bare host (`api.example3.vn`); `project.json` `data.apiDomain` → full base URL with apiName + trailing `/` (`https://tapi.example.vn/acc/`). Top bug: bare host + apiName without `/` → `tapi.lhu.edu.vnts` instead of `tapi.lhu.edu.vn/ts`. See [tapi-reference.md](tapi-reference.md) §3. Don't build URLs; copy the `url (reference only, not called; copy verbatim)` line from `fui sp verify`/`fui sp help`.
78
78
 
79
79
  ## 2. Finding connection info
80
80
 
@@ -121,7 +121,7 @@ Never extract UID/PWD by hand (PowerShell etc.) — exposes credentials.
121
121
 
122
122
  ```
123
123
  1. fui project sync -p <projectId> → local project.json (apiDomain source)
124
- 2. fui use -p <projectId> → project implied afterwards
124
+ 2. cd {workspace}/<projectId> → project implied afterwards (or pass -p <projectId> each time)
125
125
  3. fui db alias list → aliases you may manage
126
126
  4. fui db add-by-name <apiName> → lookup + token + test + save + schema (do NOT run fui schema pull after)
127
127
  5. Module uses other aliases? Repeat 4 per alias. Added connections do NOT become default (use --default).
@@ -151,7 +151,7 @@ Pass secrets only via stdin (`-`) or `--*-env VAR`.
151
151
 
152
152
  ## 4. `fui db add`
153
153
 
154
- - Saved to `{workspace}/{projectId}/_db/_connections.json` under `apiName`. projectId ≠ apiName; a project can have many apiNames — never derive one from the other.
154
+ - Saved to `{workspace}/{projectId}/_db/_connections.json` under its connectionName (`--name`, suggested from `apiName` otherwise); `apiName` is a field of the record. projectId ≠ apiName; a project can have many apiNames — never derive one from the other.
155
155
  - **Mandatory:** projectId not stated, or alias's project unclear → ask _"Which project should this DB connection be saved to? (projectId)"_ before `fui db add`. Wrong project misplaces schema/SPs.
156
156
 
157
157
  ```
@@ -167,7 +167,7 @@ fui db add <apiName> -p <project> --token - --api-domain host [--name connection
167
167
 
168
168
  UID/PWD changed, Server/Database unchanged:
169
169
 
170
- 1. `fui db list -p <projectId>` — confirm the connection has `sqlServer` and `database` (missing → full `fui db add` instead).
170
+ 1. `fui token decode --db <connectionName>` — confirm the stored token has a server and a database (missing → full `fui db add` instead). The password is never shown.
171
171
  2. `fui db token-rebuild --db <connectionName> --uid <login> --pwd -` — rebuilds, tests, saves.
172
172
 
173
173
  `--db` REQUIRED with >1 connection (overwrites credentials; wrong guess clobbers another DB's token).
@@ -240,15 +240,15 @@ Prints `📋 Archived orphaned local SP file(s)...` when archiving. Name match i
240
240
  Do NOT try URL variants.
241
241
  7. fui sp verify <name> [--params …] → STATIC contract check (read or write API); calls NO endpoint.
242
242
  Reads real SP body → response shape, error contract, read/write,
243
- apiMocks snippet for fui module simulate. REQUIRED before writing
244
- apiMocks for an existing SP — never invent structure;
243
+ --mocks snippet for fui module simulate. REQUIRED before writing
244
+ --mocks for an existing SP — never invent structure;
245
245
  fui module simulate warns on unverified mocks matching real SPs.
246
246
  ```
247
247
 
248
248
  - `--db` omitted → inferred from SP name (schema + local `.sql` per connection). One match → silent; zero or several (same name in two DBs) → connection menu, pick and re-run, don't guess.
249
249
  - `fui sp save` for a brand-new SP can't infer: multi-connection projects must pass `--db` on first save.
250
250
 
251
- > `fui sp verify` reads the real body, not the name; hard error if unreadable. Write-API testing with `fui module simulate`: [tools-registry.md](tools-registry.md). Guards on `DROP`/`ALTER TABLE`, `DELETE`/`UPDATE` without `WHERE` are enforced in code, no bypass. Writes via `fui query`/`fui exec` need `--confirm-write`.
251
+ > `fui sp verify` reads the real body, not the name; hard error if unreadable. Write-API testing with `fui module simulate`: [tools-registry.md](tools-registry.md). Guards on `DROP TABLE`/`DROP DATABASE`/`ALTER TABLE`, `xp_cmdshell`, `DELETE`/`UPDATE` without `WHERE` are enforced in code, no bypass. `sp deploy` also refuses a file that is not exactly one definition of the named object. Writes via `fui query`/`fui exec` need `--confirm-write`.
252
252
 
253
253
  **Rollback:** read `{workspace}/_history/{projectId}/_db/{connectionName}/{name}_{timestamp}.sql` → `fui sp save <name> -f <old file>` → `fui sp deploy <name>`.
254
254
 
@@ -272,7 +272,7 @@ EXEC XDATA23.Calendar2015.dbo.spIO_IN_DeleteMonHocInfo @MonHocID, @ErrorDKMH OUT
272
272
 
273
273
  ## 9. List connections
274
274
 
275
- `fui db list -p <projectId>` — every connection: connectionName (`db`), `apiName`, `apiDomain`, `sqlServer`, `database`, `_db/{connectionName}/`, dbToken/userToken present or not (never values), which is `default`. Run first when unsure which `--db` to use. Also lists `_db/` folders with no connection (leftovers, nothing reads them) — delete manually when sure.
275
+ `fui db list -p <projectId>` — every connection: `db` (`project/connectionName`, also the `_db/{connectionName}/` folder), `engine`, `default` (`*`), `apiDomain`, `database`, `userToken` (yes/no, never the value), `apiName`. Run first when unsure which `--db` to use. Server and login of one connection: `fui token decode --db <connectionName>`. `_db/` folders with no connection are not listed; nothing reads them.
276
276
 
277
277
  ## 10. Cheat sheet
278
278