@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
|
@@ -30,21 +30,21 @@ Tiers differ ~100× in cost per run:
|
|
|
30
30
|
| `Unknown custom element: <f-…>` / empty component | **1** | wrong name or missing required import |
|
|
31
31
|
| Chart draws nothing | **1** (missing `echarts.min.js`/`fechart.js`, most cases) → **5** (0-size canvas) | |
|
|
32
32
|
| Misaligned columns, cramped row | **2** (Σw > 12) | |
|
|
33
|
-
| Dialog won't open | **2** (`⚠
|
|
33
|
+
| Dialog won't open | **2** (`⚠ no opener`) → **3** | |
|
|
34
34
|
| Wrong action order · infinite watch loop · CONFIRM/MESS wrong branch | **3** | |
|
|
35
35
|
| Wrong numbers · `undefined`/`NaN` on page | **3** | almost always `OUT`/mapData on the wrong key, not display |
|
|
36
36
|
| API returns unexpected shape | `fui sp verify` (static, **calls no endpoint**) | infers from the real SP body + generates `--mocks` for tier 3. Only metadata reads: `OBJECT_DEFINITION` when no local `.sql`, `sys.database_permissions` with `--check-grant`; never runs the SP |
|
|
37
37
|
| One `uc-*.vue` wrong | **4** | once proven, remaining bugs are assembly |
|
|
38
38
|
| Horizontal overflow · wrong height · leaking style · bad spacing · broken on mobile | **5** | only tier seeing geometry |
|
|
39
|
-
| Import/asset not loaded | **5**, section `⛔
|
|
40
|
-
| Suspected "platform library bug" | **5**, check `⛔
|
|
39
|
+
| Import/asset not loaded | **5**, section `⛔ MISSING ENV FILES` | environment error — **don't** edit the module for it |
|
|
40
|
+
| Suspected "platform library bug" | **5**, check `⛔ STALE, UNVERIFIED ASSETS` FIRST | stale asset ⇒ on-disk copy ran; CDN conclusions worthless |
|
|
41
41
|
| **Permission-gated** button/menu hidden for real users but shown in preview | **1** after `fui right function list` | undefined code ⇒ `rightTest()` always false, no JS error. Tiers 3/5 **pre-grant** scanned codes so they always show — [permission-system.md](permission-system.md) |
|
|
42
42
|
|
|
43
|
-
**Stale assets:** `--render` revalidates every asset per run (conditional GET; trace shows `N revalidate 304`). `⛔
|
|
43
|
+
**Stale assets:** `--render` revalidates every asset per run (conditional GET; trace shows `N revalidate 304`). `⛔ STALE, UNVERIFIED ASSETS` = network failed, on-disk copy ran ⇒ "library X has a bug" from that run is **unusable**; reconnect and re-render, or `--refresh-assets`. `ℹ️ PLATFORM ASSETS CHANGED` = platform file changed between runs — behavior changed without a `module.json` edit ⇒ look there first.
|
|
44
44
|
|
|
45
|
-
**Read the `--render` report top-down:** `═══
|
|
45
|
+
**Read the `--render` report top-down:** `═══ DIAGNOSIS ═══` groups failures by **where to fix** — `[ENV]` (fix first; the others may be consequences) · `[MODULE]` (module.json/script.js/component) · `[VIEW]` (DOM audit + image). `↪` = cheaper tier that should have caught it.
|
|
46
46
|
|
|
47
|
-
**STOP re-rendering** when `Δ
|
|
47
|
+
**STOP re-rendering** when `Δ vs previous run` shows _no metric changed_, or the report carries a `🛑 STUCK` block: the fix missed the bug. Drop to tiers 1–3 to learn why, then return.
|
|
48
48
|
|
|
49
49
|
---
|
|
50
50
|
|
|
@@ -115,10 +115,10 @@ Tiers differ ~100× in cost per run:
|
|
|
115
115
|
- [ ] Read each warning — may be intentional (unlisted component, optional param...), never skip unread
|
|
116
116
|
- [ ] **`[design]`** warnings (colored dialog toolbar, red delete button, odd dialog width, no floating label, delete without CONFIRM, redundant Close button, hand-built v-snackbar/v-alert instead of MESS/MESSBOX, main f-table/f-sheet without `":height": -16` so the page scrolls instead of the table...) — **must fix** unless the user explicitly asked otherwise this session
|
|
117
117
|
- [ ] Strict mode (`FUI_STRICT=1`): errors BLOCK publish
|
|
118
|
-
- [ ] `fui module outline`, READ the tree: row Σw ≤12? `⚠
|
|
118
|
+
- [ ] `fui module outline`, READ the tree: row Σw ≤12? `⚠ no opener`? `⚠ never called`? `⚠ unused in controls/watch/actions`?
|
|
119
119
|
- [ ] Complex runtime logic (action chain, watch cascade, dialog flow): `fui module simulate` with main-flow `--scenario` + `--mocks` (shape from `fui sp verify` snippet) + asserts — fix every `⚠` and ✘ first. Your `--mocks` always win; a static API with NO mock whose SP is confirmed read-only (`fui sp verify`/local `.sql`) is called for real (max 8 calls/run, cached in `_testdata/`) instead of `{data:[]}` — see the "Live/Cache data" line. SPs with writes are never called
|
|
120
|
-
- [ ] **`--mocks` for existing SPs come from `fui sp verify`, never guessed** — on `🚨
|
|
121
|
-
- [ ] **Final visual QA (Playwright available)**: `fui module simulate --render --vue-data '{..demo data..}' --mocks '{...}'`. READ the
|
|
120
|
+
- [ ] **`--mocks` for existing SPs come from `fui sp verify`, never guessed** — on `🚨 UNVERIFIED MOCK SHAPE`, stop, run `fui sp verify`, copy its `--mocks snippet` verbatim (no hand edits). Invented shapes only for SPs not yet deployed
|
|
121
|
+
- [ ] **Final visual QA (Playwright available)**: `fui module simulate --render --vue-data '{..demo data..}' --mocks '{...}'`. OPEN and READ the image files the report lists (`Screenshot:`, `Full-page:`; nothing is attached to the output) + DOM audit (empty app, overflow, "undefined"/"NaN", broken image, 0-size chart canvas) + JS errors/`[Vue warn]` + final vueData; fix and re-render until clean. **`⛔ MISSING ENV FILES` first**: environment error — run its suggested command (`fui project sync`/`fui module get`); errors below may be consequences, don't edit module.json for them. How to read screenshots: `ui-screenshot-review.md`. Chart/animation missing → raise `--settle-ms` (e.g. 1500). Open dialog captured as `render-dialog.jpg`. Mobile use → extra run `--device mobile`. No Playwright → skip (command returns install steps)
|
|
122
122
|
- [ ] **Changed project level + module level** (project component/import, `project.json`/menu): publish **project level FIRST**, then render — `--render` loads `$projectData`, project components and project CSS from the server (`_aset`); module-level parts render locally (iterate freely, publish later). **Project components are hard-blocked** (no bypass flag) if a local `uc-*.vue` differs from the server bundle: `fui component preview` → `fui component new`/`update` → render. Blocked because server is newer (web IDE edit) → `fui project sync`, never push over
|
|
123
123
|
|
|
124
124
|
### Publish — only what changed
|
|
@@ -151,7 +151,7 @@ For `fui module publish html`: preview ran and diff reviewed · user explicitly
|
|
|
151
151
|
- [ ] Permission check at top: `IF @sys_SystemRight < N BEGIN RAISERROR(N'[Unauthorized]Bạn không có quyền...', 16, 1) RETURN END` — severity always `16`; `[Unauthorized]` prefix required so tAPI returns HTTP 401 ([tapi-reference.md](tapi-reference.md) §5)
|
|
152
152
|
- [ ] No SQL string concatenation (parameterize)
|
|
153
153
|
- [ ] `.sql` has **NO `GRANT EXECUTE`** — tAPI sends one batch, no `GO`, so a GRANT after `END` is **swallowed into the SP body** and runs on every call, silently. `fui sp deploy` grants new procedures and **re-queries `sys.database_permissions` to confirm**
|
|
154
|
-
- [ ] `fui sp deploy` shows
|
|
154
|
+
- [ ] `fui sp deploy` shows `GRANT EXECUTE ... TO [public]: applied` (new SP) or `[public] has EXECUTE on "<sp>"`; on `grant FAILED` or `[public] lacks EXECUTE` run exactly the printed `fui exec "GRANT ..."` (**don't redeploy**). `fui exec` deploys have no auto-grant (fui warns with the command). Later doubts: `fui sp verify <name> --check-grant`
|
|
155
155
|
|
|
156
156
|
### Logic
|
|
157
157
|
|
|
@@ -216,7 +216,7 @@ For `fui module publish html`: preview ran and diff reviewed · user explicitly
|
|
|
216
216
|
|
|
217
217
|
1. **Re-read every file changed in that publish first**; don't jump elsewhere
|
|
218
218
|
2. Looks right ⇒ `fui module preview` for server vs local diff
|
|
219
|
-
3. Preview shows `
|
|
219
|
+
3. Preview shows `controls 0, data keys 0` ⇒ server holds a broken old version — push again
|
|
220
220
|
4. Only when local is confirmed correct: check other files (header.html, script.js, imports)
|
|
221
221
|
|
|
222
222
|
---
|
|
@@ -1,163 +1,163 @@
|
|
|
1
|
-
# Watcher Patterns
|
|
2
|
-
|
|
3
|
-
> Owns: **`watch`: `v_old`, deep-watch, cascading, race control**. Action syntax: [controls-patterns.md](controls-patterns.md).
|
|
4
|
-
|
|
5
|
-
Use for multi-level dependent forms (Province → District → Ward) and attribute filters (status, type, date, keyword...). Goals: correct FUI structure (`data`, `watch`, `controls`, `set`), no watcher loops, fewer redundant API calls, maintainable large modules.
|
|
6
|
-
|
|
7
|
-
## 1. Rules
|
|
8
|
-
|
|
9
|
-
1. **Watch the smallest input key** — an ID or filter field (`provinceID`, `districtID`, `filter.status`), not a large object unless needed.
|
|
10
|
-
2. **Split actions by level** — `handleProvinceChange` only resets dependents and calls `apiLoadDistricts`; `handleDistrictChange` only resets the level below and calls `apiLoadWards`; `applyFilters` holds the final list-loading logic.
|
|
11
|
-
3. **`deep-watch` selectively** — only small filter objects (e.g. `filter`). Never deep-watch large arrays like `items`, `tableData`.
|
|
12
|
-
4. **No self-made loops** — never watch the action's own output var; never watch `items` and write `items` in the callback.
|
|
13
|
-
5. **Guard before API calls** — `IF/THEN/ELSE` to validate input (e.g. no province selected → don't load districts).
|
|
14
|
-
6. **Debounce/race control in `script.js`** — watchers only orchestrate; debounce and stale-response-overwrite protection live in JS helpers.
|
|
15
|
-
7. **Force literal strings only via `IN`** — in an action with `IN`, a string without spaces may be read as an expression → use `` `value`` or `"'value'"`. Full rules: [controls-patterns.md §0](controls-patterns.md#0-value-resolution-rules-fundamental).
|
|
16
|
-
|
|
17
|
-
## 2. Pattern A: administrative-area cascade
|
|
18
|
-
|
|
19
|
-
```json
|
|
20
|
-
{
|
|
21
|
-
"data": [
|
|
22
|
-
{
|
|
23
|
-
"formData": {
|
|
24
|
-
"provinceID": null,
|
|
25
|
-
"districtID": null,
|
|
26
|
-
"wardID": null
|
|
27
|
-
},
|
|
28
|
-
"provinceList": [],
|
|
29
|
-
"districtList": [],
|
|
30
|
-
"wardList": []
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
"apiLoadProvinces": {
|
|
34
|
-
"API": "/api/location/provinces",
|
|
35
|
-
"OUT": "provinceList"
|
|
36
|
-
},
|
|
37
|
-
"apiLoadDistricts": {
|
|
38
|
-
"API": "/api/location/districts",
|
|
39
|
-
"IN": { "ProvinceID": "formData.provinceID" },
|
|
40
|
-
"OUT": "districtList"
|
|
41
|
-
},
|
|
42
|
-
"apiLoadWards": {
|
|
43
|
-
"API": "/api/location/wards",
|
|
44
|
-
"IN": { "DistrictID": "formData.districtID" },
|
|
45
|
-
"OUT": "wardList"
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
"handleProvinceChange": [
|
|
50
|
-
{
|
|
51
|
-
"districtList": [],
|
|
52
|
-
"wardList": [],
|
|
53
|
-
"formData.districtID": null,
|
|
54
|
-
"formData.wardID": null
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
"IF": "formData.provinceID",
|
|
58
|
-
"THEN": { "CALL": "apiLoadDistricts" }
|
|
59
|
-
}
|
|
60
|
-
],
|
|
61
|
-
"handleDistrictChange": [
|
|
62
|
-
{
|
|
63
|
-
"wardList": [],
|
|
64
|
-
"formData.wardID": null
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
"IF": "formData.districtID",
|
|
68
|
-
"THEN": { "CALL": "apiLoadWards" }
|
|
69
|
-
}
|
|
70
|
-
]
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
"CALL": "apiLoadProvinces"
|
|
74
|
-
}
|
|
75
|
-
],
|
|
76
|
-
"watch": {
|
|
77
|
-
"formData.provinceID": { "CALL": "handleProvinceChange" },
|
|
78
|
-
"formData.districtID": { "CALL": "handleDistrictChange" }
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
Key points: watch specific keys; reset lower levels before calling their API; guard against calling with null.
|
|
84
|
-
|
|
85
|
-
## 3. Pattern B: filter object + deep-watch
|
|
86
|
-
|
|
87
|
-
```json
|
|
88
|
-
{
|
|
89
|
-
"data": [
|
|
90
|
-
{
|
|
91
|
-
"filter": {
|
|
92
|
-
"keyword": "",
|
|
93
|
-
"status": null,
|
|
94
|
-
"fromDate": null,
|
|
95
|
-
"toDate": null
|
|
96
|
-
},
|
|
97
|
-
"items": []
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
"apiLoadItems": {
|
|
101
|
-
"API": "/api/items/search",
|
|
102
|
-
"IN": {
|
|
103
|
-
"Keyword": "filter.keyword",
|
|
104
|
-
"Status": "filter.status",
|
|
105
|
-
"FromDate": "filter.fromDate",
|
|
106
|
-
"ToDate": "filter.toDate"
|
|
107
|
-
},
|
|
108
|
-
"OUT": "items"
|
|
109
|
-
}
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
"handleFilterChanged": {
|
|
113
|
-
"CALL": "apiLoadItems"
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
],
|
|
117
|
-
"watch": {
|
|
118
|
-
"deep-watch": {
|
|
119
|
-
"filter": { "CALL": "handleFilterChanged" }
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
Keep `filter` small and stable. Continuous text-search input → debounce in `script.js`.
|
|
126
|
-
|
|
127
|
-
## 4. Recommended debounce (script.js)
|
|
128
|
-
|
|
129
|
-
```javascript
|
|
130
|
-
var filterTimer = null;
|
|
131
|
-
|
|
132
|
-
function debounceFilter(input) {
|
|
133
|
-
var callActionName = input && input.callActionName;
|
|
134
|
-
var waitMs = input && input.waitMs;
|
|
135
|
-
clearTimeout(filterTimer);
|
|
136
|
-
filterTimer = setTimeout(function () {
|
|
137
|
-
CALL(vueData[callActionName]);
|
|
138
|
-
}, waitMs || 350);
|
|
139
|
-
}
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Action in `module.json`:
|
|
143
|
-
|
|
144
|
-
```json
|
|
145
|
-
{
|
|
146
|
-
"handleFilterChanged": {
|
|
147
|
-
"FUN": "debounceFilter",
|
|
148
|
-
"IN": {
|
|
149
|
-
"callActionName": "'apiLoadItems'",
|
|
150
|
-
"waitMs": 350
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
## 5. Watcher QA checklist
|
|
157
|
-
|
|
158
|
-
1. Watch key small enough (ID/field, not a large object)?
|
|
159
|
-
2. Dependents reset before calling the API?
|
|
160
|
-
3. `IF` guard before API when input is null/empty?
|
|
161
|
-
4. Any watcher loop risk?
|
|
162
|
-
5. Text input needs debounce?
|
|
163
|
-
6. Action with `IN`: literal string rule applied to space-free text values?
|
|
1
|
+
# Watcher Patterns
|
|
2
|
+
|
|
3
|
+
> Owns: **`watch`: `v_old`, deep-watch, cascading, race control**. Action syntax: [controls-patterns.md](controls-patterns.md).
|
|
4
|
+
|
|
5
|
+
Use for multi-level dependent forms (Province → District → Ward) and attribute filters (status, type, date, keyword...). Goals: correct FUI structure (`data`, `watch`, `controls`, `set`), no watcher loops, fewer redundant API calls, maintainable large modules.
|
|
6
|
+
|
|
7
|
+
## 1. Rules
|
|
8
|
+
|
|
9
|
+
1. **Watch the smallest input key** — an ID or filter field (`provinceID`, `districtID`, `filter.status`), not a large object unless needed.
|
|
10
|
+
2. **Split actions by level** — `handleProvinceChange` only resets dependents and calls `apiLoadDistricts`; `handleDistrictChange` only resets the level below and calls `apiLoadWards`; `applyFilters` holds the final list-loading logic.
|
|
11
|
+
3. **`deep-watch` selectively** — only small filter objects (e.g. `filter`). Never deep-watch large arrays like `items`, `tableData`.
|
|
12
|
+
4. **No self-made loops** — never watch the action's own output var; never watch `items` and write `items` in the callback.
|
|
13
|
+
5. **Guard before API calls** — `IF/THEN/ELSE` to validate input (e.g. no province selected → don't load districts).
|
|
14
|
+
6. **Debounce/race control in `script.js`** — watchers only orchestrate; debounce and stale-response-overwrite protection live in JS helpers.
|
|
15
|
+
7. **Force literal strings only via `IN`** — in an action with `IN`, a string without spaces may be read as an expression → use `` `value`` or `"'value'"`. Full rules: [controls-patterns.md §0](controls-patterns.md#0-value-resolution-rules-fundamental).
|
|
16
|
+
|
|
17
|
+
## 2. Pattern A: administrative-area cascade
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"data": [
|
|
22
|
+
{
|
|
23
|
+
"formData": {
|
|
24
|
+
"provinceID": null,
|
|
25
|
+
"districtID": null,
|
|
26
|
+
"wardID": null
|
|
27
|
+
},
|
|
28
|
+
"provinceList": [],
|
|
29
|
+
"districtList": [],
|
|
30
|
+
"wardList": []
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"apiLoadProvinces": {
|
|
34
|
+
"API": "/api/location/provinces",
|
|
35
|
+
"OUT": "provinceList"
|
|
36
|
+
},
|
|
37
|
+
"apiLoadDistricts": {
|
|
38
|
+
"API": "/api/location/districts",
|
|
39
|
+
"IN": { "ProvinceID": "formData.provinceID" },
|
|
40
|
+
"OUT": "districtList"
|
|
41
|
+
},
|
|
42
|
+
"apiLoadWards": {
|
|
43
|
+
"API": "/api/location/wards",
|
|
44
|
+
"IN": { "DistrictID": "formData.districtID" },
|
|
45
|
+
"OUT": "wardList"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"handleProvinceChange": [
|
|
50
|
+
{
|
|
51
|
+
"districtList": [],
|
|
52
|
+
"wardList": [],
|
|
53
|
+
"formData.districtID": null,
|
|
54
|
+
"formData.wardID": null
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"IF": "formData.provinceID",
|
|
58
|
+
"THEN": { "CALL": "apiLoadDistricts" }
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
"handleDistrictChange": [
|
|
62
|
+
{
|
|
63
|
+
"wardList": [],
|
|
64
|
+
"formData.wardID": null
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"IF": "formData.districtID",
|
|
68
|
+
"THEN": { "CALL": "apiLoadWards" }
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"CALL": "apiLoadProvinces"
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
"watch": {
|
|
77
|
+
"formData.provinceID": { "CALL": "handleProvinceChange" },
|
|
78
|
+
"formData.districtID": { "CALL": "handleDistrictChange" }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Key points: watch specific keys; reset lower levels before calling their API; guard against calling with null.
|
|
84
|
+
|
|
85
|
+
## 3. Pattern B: filter object + deep-watch
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"data": [
|
|
90
|
+
{
|
|
91
|
+
"filter": {
|
|
92
|
+
"keyword": "",
|
|
93
|
+
"status": null,
|
|
94
|
+
"fromDate": null,
|
|
95
|
+
"toDate": null
|
|
96
|
+
},
|
|
97
|
+
"items": []
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"apiLoadItems": {
|
|
101
|
+
"API": "/api/items/search",
|
|
102
|
+
"IN": {
|
|
103
|
+
"Keyword": "filter.keyword",
|
|
104
|
+
"Status": "filter.status",
|
|
105
|
+
"FromDate": "filter.fromDate",
|
|
106
|
+
"ToDate": "filter.toDate"
|
|
107
|
+
},
|
|
108
|
+
"OUT": "items"
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"handleFilterChanged": {
|
|
113
|
+
"CALL": "apiLoadItems"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
"watch": {
|
|
118
|
+
"deep-watch": {
|
|
119
|
+
"filter": { "CALL": "handleFilterChanged" }
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Keep `filter` small and stable. Continuous text-search input → debounce in `script.js`.
|
|
126
|
+
|
|
127
|
+
## 4. Recommended debounce (script.js)
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
var filterTimer = null;
|
|
131
|
+
|
|
132
|
+
function debounceFilter(input) {
|
|
133
|
+
var callActionName = input && input.callActionName;
|
|
134
|
+
var waitMs = input && input.waitMs;
|
|
135
|
+
clearTimeout(filterTimer);
|
|
136
|
+
filterTimer = setTimeout(function () {
|
|
137
|
+
CALL(vueData[callActionName]);
|
|
138
|
+
}, waitMs || 350);
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Action in `module.json`:
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"handleFilterChanged": {
|
|
147
|
+
"FUN": "debounceFilter",
|
|
148
|
+
"IN": {
|
|
149
|
+
"callActionName": "'apiLoadItems'",
|
|
150
|
+
"waitMs": 350
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## 5. Watcher QA checklist
|
|
157
|
+
|
|
158
|
+
1. Watch key small enough (ID/field, not a large object)?
|
|
159
|
+
2. Dependents reset before calling the API?
|
|
160
|
+
3. `IF` guard before API when input is null/empty?
|
|
161
|
+
4. Any watcher loop risk?
|
|
162
|
+
5. Text input needs debounce?
|
|
163
|
+
6. Action with `IN`: literal string rule applied to space-free text values?
|