@fui-org/fui-cli 1.3.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +19 -3
  2. package/dist/fui-bmg5pnmq.js +379 -0
  3. package/dist/fui.js +1 -1
  4. package/package.json +3 -6
  5. package/skills/fui/SKILL.md +9 -41
  6. package/skills/fui-skill/SKILL.md +95 -225
  7. package/skills/fui-skill/assets/projectdefaultstyle-3.0.css +555 -0
  8. package/skills/fui-skill/assets/projectdefaultstyle.css +207 -235
  9. package/skills/fui-skill/references/INDEX.md +105 -137
  10. package/skills/fui-skill/references/advanced-techniques.md +76 -68
  11. package/skills/fui-skill/references/coding-standards.md +56 -56
  12. package/skills/fui-skill/references/component-design.md +166 -173
  13. package/skills/fui-skill/references/component-quickref.md +61 -60
  14. package/skills/fui-skill/references/component-table.md +128 -117
  15. package/skills/fui-skill/references/components-dialog.md +55 -56
  16. package/skills/fui-skill/references/components-display.md +24 -30
  17. package/skills/fui-skill/references/components-echart.md +186 -261
  18. package/skills/fui-skill/references/components-input.md +72 -96
  19. package/skills/fui-skill/references/controls-patterns.md +196 -342
  20. package/skills/fui-skill/references/controls-styling-vocabulary.md +130 -97
  21. package/skills/fui-skill/references/db-table-design.md +24 -28
  22. package/skills/fui-skill/references/db-workflow.md +191 -390
  23. package/skills/fui-skill/references/default-function.md +169 -128
  24. package/skills/fui-skill/references/design-modes.md +35 -63
  25. package/skills/fui-skill/references/echart-templates.md +204 -196
  26. package/skills/fui-skill/references/fastproject.md +62 -60
  27. package/skills/fui-skill/references/fsheet.md +109 -124
  28. package/skills/fui-skill/references/fullstack-workflow.md +90 -128
  29. package/skills/fui-skill/references/module-data-patterns.md +31 -40
  30. package/skills/fui-skill/references/module-json-anatomy.md +47 -52
  31. package/skills/fui-skill/references/module-structure.md +80 -196
  32. package/skills/fui-skill/references/new-session.md +49 -51
  33. package/skills/fui-skill/references/pdfmake.md +17 -17
  34. package/skills/fui-skill/references/permission-system.md +89 -108
  35. package/skills/fui-skill/references/platform-architecture.md +128 -153
  36. package/skills/fui-skill/references/project-config.md +102 -134
  37. package/skills/fui-skill/references/project-provisioning.md +139 -244
  38. package/skills/fui-skill/references/script-map.md +208 -242
  39. package/skills/fui-skill/references/sql-clr-functions.md +98 -97
  40. package/skills/fui-skill/references/system-design.md +63 -88
  41. package/skills/fui-skill/references/tapi-file-api.md +46 -52
  42. package/skills/fui-skill/references/tapi-permission-patterns.md +51 -53
  43. package/skills/fui-skill/references/tapi-reference.md +132 -207
  44. package/skills/fui-skill/references/tools-registry.md +84 -460
  45. package/skills/fui-skill/references/ui-crosswindow-patterns.md +79 -75
  46. package/skills/fui-skill/references/ui-dialog-patterns.md +98 -72
  47. package/skills/fui-skill/references/ui-layout-patterns.md +26 -26
  48. package/skills/fui-skill/references/ui-patterns.md +71 -83
  49. package/skills/fui-skill/references/ui-screenshot-review.md +63 -62
  50. package/skills/fui-skill/references/ui-table-cell-patterns.md +61 -59
  51. package/skills/fui-skill/references/ui-templates.md +16 -23
  52. package/skills/fui-skill/references/verification.md +236 -246
  53. package/skills/fui-skill/references/watcher-patterns.md +30 -63
  54. package/skills/fui-skill/references/websocket-realtime.md +83 -66
  55. package/skills/fui-skill/scripts/component-3.0.js +298 -131
  56. package/skills/fui-skill/scripts/component.js +277 -271
  57. package/skills/fui-skill/scripts/componentTable-3.0.js +182 -53
  58. package/skills/fui-skill/scripts/componentTable.js +171 -49
  59. package/skills/fui-skill/scripts/defaultfunction-3.0.js +88 -3
  60. package/skills/fui-skill/scripts/defaultfunction.js +88 -3
  61. package/skills/fui-skill/scripts/fsheet.js +38 -0
  62. package/dist/fui-y8an39cn.js +0 -420
  63. package/skills/fui-skill/README.md +0 -112
  64. package/skills/fui-skill/metadata.json +0 -75
  65. package/skills/fui-skill-cli/SKILL.md +0 -139
  66. package/skills/fui-skill-cli/references/INDEX.md +0 -110
  67. package/skills/fui-skill-cli/references/advanced-techniques.md +0 -168
  68. package/skills/fui-skill-cli/references/coding-standards.md +0 -112
  69. package/skills/fui-skill-cli/references/component-design.md +0 -448
  70. package/skills/fui-skill-cli/references/component-quickref.md +0 -78
  71. package/skills/fui-skill-cli/references/component-table.md +0 -248
  72. package/skills/fui-skill-cli/references/components-dialog.md +0 -191
  73. package/skills/fui-skill-cli/references/components-display.md +0 -141
  74. package/skills/fui-skill-cli/references/components-echart.md +0 -316
  75. package/skills/fui-skill-cli/references/components-input.md +0 -335
  76. package/skills/fui-skill-cli/references/controls-patterns.md +0 -701
  77. package/skills/fui-skill-cli/references/controls-styling-vocabulary.md +0 -137
  78. package/skills/fui-skill-cli/references/db-table-design.md +0 -73
  79. package/skills/fui-skill-cli/references/db-workflow.md +0 -288
  80. package/skills/fui-skill-cli/references/default-function.md +0 -425
  81. package/skills/fui-skill-cli/references/design-modes.md +0 -57
  82. package/skills/fui-skill-cli/references/echart-templates.md +0 -489
  83. package/skills/fui-skill-cli/references/fastproject.md +0 -99
  84. package/skills/fui-skill-cli/references/fsheet.md +0 -203
  85. package/skills/fui-skill-cli/references/fullstack-workflow.md +0 -313
  86. package/skills/fui-skill-cli/references/module-data-patterns.md +0 -117
  87. package/skills/fui-skill-cli/references/module-json-anatomy.md +0 -132
  88. package/skills/fui-skill-cli/references/module-structure.md +0 -141
  89. package/skills/fui-skill-cli/references/new-session.md +0 -85
  90. package/skills/fui-skill-cli/references/pdfmake.md +0 -60
  91. package/skills/fui-skill-cli/references/permission-system.md +0 -150
  92. package/skills/fui-skill-cli/references/platform-architecture.md +0 -269
  93. package/skills/fui-skill-cli/references/project-config.md +0 -303
  94. package/skills/fui-skill-cli/references/project-provisioning.md +0 -278
  95. package/skills/fui-skill-cli/references/script-map.md +0 -262
  96. package/skills/fui-skill-cli/references/sql-clr-functions.md +0 -225
  97. package/skills/fui-skill-cli/references/system-design.md +0 -89
  98. package/skills/fui-skill-cli/references/tapi-file-api.md +0 -185
  99. package/skills/fui-skill-cli/references/tapi-permission-patterns.md +0 -156
  100. package/skills/fui-skill-cli/references/tapi-reference.md +0 -474
  101. package/skills/fui-skill-cli/references/tools-registry.md +0 -84
  102. package/skills/fui-skill-cli/references/ui-crosswindow-patterns.md +0 -321
  103. package/skills/fui-skill-cli/references/ui-dialog-patterns.md +0 -255
  104. package/skills/fui-skill-cli/references/ui-layout-patterns.md +0 -176
  105. package/skills/fui-skill-cli/references/ui-patterns.md +0 -303
  106. package/skills/fui-skill-cli/references/ui-screenshot-review.md +0 -95
  107. package/skills/fui-skill-cli/references/ui-table-cell-patterns.md +0 -318
  108. package/skills/fui-skill-cli/references/ui-templates.md +0 -22
  109. package/skills/fui-skill-cli/references/verification.md +0 -236
  110. package/skills/fui-skill-cli/references/watcher-patterns.md +0 -163
  111. package/skills/fui-skill-cli/references/websocket-realtime.md +0 -271
@@ -1,163 +0,0 @@
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,271 +0,0 @@
1
- # FUI Real-time Messaging — WebSocket (SignalR)
2
-
3
- > Owns: **real-time/SignalR: `webSocketJoinGroup`, `webSocket_Send`**.
4
-
5
- Real-time uses **SignalR** (not the browser WebSocket API), hosted at `ws.fui.vn`. Client logic is already global in `defaultfunction.js` — do not rewrite it. Declare the right structure in `module.json` and use the 3 built-ins: `webSocketConnection`, `webSocketJoinGroup`, `webSocket_Send`.
6
-
7
- ---
8
-
9
- ## 1. Required imports
10
-
11
- Add 2 JS files with `fui import new` — **in this order** (second depends on first):
12
-
13
- | Order | File | Note |
14
- | --------------- | ------------------------------------------------------- | ------------------------------------------------------------------ |
15
- | 1 (lower sort) | `https://ws.fui.vn/Scripts/jquery.signalR-2.4.1.min.js` | SignalR client core |
16
- | 2 (higher sort) | `https://ws.fui.vn/signalr/hubs` | Server-generated hub proxy — JS content despite no `.js` extension |
17
-
18
- ```bash
19
- fui import new --type .js --content-type link --name "https://ws.fui.vn/Scripts/jquery.signalR-2.4.1.min.js" --sort 100
20
- fui import new --type .js --content-type link --name "https://ws.fui.vn/signalr/hubs" --sort 110
21
- ```
22
-
23
- See `fui import new --help`.
24
-
25
- > jQuery is already in the FUI runtime — do not import it. Run `fui import list` first to avoid duplicates (same flow as `f-echart` in [components-echart.md](components-echart.md)).
26
-
27
- ---
28
-
29
- ## 2. Auto-connect on module load
30
-
31
- FUI bootstrap (`buildApp` in `fastproject.js`) checks `vueData.webSocket` right after `data[]` runs:
32
-
33
- - If `vueData.webSocket` exists → creates `vueData.webSocketState = { code, text }` and calls `webSocketConnection('https://ws.fui.vn/signalr', vueData.webSocket, vueData.webSocketState)` + `webSocketJoinGroup(vueData.webSocket)`. **Do not call these yourself at bootstrap** — just declare `webSocket` in `data[]`.
34
- - `webSocketState.text`: `Reconnecting` / `Connected` / `Disconnected`. Bind to UI for debugging (`innerHTML: "{{webSocketState}}"`).
35
-
36
- ---
37
-
38
- ## 3. Declare groups to join — in `data[]`
39
-
40
- ```json
41
- "data": [
42
- {
43
- "TenNhomDong": "`ThuNha",
44
- "DGroup": "`device-abc",
45
- "webSocket": {
46
- "nhom1": [],
47
- "nhom2": null,
48
- "{{DGroup}}": {}
49
- }
50
- }
51
- ]
52
- ```
53
-
54
- - Each **key** of `webSocket` = a group joined on load.
55
- - **Value type sets receive behavior:**
56
- - **array** (`[]`) → new messages **pushed** to the end (history: chat log, event list...).
57
- - **`null` or object** (`{}`) → new message **overwrites** (latest only: status/counter/single notification).
58
- - Group name: literal (`nhom1`) or **dynamic via `{{ }}` in the key** — `"{{DGroup}}"` resolves to `DGroup`'s current value (e.g. `device-abc`), for per-user/device groups.
59
- - `` `ThuNha `` / `` `device-abc `` use backtick to force a literal string — see [Value Resolution Rules](controls-patterns.md#0-value-resolution-rules-fundamental).
60
-
61
- ---
62
-
63
- ## 4. Join more groups at runtime — `FUN: webSocketJoinGroup`
64
-
65
- Join any time, including as an auto-run action in `data[]` (see [ui-patterns.md §1](ui-patterns.md)):
66
-
67
- ```json
68
- {
69
- "FUN": "webSocketJoinGroup",
70
- "IN": {
71
- "Nhom3": null,
72
- "{{TenNhomDong}}": []
73
- }
74
- }
75
- ```
76
-
77
- - `IN` **is the `groupObj`** (first param of `webSocketJoinGroup(groupObj, timeout, callBackFunc)`), not mapped by param name. So `FUN` + `IN` **cannot pass `timeout`/`callBackFunc`** — for those use raw `EXE`: `"EXE": "webSocketJoinGroup({Nhom3:null}, 3000, function(){ ... })"`.
78
- - Use for groups that are **conditional** or known only **after** load (e.g. after an API returns a room/chat ID), instead of static `webSocket: {}`.
79
-
80
- ---
81
-
82
- ## 5. Receive messages
83
-
84
- ### watch (recommended — run logic on new message)
85
-
86
- ```json
87
- "watch": {
88
- "webSocket.nhom1": { "MESS": "thong bao" },
89
- "webSocket.device-abc": { "MESS": "thong bao" }
90
- }
91
- ```
92
-
93
- > **Important:** `watch` keys **cannot** use `{{ }}` (literal, not interpolated) — write the resolved group (`webSocket.device-abc`, not `webSocket.{{DGroup}}`).
94
-
95
- ### Bind directly to UI (raw, no watch)
96
-
97
- ```json
98
- {
99
- "el": "v-textarea",
100
- "attr": { "v-model": "webSocket.nhom2", "label": "Nội dung nhận nhom2", "rows": 5 }
101
- }
102
- ```
103
-
104
- ### Debug all state
105
-
106
- ```json
107
- { "el": "div", "attr": {}, "innerHTML": "{{webSocket}}" }
108
- ```
109
-
110
- ---
111
-
112
- ## 6. Send — `webSocket_Send`
113
-
114
- `webSocket_Send(obj)` takes **one object** `{ group, data }`; `data` may be object, array or string — the function picks the SignalR API (`messageSendObject` / `messageSendArray` / `messageSend`).
115
-
116
- ### Way 1 — `FUN` + `IN` (object payload)
117
-
118
- ```json
119
- {
120
- "FUN": "webSocket_Send",
121
- "IN": {
122
- "group": "`nhom1",
123
- "data": { "text": "doi tuong 1", "text2": "doi tuong 2" }
124
- }
125
- }
126
- ```
127
-
128
- ### Way 2 — raw `EXE` (read dynamic vueData values)
129
-
130
- ```json
131
- { "EXE": "webSocket_Send({ group:'nhom2', data:vueData.messText })" }
132
- ```
133
-
134
- ### Way 3 — array payload
135
-
136
- ```json
137
- {
138
- "FUN": "webSocket_Send",
139
- "IN": { "group": "`nhom3", "data": ["mang 1", "mang 2"] }
140
- }
141
- ```
142
-
143
- ### Directly in `script.js` (outside the action engine)
144
-
145
- ```js
146
- webSocket_Send({ group: "Nhom3", data: "Gui noi dung từ JS" });
147
- ```
148
-
149
- ### Way 4 — HTTP API (no client SignalR connection needed)
150
-
151
- REST endpoint to send to a group:
152
-
153
- ```json
154
- {
155
- "API": "https://ws.fui.vn/api/messageSend/nhom1",
156
- "IN": { "text": "test 1", "text2": "test 2" }
157
- }
158
- ```
159
-
160
- Group name interpolates in the URL:
161
-
162
- ```json
163
- { "API": "https://ws.fui.vn/api/messageSend/{{DGroup}}" }
164
- ```
165
-
166
- Use when `FUN`/`EXE` is inconvenient (e.g. server-side sends, or decoupling sending from whether the client holds a SignalR connection).
167
-
168
- ---
169
-
170
- ## 7. Full example
171
-
172
- ```json
173
- {
174
- "data": [
175
- {
176
- "TenNhomDong": "`ThuNha",
177
- "DGroup": "`device-abc",
178
- "webSocket": { "nhom1": [], "nhom2": null, "{{DGroup}}": {} }
179
- },
180
- {
181
- "FUN": "webSocketJoinGroup",
182
- "IN": { "Nhom3": null, "{{TenNhomDong}}": [] }
183
- }
184
- ],
185
- "watch": {
186
- "webSocket.nhom1": { "MESS": "thong bao" },
187
- "webSocket.device-abc": { "MESS": "thong bao" }
188
- },
189
- "controls": [
190
- {
191
- "prop": "fluid grid-list-md",
192
- "rows": [
193
- {
194
- "prop": "",
195
- "cols": [
196
- {
197
- "el": "v-textarea",
198
- "col": { "class": "" },
199
- "attr": { "v-model": "messText", "label": "noi dung", ":required": false, "rows": 5 },
200
- "w": ""
201
- },
202
- {
203
- "el": "f-button",
204
- "col": { "class": "shrink" },
205
- "attr": {
206
- "label": "send - nhom2",
207
- ":action": [
208
- {
209
- "FUN": "webSocket_Send",
210
- "IN": { "group": "`nhom1", "data": { "text": "doi tuong 1", "text2": "doi tuong 2" } }
211
- },
212
- { "EXE": "webSocket_Send({ group:'nhom2', data:vueData.messText})" },
213
- { "FUN": "webSocket_Send", "IN": { "group": "`nhom3", "data": ["mang 1", "mang 2"] } }
214
- ]
215
- }
216
- }
217
- ]
218
- },
219
- {
220
- "prop": "",
221
- "cols": [
222
- {
223
- "el": "v-textarea",
224
- "col": {},
225
- "attr": { "v-model": "webSocket.nhom2", "label": "Nội dung nhận nhom2", ":required": false, "rows": 5 }
226
- }
227
- ]
228
- },
229
- { "prop": "", "cols": [{ "el": "div", "col": {}, "attr": {}, "innerHTML": "{{webSocket}}" }] },
230
- {
231
- "prop": "",
232
- "cols": [
233
- {
234
- "el": "f-button",
235
- "col": {},
236
- "attr": {
237
- "label": "send by API - nhom1",
238
- ":action": [
239
- { "API": "https://ws.fui.vn/api/messageSend/nhom1", "IN": { "text": "test 1", "text2": "test 2" } }
240
- ]
241
- }
242
- },
243
- {
244
- "el": "f-button",
245
- "col": {},
246
- "attr": {
247
- ":label": "'send by API - ' + DGroup",
248
- ":action": [{ "API": "https://ws.fui.vn/api/messageSend/{{DGroup}}" }]
249
- }
250
- }
251
- ]
252
- },
253
- { "prop": "", "cols": [{ "el": "div", "col": {}, "attr": {}, "innerHTML": "{{webSocketState}}" }] }
254
- ]
255
- }
256
- ],
257
- "set": {}
258
- }
259
- ```
260
-
261
- ---
262
-
263
- ## 8. Checklist
264
-
265
- - [ ] `fui import list`: are `jquery.signalR-2.4.1.min.js` + `signalr/hubs` present?
266
- - [ ] Missing → `fui import new` both, sort order signalR first, hubs second
267
- - [ ] Declare `data[].webSocket = { groupName: [] | null | {} }` — value type by need (history vs latest)
268
- - [ ] Runtime/conditional groups → `FUN: webSocketJoinGroup` (no `timeout`/`callBackFunc` via `IN`; use `EXE` if needed)
269
- - [ ] Receive: `watch` (literal key, no `{{ }}`) or bind `v-model="webSocket.<group>"`
270
- - [ ] Send: `FUN: webSocket_Send` (object/array), `EXE` for dynamic `vueData`, or `API: https://ws.fui.vn/api/messageSend/<group>` for HTTP instead of client SignalR
271
- - [ ] Use `vueData.webSocketState` if the user needs to see connection status