@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.
- package/README.md +27 -7
- package/dist/fui-qrb1j9jz.js +397 -0
- package/dist/fui.js +1 -1
- package/package.json +1 -1
- package/skills/fui/SKILL.md +143 -38
- package/skills/fui/agents/fui-builder.md +62 -0
- package/skills/fui/agents/fui-tester.md +77 -0
- package/skills/{fui-skill → fui}/assets/projectdefaultstyle.css +489 -489
- package/skills/{fui-skill → fui}/examples/component.vue +162 -162
- package/skills/{fui-skill → fui}/references/INDEX.md +5 -4
- package/skills/{fui-skill → fui}/references/advanced-techniques.md +170 -168
- package/skills/{fui-skill → fui}/references/component-design.md +2 -2
- package/skills/{fui-skill → fui}/references/component-quickref.md +78 -78
- package/skills/{fui-skill → fui}/references/component-table.md +287 -287
- package/skills/{fui-skill → fui}/references/components-dialog.md +191 -191
- package/skills/{fui-skill → fui}/references/components-display.md +141 -141
- package/skills/{fui-skill → fui}/references/components-echart.md +316 -316
- package/skills/{fui-skill → fui}/references/components-input.md +335 -335
- package/skills/{fui-skill → fui}/references/controls-patterns.md +2 -2
- package/skills/{fui-skill → fui}/references/controls-styling-vocabulary.md +173 -173
- package/skills/{fui-skill → fui}/references/db-workflow.md +9 -9
- package/skills/{fui-skill → fui}/references/default-function.md +456 -456
- package/skills/{fui-skill → fui}/references/design-modes.md +57 -57
- package/skills/{fui-skill → fui}/references/echart-templates.md +489 -489
- package/skills/{fui-skill → fui}/references/fastproject.md +99 -99
- package/skills/{fui-skill → fui}/references/fullstack-workflow.md +314 -313
- package/skills/{fui-skill → fui}/references/module-data-patterns.md +117 -117
- package/skills/{fui-skill → fui}/references/module-json-anatomy.md +132 -132
- package/skills/{fui-skill → fui}/references/module-structure.md +144 -144
- package/skills/{fui-skill → fui}/references/new-session.md +1 -2
- package/skills/{fui-skill → fui}/references/pdfmake.md +60 -60
- package/skills/{fui-skill → fui}/references/permission-system.md +1 -1
- package/skills/{fui-skill → fui}/references/platform-architecture.md +269 -269
- package/skills/{fui-skill → fui}/references/project-config.md +303 -303
- package/skills/{fui-skill → fui}/references/project-provisioning.md +4 -4
- package/skills/{fui-skill → fui}/references/script-map.md +262 -262
- package/skills/{fui-skill → fui}/references/sql-clr-functions.md +225 -225
- package/skills/{fui-skill → fui}/references/system-design.md +2 -2
- package/skills/{fui-skill → fui}/references/tapi-permission-patterns.md +156 -156
- package/skills/{fui-skill → fui}/references/tapi-reference.md +474 -474
- package/skills/fui/references/team-run.md +141 -0
- package/skills/fui/references/tools-registry.md +211 -0
- package/skills/{fui-skill → fui}/references/ui-crosswindow-patterns.md +321 -321
- package/skills/{fui-skill → fui}/references/ui-dialog-patterns.md +255 -255
- package/skills/{fui-skill → fui}/references/ui-layout-patterns.md +176 -176
- package/skills/{fui-skill → fui}/references/ui-patterns.md +303 -303
- package/skills/{fui-skill → fui}/references/ui-screenshot-review.md +95 -95
- package/skills/{fui-skill → fui}/references/ui-table-cell-patterns.md +318 -318
- package/skills/{fui-skill → fui}/references/verification.md +11 -11
- package/skills/{fui-skill → fui}/references/watcher-patterns.md +163 -163
- package/skills/{fui-skill → fui}/references/websocket-realtime.md +271 -271
- package/skills/{fui-skill → fui}/scripts/component-3.0.js +2715 -2715
- package/skills/{fui-skill → fui}/scripts/component.js +3147 -3147
- package/skills/{fui-skill → fui}/scripts/componentTable-3.0.js +1038 -1038
- package/skills/{fui-skill → fui}/scripts/componentTable.js +891 -891
- package/skills/{fui-skill → fui}/scripts/defaultfunction-3.0.js +866 -866
- package/skills/{fui-skill → fui}/scripts/defaultfunction.js +1051 -1051
- package/skills/{fui-skill → fui}/scripts/fastproject-3.0.js +870 -870
- package/skills/{fui-skill → fui}/scripts/fastproject.js +828 -828
- package/dist/fui-h4z7zhk2.js +0 -381
- package/skills/fui-skill/SKILL.md +0 -139
- package/skills/fui-skill/references/tools-registry.md +0 -84
- /package/skills/{fui-skill → fui}/assets/projectdefaultstyle-3.0.css +0 -0
- /package/skills/{fui-skill → fui}/design-md/airbnb/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/airbnb/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/airtable/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/airtable/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/apple/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/apple/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/asu/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/asu/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/binance/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/binance/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw-m/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw-m/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bugatti/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bugatti/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cal/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cal/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/claude/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/claude/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clay/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clay/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clickhouse/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clickhouse/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cohere/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cohere/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/coinbase/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/coinbase/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/composio/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/composio/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cursor/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cursor/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/elevenlabs/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/elevenlabs/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/expo/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/expo/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ferrari/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ferrari/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/figma/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/figma/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/framer/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/framer/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/fui/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/hashicorp/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/hashicorp/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ibm/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ibm/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/intercom/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/intercom/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/kraken/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/kraken/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lamborghini/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lamborghini/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/linear.app/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/linear.app/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lovable/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lovable/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mastercard/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mastercard/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/meta/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/meta/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/minimax/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/minimax/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mintlify/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mintlify/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/miro/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/miro/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mistral.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mistral.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mongodb/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mongodb/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nike/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nike/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/notion/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/notion/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nvidia/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nvidia/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ollama/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ollama/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/opencode.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/opencode.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/pinterest/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/pinterest/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/playstation/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/playstation/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/posthog/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/posthog/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/raycast/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/raycast/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/renault/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/renault/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/replicate/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/replicate/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/resend/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/resend/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/revolut/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/revolut/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/runwayml/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/runwayml/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sanity/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sanity/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sentry/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sentry/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/shopify/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/shopify/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/slack/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spacex/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spacex/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spotify/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spotify/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/starbucks/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/starbucks/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/stripe/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/stripe/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/supabase/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/supabase/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/superhuman/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/superhuman/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/tesla/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/tesla/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/theverge/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/theverge/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/together.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/together.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/uber/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/uber/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vercel/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vercel/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vodafone/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vodafone/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/voltagent/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/voltagent/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/warp/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/warp/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/webflow/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/webflow/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wired/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wired/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wise/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wise/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/x.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/x.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/zapier/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/zapier/README.md +0 -0
- /package/skills/{fui-skill → fui}/examples/f-table-patterns.json +0 -0
- /package/skills/{fui-skill → fui}/examples/module-patterns.json +0 -0
- /package/skills/{fui-skill → fui}/examples/project-patterns.json +0 -0
- /package/skills/{fui-skill → fui}/references/coding-standards.md +0 -0
- /package/skills/{fui-skill → fui}/references/db-table-design.md +0 -0
- /package/skills/{fui-skill → fui}/references/fsheet.md +0 -0
- /package/skills/{fui-skill → fui}/references/tapi-file-api.md +0 -0
- /package/skills/{fui-skill → fui}/references/ui-templates.md +0 -0
- /package/skills/{fui-skill → fui}/scripts/fechart.js +0 -0
- /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 —
|
|
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
|
|
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
|
|
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 `
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
244
|
-
|
|
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:
|
|
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
|
|