flet-box 1.1.2 → 1.1.3

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 (154) hide show
  1. package/AGENTS_FOR_CODE_GEN.md +284 -0
  2. package/CONTRIBUTING.md +238 -0
  3. package/README.md +2 -2
  4. package/bin/cli.js +88 -1
  5. package/bin/commands/auditFix.js +181 -27
  6. package/bin/commands/checkApi.js +14 -3
  7. package/bin/commands/checkStructure.js +368 -0
  8. package/bin/commands/checkSyntax.js +293 -0
  9. package/bin/commands/checkUi.js +666 -14
  10. package/bin/commands/create.js +14 -0
  11. package/bin/commands/createBundle.js +2 -2
  12. package/bin/commands/metrics.js +448 -0
  13. package/bin/commands/release.js +18 -0
  14. package/bin/commands/report.js +849 -0
  15. package/bin/commands/tools.js +76 -0
  16. package/bin/commands/unpack.js +39 -11
  17. package/bin/commands/verify.js +879 -0
  18. package/bin/mcp.js +280 -0
  19. package/bin/utils/contract.js +27 -0
  20. package/bin/utils/dtsProps.js +5 -1
  21. package/bin/utils/migrationGuide.js +146 -0
  22. package/bin/utils/moduleExports.js +9 -5
  23. package/bin/utils/templates.js +225 -18
  24. package/bin/utils/toolkit.js +191 -0
  25. package/docs/README.md +5 -1
  26. package/docs/arquitectura.md +1 -1
  27. package/docs/cli/README.md +168 -2
  28. package/docs/guides/README.md +6 -0
  29. package/docs/guides/accessibility.md +2 -1
  30. package/docs/guides/ai-toolkit.md +166 -0
  31. package/docs/guides/compatibility.md +1 -1
  32. package/docs/guides/migration.md +92 -0
  33. package/docs/guides/minification-and-protection.md +2 -1
  34. package/docs/guides/mobile-and-platforms.md +1 -1
  35. package/docs/guides/online-updates.md +2 -0
  36. package/docs/server/deployment.md +1 -1
  37. package/docs/tools/styling.md +2 -2
  38. package/docs/tools/theme.md +139 -4
  39. package/docs/widget/Accordion.md +74 -8
  40. package/docs/widget/AdMob.md +68 -3
  41. package/docs/widget/AdSense.md +67 -2
  42. package/docs/widget/AlertDialog.md +1 -1
  43. package/docs/widget/AnimatedBox.md +80 -39
  44. package/docs/widget/AnimatedText.md +74 -18
  45. package/docs/widget/Audio.md +73 -14
  46. package/docs/widget/Avatar.md +69 -4
  47. package/docs/widget/Badge.md +68 -3
  48. package/docs/widget/BottomSheet.md +2 -2
  49. package/docs/widget/Button.md +69 -3
  50. package/docs/widget/Card.md +69 -4
  51. package/docs/widget/Carousel.md +67 -2
  52. package/docs/widget/Chart.md +67 -2
  53. package/docs/widget/Checkbox.md +74 -8
  54. package/docs/widget/Chip.md +68 -3
  55. package/docs/widget/CircularBar.md +74 -15
  56. package/docs/widget/CircularChart.md +71 -10
  57. package/docs/widget/CodeViewer.md +67 -2
  58. package/docs/widget/Column.md +68 -3
  59. package/docs/widget/Container.md +68 -3
  60. package/docs/widget/DataTable.md +67 -2
  61. package/docs/widget/Divider.md +67 -2
  62. package/docs/widget/DraggBox.md +67 -2
  63. package/docs/widget/Dropdown.md +76 -10
  64. package/docs/widget/DroppBox.md +67 -2
  65. package/docs/widget/FloatingActionButton.md +68 -3
  66. package/docs/widget/GridView.md +67 -2
  67. package/docs/widget/Icon.md +71 -5
  68. package/docs/widget/Image.md +68 -3
  69. package/docs/widget/Input.md +68 -3
  70. package/docs/widget/Inspector.md +2 -2
  71. package/docs/widget/InstallButton.md +73 -13
  72. package/docs/widget/ListTile.md +69 -3
  73. package/docs/widget/ListView.md +67 -2
  74. package/docs/widget/Maps.md +68 -3
  75. package/docs/widget/Markdown.md +74 -15
  76. package/docs/widget/MatrixRain.md +72 -11
  77. package/docs/widget/Menu.md +209 -0
  78. package/docs/widget/Modal.md +3 -3
  79. package/docs/widget/Pagination.md +68 -3
  80. package/docs/widget/ParallaxBox.md +72 -12
  81. package/docs/widget/ProgressBar.md +69 -4
  82. package/docs/widget/QRCode.md +67 -2
  83. package/docs/widget/README.md +54 -50
  84. package/docs/widget/Radio.md +76 -10
  85. package/docs/widget/Rating.md +70 -6
  86. package/docs/widget/Row.md +68 -3
  87. package/docs/widget/Skeleton.md +68 -3
  88. package/docs/widget/Slider.md +75 -9
  89. package/docs/widget/SnackBar.md +2 -2
  90. package/docs/widget/Stack.md +68 -3
  91. package/docs/widget/Stepper.md +68 -3
  92. package/docs/widget/Switch.md +74 -8
  93. package/docs/widget/Text.md +71 -4
  94. package/docs/widget/ThemePicker.md +197 -0
  95. package/docs/widget/Tooltip.md +72 -7
  96. package/docs/widget/TreeView.md +70 -10
  97. package/docs/widget/Video.md +71 -10
  98. package/package.json +10 -3
  99. package/skills/fletbox-design/SKILL.md +89 -5
  100. package/skills/fletbox-motion/SKILL.md +1 -1
  101. package/skills/fletbox-rules/SKILL.md +56 -4
  102. package/src/core/runApp.js +12 -0
  103. package/src/index.d.ts +374 -101
  104. package/src/index.js +9 -0
  105. package/src/navigations/AdaptiveScaffold.js +2 -2
  106. package/src/navigations/BottomNavigation.js +1 -1
  107. package/src/navigations/Scaffold.js +18 -6
  108. package/src/tools/useState.js +11 -4
  109. package/src/utils/colors.js +275 -0
  110. package/src/utils/errorBoundary.js +288 -0
  111. package/src/utils/index.js +10 -2
  112. package/src/utils/stackPosition.js +39 -24
  113. package/src/utils/syntaxHighlight.js +4 -1
  114. package/src/utils/themes.js +279 -3
  115. package/src/widget-factory/addChildren.js +17 -10
  116. package/src/widget-factory/assignProps.js +18 -6
  117. package/src/widget-factory/createWidget.js +7 -0
  118. package/src/widget-factory/deprecations.js +77 -0
  119. package/src/widget-factory/translateProps.js +11 -1
  120. package/src/widget-factory/warnings.js +1 -27
  121. package/src/widget-factory/widgetFactory.js +59 -17
  122. package/src/widgets/Accordion.js +21 -7
  123. package/src/widgets/Badge.js +11 -2
  124. package/src/widgets/Button.js +15 -3
  125. package/src/widgets/Carousel.js +12 -4
  126. package/src/widgets/Chart.js +7 -2
  127. package/src/widgets/Checkbox.js +1 -2
  128. package/src/widgets/Chip.js +24 -10
  129. package/src/widgets/CircularBar.js +9 -3
  130. package/src/widgets/CircularChart.js +5 -3
  131. package/src/widgets/Container.js +6 -0
  132. package/src/widgets/Divider.js +3 -1
  133. package/src/widgets/DraggBox.js +9 -0
  134. package/src/widgets/Dropdown.js +3 -3
  135. package/src/widgets/DroppBox.js +5 -0
  136. package/src/widgets/FloatingActionButton.js +1 -1
  137. package/src/widgets/Icon.js +5 -1
  138. package/src/widgets/InstallButton.js +4 -2
  139. package/src/widgets/Markdown.js +171 -103
  140. package/src/widgets/Menu.js +340 -0
  141. package/src/widgets/Modal.js +7 -1
  142. package/src/widgets/Pagination.js +10 -5
  143. package/src/widgets/ProgressBar.js +5 -4
  144. package/src/widgets/QRCode.js +4 -0
  145. package/src/widgets/Radio.js +1 -2
  146. package/src/widgets/Rating.js +9 -0
  147. package/src/widgets/Slider.js +4 -1
  148. package/src/widgets/SnackBar.js +2 -2
  149. package/src/widgets/Switch.js +1 -2
  150. package/src/widgets/Text.js +9 -5
  151. package/src/widgets/ThemePicker.js +234 -0
  152. package/src/widgets/TreeView.js +2 -1
  153. package/src/widgets/index.js +2 -0
  154. package/docs/CONTRIBUTING.md +0 -156
@@ -0,0 +1,284 @@
1
+ # AGENTS_FOR_CODE_GEN.md
2
+
3
+ The contract for an agent that **writes FletBox code**. It holds in an app built
4
+ with `flet-box create` and in the framework's own source.
5
+
6
+ `AGENTS.md` is the contract for *maintaining the framework*. This one is for
7
+ *producing code with it*: what may be written, what may not, and how it is
8
+ proved.
9
+
10
+ ---
11
+
12
+ ## 0. The golden rule
13
+
14
+ > **If a widget or a prop is not in `.fletbox/api/cheatsheet.md`, it does not
15
+ > exist.**
16
+
17
+ An invented prop throws nothing. The engine ignores it, the layout breaks
18
+ quietly, and nobody finds it for weeks. If the cheatsheet does not hold what
19
+ the task needs, **ask** — do not invent.
20
+
21
+ | What I need | Where it lives | How it is produced |
22
+ | --- | --- | --- |
23
+ | Which widgets exist and which props each one takes | `.fletbox/api/cheatsheet.md` | `flet-box check-api --emit-api` — runs before `npm run dev` |
24
+ | The same surface as data: one entry per prop, with its engine target, default, aliases and a sample value | `.fletbox/api/api.json` | `flet-box unpack --all --md` |
25
+ | Everything one widget accepts, and whether each prop is really wired | `flet-box unpack <Widget>` | — |
26
+ | A call I can paste as-is | `flet-box unpack <Widget> --example` | — |
27
+ | The real props of one widget | `npm run check -- --props <Widget>` | — |
28
+
29
+ Both files are **generated output**: never hand-edit them, regenerate after
30
+ upgrading `flet-box`.
31
+
32
+ ---
33
+
34
+ ## 1. Five things you may never do
35
+
36
+ 1. **No runtime dependency.** FletBox has zero runtime dependencies and the app
37
+ is offline: no `react`, `vue`, `svelte`, `lit`, `jquery`, `tailwindcss`, no
38
+ CDN `<script>`, no `.jsx`/`.tsx`. Plain ESM `.js`, with the `.js` extension
39
+ in every import.
40
+ 2. **No hardcoded color.** Every fill, ink and border is a `colors.*` token.
41
+ A hex literal in a screen is reported as `RAW_COLOR`.
42
+ 3. **No hand-written DOM.** Build with widgets and props — never with
43
+ `innerHTML`, `createElement` or a template string. (The engine uses
44
+ `innerHTML` internally for rich text; app code does not.)
45
+ 4. **No widget or prop outside the cheatsheet**, and no positional arguments:
46
+ `Text({ text: "Hi" })`, never `Text("Hi")`.
47
+ 5. **No API change without deprecation.** Renaming or removing a prop is a
48
+ breaking change: keep the old name working, mark it, and write the
49
+ "from X to Y" line in the migration guide.
50
+
51
+ ---
52
+
53
+ ## 2. Ten rules that survive a review
54
+
55
+ Each cites the numbered rule in `skills/fletbox-design/SKILL.md`, which is
56
+ where the longer argument lives.
57
+
58
+ 1. **Numbers, not pixels.** `padding: 16` is a framework unit and scales with
59
+ the reader's font; `padding: "16px"` does not. (03, 07)
60
+ 2. **Tokens, not colors.** `bgColor: colors.surface`. One brand color per
61
+ screen; the accent is the reader's to choose, never a hex in a widget. (03, 28)
62
+ 3. **Hierarchy in three seconds.** One type size per element, one weight per
63
+ role. A label and its value that look alike mean the hierarchy is broken. (08)
64
+ 4. **Space carries meaning.** 16 inside a card, 16 or 24 between cards, 24
65
+ between sections. Never mix padding and gap on the same list. (07)
66
+ 5. **Depth is a scale.** `elevation` picks the level; a hand-written shadow is
67
+ noise. (10)
68
+ 6. **Every control has a name.** An icon-only button needs `aria-label`, a
69
+ field needs a label. The gate reports `ICON_ONLY_CONTROL` and
70
+ `UNLABELED_FIELD`. (11)
71
+ 7. **Tap targets are 40 or more**, reached with `height`, not with padding. (11)
72
+ 8. **Every interaction has four states.** hover, focus, active and disabled
73
+ come from the engine's props — do not re-implement them per widget. (12)
74
+ 9. **The phone range is the test device.** From 320×568 to 430×932 nothing
75
+ overflows, nothing is clipped, nothing scrolls sideways. (16)
76
+ 10. **One screen per file, one component per file.** A file exporting two
77
+ widgets is a file nobody finds later.
78
+
79
+ ---
80
+
81
+ ## 3. Examples
82
+
83
+ ### Good
84
+
85
+ ```js
86
+ import { Button, Container, Row, Text, colors, useState } from "flet-box";
87
+
88
+ export const StatsScreen = () => {
89
+ const [period, setPeriod] = useState("stats-period", "week");
90
+
91
+ return Container({
92
+ direction: "column",
93
+ gap: 16,
94
+ padding: 24,
95
+ bgColor: colors.background,
96
+ children: [
97
+ Text({ text: "This week", size: 24, weight: 600, color: colors.text }),
98
+ Row({
99
+ gap: 12,
100
+ children: [
101
+ Button({ text: "Week", onPress: () => setPeriod("week"), variant: "outlined" }),
102
+ Button({ text: "Month", onPress: () => setPeriod("month"), variant: "outlined" }),
103
+ ],
104
+ }),
105
+ Button({
106
+ icon: "refresh",
107
+ "aria-label": "Refresh stats",
108
+ onPress: refresh,
109
+ bgColor: colors.primary,
110
+ height: 40,
111
+ }),
112
+ ],
113
+ });
114
+ };
115
+ ```
116
+
117
+ Every widget and prop there is in the cheatsheet, every length is a number, and
118
+ every color is a token.
119
+
120
+ ### Bad
121
+
122
+ ```js
123
+ import { Card } from "react"; // 1. foreign framework
124
+ ```
125
+
126
+ ```js
127
+ import { Button, Text } from "flet-box";
128
+
129
+ const el = document.createElement("div"); // 3. hand-written DOM
130
+ el.innerHTML = `<b>${name}</b>`;
131
+
132
+ Text({ text: "Total", color: "#4f46e5", size: 13 }); // 2. hex + off-scale size
133
+ Text("Total"); // 4. positional argument
134
+ Button({ icon: "save", onPress: save }); // 6. a control with no name
135
+ Button({ text: "Save", onPress: save, bgColor: "#4f46e5" }); // 2. hardcoded color
136
+ ```
137
+
138
+ ---
139
+
140
+ ## 4. The verification protocol
141
+
142
+ ```bash
143
+ npm run verify # every gate, one report, one exit code
144
+ ```
145
+
146
+ 1. Run it after every edit, before saying the task is done.
147
+ 2. Fix **only** the error the report names. Do not rewrite the file to make it
148
+ pass — a check that fails on line 12 is a line 12 problem.
149
+ 3. **At most two attempts.** If the same error survives the second attempt,
150
+ revert the change and hand the report back with what you tried.
151
+ 4. A warning may stay only when you can justify it in one sentence.
152
+
153
+ `npm run verify` is `flet-box verify`: it parses every module first (`node
154
+ --check`, the parser the runtime uses — a missing `)` is reported as
155
+ `file:line:column` with the offending line, a caret under the character and a
156
+ hint), then runs the anti-hallucination gate (invented widgets, positional
157
+ arguments, foreign imports, broken imports), the type gate (the app's own
158
+ `jsconfig.json` through TypeScript: a widget declared as a bare `HTMLElement`
159
+ makes the documented `widget.update()` an error, and a prop the declaration
160
+ forgot is one too), the design gate (tokens, the scale,
161
+ tap targets, labels, contrast), the structural gate (a cycle between two of the
162
+ app's own modules, a module outside the project's folders, markup written by
163
+ hand) and the prop contract (a prop the widget reads but nothing declares), and
164
+ prints one report with one exit code. A project that declares its own `test`,
165
+ `test:browser` or `test:visual` script gets those run too; a generated app
166
+ declares none, so that step is skipped there. `flet-box verify --watch` re-runs
167
+ the fast gates on every edit, and `flet-box run --verify` does the same while the
168
+ dev server runs.
169
+
170
+ A mistyped name is mechanical, not creative: `npm run fix` rewrites what it
171
+ can prove (`chilldren` → `children`, `bgColr` → `bgColor`, a relative path that
172
+ does not resolve) and leaves anything ambiguous for you to decide. Read the
173
+ diff.
174
+
175
+ Lost before the first gate answers? `flet-box report` names every file of the
176
+ project with its verdict and a ranked next step, and `flet-box tools` is the
177
+ catalogue that maps a question to the command. Both read; neither repairs, and
178
+ neither appends a metrics row.
179
+
180
+ Then look at it: `npm run dev` and use the screen. A green report proves the
181
+ code is legal, not that it is right.
182
+
183
+ ---
184
+
185
+ ## 5. The surgical protocol
186
+
187
+ The report names a line. The edit touches that line, and nothing else.
188
+
189
+ 1. **Fix only what is named.** Every finding carries a file, a line and a code.
190
+ Change that line. A file that fails on three lines gets three edits — not a
191
+ rewrite of the file, and never a rewrite of the screen around it.
192
+ 2. **Let the tool repair first.** A mistyped prop, a wrong relative path and a
193
+ missing import are mechanical, and the repair is the tool's job:
194
+ `npm run verify -- --fix` rewrites what it can prove and re-runs the gates
195
+ on the result. Read the diff it prints before you write anything.
196
+ 3. **Do not widen the blast radius.** No reformatting, no renaming, no
197
+ "while I was here". A diff that touches a line the report did not name is a
198
+ diff you have to justify — and usually it is one you should not make.
199
+ 4. **A new finding is a decision, not noise.** If the fix makes a gate report
200
+ somewhere else, that is the next thing to look at. Do not silence it by
201
+ loosening the value that caused it.
202
+ 5. **Two attempts, then revert.** The same error surviving the second attempt
203
+ means the diagnosis was wrong, not that the third attempt will be luckier.
204
+ Revert and hand back the report with what you tried.
205
+ 6. **Say what you did not touch.** The handoff names the files you changed and
206
+ the ones the report named but you left alone, with one sentence of why.
207
+
208
+ ---
209
+
210
+ ## 6. What gets rejected, and why
211
+
212
+ A change comes back when it breaks one of these. Each row names who says so,
213
+ so "it was rejected" is never a matter of taste.
214
+
215
+ | It comes back when | Who says so | What to write instead |
216
+ | --- | --- | --- |
217
+ | A widget, a prop or an export that does not exist | `UNKNOWN_EXPORT`, `UNKNOWN_PROP` | look it up in the cheatsheet; if it is not there, ask |
218
+ | A positional call | `POSITIONAL_ARG` | `Text({ text: "Hi" })` |
219
+ | An import that does not resolve | `MODULE_NOT_FOUND`, `MISSING_EXPORT` | `npm run fix`, or the path the file really has |
220
+ | A prop the widget reads but no type declares | `INVENTED_PROP` | declare it, so an editor finds it |
221
+ | A prop that no type declares and nothing reads | `DEAD_PROP` | wire it in the widget, or drop the declaration |
222
+ | A hex where a token belongs | `RAW_COLOR` | `colors.primary` |
223
+ | A length that is not on the scale | `OFF_SCALE_SPACING`, `OFF_SCALE_RADIUS`, `OFF_SCALE_TYPE` | the number the scale holds |
224
+ | A control smaller than the tap target | `SMALL_TAP_TARGET` | `height: 40` |
225
+ | A control with no name | `UNLABELED_FIELD`, `ICON_ONLY_CONTROL` | a label, or an `aria-label` |
226
+ | A cycle, a file from outside the project, markup written by hand | `CIRCULAR_IMPORT`, `SOURCE_OUTSIDE_ROOT`, `HAND_WRITTEN_DOM` | import the file that owns it; build with widgets |
227
+ | A rename or a removal with no deprecation | a review | keep the old name working, mark it, write the "from X to Y" line |
228
+ | A diff that touches lines the report did not name | a review | revert the extra lines |
229
+ | A claim with no measurement | a review | measure it, or say that it is unmeasured |
230
+ | A baseline moved to quiet a gate | a review | a baseline moves when the framework moves |
231
+
232
+ The last four rows are not gates: no tool can prove them, and a reader has to.
233
+ A machine can say that a prop does not exist; only a person can say that a
234
+ diff does more than the task asked.
235
+
236
+ And two things are **not** a reason to send anything back: a warning you can
237
+ justify in one sentence, and a fix that lands exactly on the line the report
238
+ named. Both are in the two sections above.
239
+
240
+ ---
241
+
242
+ ## 7. The log of what keeps coming back
243
+
244
+ Every `npm run verify` run appends a row to `.fletbox/metrics/generations.md` —
245
+ when it happened, whether the gates were green, which gates failed, which codes
246
+ they reported. **Every** run, green or not: a log that keeps only the
247
+ successes cannot tell a correction from a first try.
248
+
249
+ ```bash
250
+ npm run metrics # the north metric and the failures that recur
251
+ ```
252
+
253
+ A **generation** is a run of failing rows closed by the first green one, and the
254
+ **north metric** is the share of generations whose *first* run was green — the
255
+ share of code that passed the gate with no correction at all. That is the number
256
+ worth raising, and it moves only when the code changes, not when the report gets
257
+ softer.
258
+
259
+ The same command prints the three failures that recur most often and the rule
260
+ each one should belong to. **That list is the loop.** A code that keeps coming
261
+ back and has no rule behind it is a rule that has not been written yet — and the
262
+ command fails when it finds one, so the gap cannot be forgotten.
263
+
264
+ A code that keeps coming back *with* a rule behind it is a different problem,
265
+ and the command names it: the rule is not landing. That is a stronger rule, a
266
+ stronger place to put it, or an agent that did not read it.
267
+
268
+ So when you see the same code twice in the log, do not only fix the line it
269
+ names: write the rule that would have prevented it, where it belongs (the
270
+ design skill, the rules skill, or the gate itself), and prove it with a check
271
+ that fails without the fix.
272
+
273
+ ---
274
+
275
+ ## 8. If you are working on the framework itself
276
+
277
+ `AGENTS.md` in the framework root is the contract for engine work: one
278
+ rendering engine, the rem contract, the widget structure, the gates that guard
279
+ each of them. This file still applies to anything you write there: real APIs
280
+ only, tokens only, no new rendering system, and every rule you break gets a
281
+ guard that fails before the fix.
282
+
283
+ When you find a defect in the framework, fix it **upstream** and prove it with
284
+ a check that fails without the fix. Never patch `node_modules`.
@@ -0,0 +1,238 @@
1
+ # Contributing to FletBox
2
+
3
+ FletBox is a vanilla-JS UI framework: ESM only, zero runtime dependencies, no
4
+ build step. This file is the map of the repository for whoever adds code to it.
5
+
6
+ Two contracts live next to it, and they are not the same document:
7
+
8
+ | File | It is the contract for |
9
+ | --- | --- |
10
+ | [AGENTS.md](AGENTS.md) | **maintaining the framework** — the architecture, the rem contract, the widget structure, the gates that guard each of them |
11
+ | [AGENTS_FOR_CODE_GEN.md](AGENTS_FOR_CODE_GEN.md) | **producing code with it** — what may be written, what gets rejected, how the work is proved. It ships inside every app `flet-box create` builds |
12
+
13
+ ## The branch, the commits, the release
14
+
15
+ The work happens on `refactory`. A commit message is one line: a conventional
16
+ prefix, a scope, an imperative subject — `feat(theme): …`, `fix(stack): …`,
17
+ `docs(widget): …`, `chore(release): …`. The body carries the *why* when the
18
+ subject cannot: the measurement that decided a value, the defect a fix closes.
19
+
20
+ Never publish by hand. `npm run release -- --patch` bumps the version,
21
+ rebuilds the bundle, commits and tags; CI publishes the tag. `npm publish` in a
22
+ terminal is the one command that must never appear in a handoff.
23
+
24
+ ## The gates
25
+
26
+ | Command | What it proves | When |
27
+ | --- | --- | --- |
28
+ | `npm run verify` | one report, one exit code: every module is parsed first, then the anti-hallucination gate, the design gate, the structural gate and the prop contract run, and then the project's own suites (`test`, `test:browser`, `test:visual`) | after every edit |
29
+ | `npm test` | the static checkers: the widget contract, the scale, the shared helpers, accessibility, the error boundary, the API contract, the bundle budget, the image tooling, the docs, the skills, **the gates' own selftest** and the snippets | before you commit |
30
+ | `npm run selftest` | the gates bite: each of the seven gates runs on a clean fixture and on one with a known defect, and must name the exact code on the exact line | after changing a checker |
31
+ | `npm run test:browser` | the behavioural harnesses in `tests/`, driven in a real Chrome | when the change touches runtime behaviour |
32
+ | `npm run test:visual` | the gallery measured in light and dark against `scripts/visual-baseline.json` | when the change touches how a widget paints |
33
+ | `npm run metrics` | the north metric: the share of generations whose first run was already green | to know whether the rules are landing |
34
+
35
+ CI runs the first three on every pull request and on every push to `refactory`
36
+ and `main`, as three parallel jobs. Nothing is published from a pull request:
37
+ the publish workflow reacts to a version tag and to nothing else.
38
+
39
+ ## Prove it twice
40
+
41
+ Every rule in this repository is proven twice: once by breaking it and once by
42
+ obeying it. A check that only proves the happy path proves nothing — a gate
43
+ that stopped matching would report a clean repository forever. So a new gate
44
+ arrives with a mutation that makes it fail and a case that makes it pass, and
45
+ `npm test` is where both live.
46
+
47
+ The same is true of a baseline. `scripts/visual-baseline.json` and
48
+ `scripts/bundle-budget.json` are promises about how the framework paints and
49
+ how heavy it is. They move when the framework moves, never to quiet a gate. A
50
+ baseline that shrinks is a good day; a baseline that moves because a
51
+ measurement was wrong is a defect.
52
+
53
+ ## What gets rejected
54
+
55
+ The full list is in
56
+ [AGENTS_FOR_CODE_GEN.md §6](AGENTS_FOR_CODE_GEN.md#6-what-gets-rejected-and-why),
57
+ and it is short: a widget or a prop that does not exist, a positional call, an
58
+ import that does not resolve, a hex where a token belongs, a length off the
59
+ scale, a tap target under 40, a control with no name, a cycle, markup written
60
+ by hand, an API change without a deprecation, a diff wider than the task, and
61
+ a claim with no measurement.
62
+
63
+ ## Fix it upstream
64
+
65
+ When an app cannot do what the contract says, the defect is in the framework.
66
+ Fix it here, prove it with a check that fails without the fix, and never patch
67
+ `node_modules`.
68
+
69
+ ## Project areas
70
+
71
+ ```text
72
+ src/core/ Runtime, mounting, PWA, and HMR
73
+ src/widgets/ UI widgets
74
+ src/widget-factory/ Widget creation and prop processing
75
+ src/navigations/ Scaffold, drawer, tabs, and router
76
+ src/services/ Browser storage and HTTP helpers
77
+ src/tools/ State and general-purpose tools
78
+ src/utils/ Themes, parsing, effects, units, and validation
79
+ src/helpers/ Shared helpers — one home per behaviour, and no imports
80
+ bin/ CLI and project generators
81
+ scripts/ The static checkers and the browser drivers (dev only)
82
+ skills/ What the framework knows about designing an app
83
+ tests/ The browser harnesses
84
+ docs/ User and API documentation
85
+ ```
86
+
87
+ ## Before changing code
88
+
89
+ 1. Read the owning module and its nearest tests or examples.
90
+ 2. Check `src/index.js` to understand the public export.
91
+ 3. Check `src/index.d.ts` when changing public types.
92
+ 4. Search for existing aliases and related widgets.
93
+ 5. Keep unrelated refactors out of the change.
94
+
95
+ ## Adding a widget
96
+
97
+ A widget should normally include:
98
+
99
+ - A file under `src/widgets/`.
100
+ - An export in `src/widgets/index.js`.
101
+ - An export in `src/index.js`.
102
+ - A prop interface and function declaration in `src/index.d.ts`.
103
+ - Basic, everyday, and full examples.
104
+ - A documentation page under `docs/widget/`.
105
+ - Accessibility notes when it is interactive.
106
+ - Cleanup for timers, listeners, observers, and subscriptions.
107
+
108
+ Use the existing `WidgetFactory` patterns instead of creating a second
109
+ rendering system, and follow
110
+ [docs/guides/widget-structure.md](docs/guides/widget-structure.md): one factory
111
+ root, `widgetName` on it, `composeUpdate()` instead of overwriting
112
+ `container.update`, and every resource released through an idempotent
113
+ `onUnmount` disposer.
114
+
115
+ ## Adding a prop
116
+
117
+ When adding a public prop:
118
+
119
+ 1. Implement it in the widget.
120
+ 2. Add its TypeScript type.
121
+ 3. Add its default value and behavior documentation.
122
+ 4. Add an example that proves the behavior.
123
+ 5. Check whether it should have a friendly alias.
124
+ 6. Test both the preferred name and aliases when aliases are intentional.
125
+
126
+ FletBox supports developer-friendly aliases, but each alias must have one
127
+ clear meaning. **An alias is a feature, not a duplicate**: never "dedupe" two
128
+ names that both work.
129
+
130
+ ## The generated files — never hand-edited
131
+
132
+ | File | Regenerate it with |
133
+ | --- | --- |
134
+ | `docs/widget/*.md` | `python3 scripts/generate_widget_docs.py` |
135
+ | `tools/snippets-creator-flet-box/*.json` | `python3 tools/snippets-creator-flet-box/build_snippets.py` |
136
+ | `.fletbox/api/cheatsheet.md` and `api.json` | `flet-box check-api --emit-api`, `flet-box unpack --all --md` |
137
+ | `www/` and `dist/` | `npm run build` |
138
+
139
+ The shape of a widget page is the generator's, not yours: the page is built
140
+ from `src/index.d.ts` and the curated examples in `scripts/_docdata/`.
141
+
142
+ ## Runtime and types must agree
143
+
144
+ A public API is not complete until these three surfaces agree:
145
+
146
+ ```text
147
+ runtime implementation
148
+ src/index.js exports
149
+ src/index.d.ts declarations
150
+ ```
151
+
152
+ If a function is declared but not exported, or exported but not typed, do not
153
+ ship it as stable: fix the source of truth. `scripts/check-api-surface.mjs`
154
+ enforces it — it imports `src/index.js` for real, so it fails when a runtime
155
+ export has no declaration, when a declared value does not exist at runtime,
156
+ when an `interface`/`type` is not `export`ed (a consumer could not
157
+ `import type` it), or when the barrel re-exports a name that no longer
158
+ exists. It also fails when the package stops being importable in Node, so
159
+ never touch `window` / `document` at module scope — guard with
160
+ `typeof window !== "undefined"`.
161
+
162
+ ## Deprecating a prop
163
+
164
+ A rename is not a breaking change, and it is not a silent one either. It has
165
+ five parts, and they belong in the same change:
166
+
167
+ 1. The new name is declared and documented **first**.
168
+ 2. The old name keeps working, marked `@deprecated` in `src/index.d.ts`.
169
+ 3. The old name lands in `src/widget-factory/deprecations.js` — the one table
170
+ the guide, the codemod and the contract test all read.
171
+ 4. The old name is listed in [the migration guide](docs/guides/migration.md) as
172
+ a "de X a Y" row, under the version that renamed it.
173
+ 5. The old name is removed in the next **major**, and that major's section says
174
+ so.
175
+
176
+ `scripts/check-migrations.mjs` proves the four surfaces agree, and
177
+ `flet-box release --major` refuses to publish a version the guide does not
178
+ cover. The codemod that applies the table is `flet-box audit-fix --only
179
+ migrations`: it renames a key only when the widget declares both names and the
180
+ call does not already carry the new one, and it removes a key the table marks as
181
+ removed. Anything it cannot prove stays untouched, under "Left for you to
182
+ decide".
183
+
184
+ The guide is a chapter of the book, so a rename also touches the book's
185
+ indexes and the neighbouring chapter's links.
186
+
187
+ ## Cleanup rules
188
+
189
+ Components that register resources must release them:
190
+
191
+ - `window` and `document` listeners.
192
+ - Timers and animation frames.
193
+ - `ResizeObserver` and `MutationObserver`.
194
+ - Router subscriptions.
195
+ - Storage subscriptions.
196
+ - WebSocket connections.
197
+
198
+ Use the widget cleanup lifecycle when available.
199
+
200
+ ## Validation checklist
201
+
202
+ Before you open the pull request:
203
+
204
+ ```bash
205
+ npm run verify
206
+ npm test
207
+ npm run test:browser
208
+ node --check path/to/changed-file.js
209
+ python3 -m py_compile scripts/generate_widget_docs.py
210
+ ```
211
+
212
+ `npm run test:browser` drives the harnesses in `tests/` in a real Chrome:
213
+
214
+ - `/tests/index.html` — the widget-factory regression contract
215
+ - `/tests/smoke.html` — instantiates every widget
216
+ - `/tests/motion.html` — the motion contract
217
+ - `/tests/a11y.html` — the accessibility contract
218
+ - `/tests/regression.html` — a guard for every bug this repository has fixed
219
+ - `/tests/errorboundary.html` — a throw never takes the screen with it
220
+ - `/tests/doccheck.html` and `/tests/doccheck-all.html` — documented behavior
221
+
222
+ Each harness writes a global (`window.__TESTS`, `window.__SMOKE`,
223
+ `window.__MOTION`, `window.__A11Y`, `window.__REGRESSION`) and must pass with 0
224
+ failures. Serve the repo with `npm run dev` to open one by hand.
225
+
226
+ ## Pull request guidance
227
+
228
+ A good pull request explains:
229
+
230
+ - What user problem it solves.
231
+ - Which files changed.
232
+ - Which public API changed.
233
+ - Whether types and docs were updated.
234
+ - Which commands or tests were run.
235
+ - Any remaining compatibility or migration concern.
236
+
237
+ Avoid committing generated output, secrets, local databases, APKs, logs, or
238
+ credentials.
package/README.md CHANGED
@@ -54,7 +54,7 @@ straight from the registry, and the project it writes declares `flet-box` as a
54
54
  normal dependency.
55
55
 
56
56
  Cloning the repository and `npm link`ing it is still how you *develop the framework
57
- itself* — see [CONTRIBUTING](docs/CONTRIBUTING.md).
57
+ itself* — see [CONTRIBUTING](CONTRIBUTING.md).
58
58
 
59
59
  ## Quick start
60
60
 
@@ -173,7 +173,7 @@ And the book itself is an app: **[`flet-box-docs`](https://www.npmjs.com/package
173
173
  - [SQLite and the API server](docs/server/data-services.md) — where your data lives + a full SQLite API example
174
174
  - [Mobile and platforms](docs/guides/mobile-and-platforms.md) — Android, iOS, PWA, and desktop
175
175
  - [Architecture](docs/arquitectura.md) — the widget contract and the single rendering engine
176
- - [Contributing](docs/CONTRIBUTING.md) — how to add code and docs
176
+ - [Contributing](CONTRIBUTING.md) — how to add code and docs
177
177
 
178
178
  ## Support & sponsorship
179
179
 
package/bin/cli.js CHANGED
@@ -15,6 +15,10 @@ import path from "path";
15
15
  import { createProject } from "./commands/create.js";
16
16
  import { checkApi } from "./commands/checkApi.js";
17
17
  import { checkUi } from "./commands/checkUi.js";
18
+ import { verify, watchVerify, formatReport } from "./commands/verify.js";
19
+ import { metrics } from "./commands/metrics.js";
20
+ import { report } from "./commands/report.js";
21
+ import { tools } from "./commands/tools.js";
18
22
  import { auditFix } from "./commands/auditFix.js";
19
23
  import { toPng } from "./commands/toPng.js";
20
24
  import { images } from "./commands/images.js";
@@ -91,8 +95,22 @@ ${cmd("check-ui", "Clean-UI gate: tokens, 8-based scale, tap targets, WCAG contr
91
95
  ${cmd(" └─ --tokens", "print the measured contrast of every color token")}
92
96
  ${cmd(" └─ --strict", "fail on contrast warnings too")}
93
97
  ${cmd(" └─ alias", "ui | checkUi")}
98
+ ${cmd("verify", "Every gate at once: one report, one exit code")}
99
+ ${cmd(" └─ --only <gate>", "syntax | api | types | ui | structure | surface | tests")}
100
+ ${cmd(" └─ --no-tests", "the static gates only; the project's suites run alone")}
101
+ ${cmd(" └─ --fix", "the mechanical repair first, then the gates")}
102
+ ${cmd(" └─ --json", "the report as data")}
103
+ ${cmd(" └─ alias", "check-all")}
104
+ ${cmd("report", "Read-only diagnosis: a verdict per file and what to do next")}
105
+ ${cmd(" └─ --json | --md", "the same facts as data or Markdown")}
106
+ ${cmd(" └─ --html [--out <file>]", "one self-contained page (.fletbox/reports/report.html)")}
107
+ ${cmd(" └─ --tests", "also run the project's own suites")}
108
+ ${cmd("tools [--emit]", "The read-only toolkit: one question, one command")}
109
+ ${cmd("mcp", "Serve that toolkit over MCP stdio (read-only)")}
110
+ ${cmd("metrics", "The north metric: code that passed with no correction")}
111
+ ${cmd(" └─ --log <file>", "read another project's log")}
94
112
  ${cmd("audit-fix", "Rewrite broken imports, missing imports and misspelled props")}
95
- ${cmd(" └─ --only imports|props", "run one repair instead of both")}
113
+ ${cmd(" └─ --only imports|props|migrations", "run one repair instead of the others")}
96
114
  ${cmd(" └─ alias", "fix-imports | fix-path-imports | auditFix")}
97
115
  ${cmd("to-png [dir]", "Convert JPEG/BMP/GIF images to PNG, no dependencies")}
98
116
  ${cmd(" └─ --replace", "remove each source after its PNG was written and read back")}
@@ -124,6 +142,7 @@ ${cmd("--version | --help", "Version and this help")}
124
142
  ${section("⚙️", "OPTIONS")}
125
143
  ${opt("--port <n>, -p <n>", "Port (default 8000) — asked if not provided")}
126
144
  ${opt("--hot", "Hot reload with run")}
145
+ ${opt("--verify", "Run the gates on every edit while the server runs")}
127
146
  ${opt("--quiet", "No request logging")}
128
147
  ${opt("--dry-run | --list", "kill-server: just list, do not kill")}
129
148
 
@@ -167,6 +186,7 @@ const parseFlags = () => {
167
186
  logRequests: true,
168
187
  hot: false,
169
188
  dryRun: false,
189
+ verify: false,
170
190
  };
171
191
 
172
192
  for (let i = 0; i < args.length; i++) {
@@ -187,6 +207,8 @@ const parseFlags = () => {
187
207
  flags.logRequests = false;
188
208
  } else if (arg === "--hot" || arg === "--hot-reload") {
189
209
  flags.hot = true;
210
+ } else if (arg === "--verify") {
211
+ flags.verify = true;
190
212
  } else if (arg === "--dry-run" || arg === "--list" || arg === "--check") {
191
213
  flags.dryRun = true;
192
214
  }
@@ -242,6 +264,24 @@ async function main() {
242
264
  switch (command) {
243
265
  case "create":
244
266
  case "new":
267
+ // `flet-box create --help` must print the help, not scaffold a folder
268
+ // named `--help`: that stray clone lands inside the project and the
269
+ // structure gate starts reporting SOURCE_OUTSIDE_ROOT for every module
270
+ // in it.
271
+ if (projectName && projectName.startsWith("-")) {
272
+ if (projectName === "--help" || projectName === "-h") {
273
+ showHelp();
274
+ break;
275
+ }
276
+ console.error(c("red", `❌ Unknown option: ${projectName}`));
277
+ console.log(
278
+ c(
279
+ "gray",
280
+ "Usage: flet-box create <project-name> [--adaptive|--blank|--full|--sidebar|--template <name>|--repo user/repo]",
281
+ ),
282
+ );
283
+ process.exit(1);
284
+ }
245
285
  if (!projectName) {
246
286
  console.error(c("red", "❌ Error: Project name required"));
247
287
  console.log(
@@ -328,6 +368,40 @@ async function main() {
328
368
  break;
329
369
  }
330
370
 
371
+ case "verify":
372
+ case "check-all": {
373
+ const { exitCode } = await verify(args.slice(1));
374
+ if (exitCode) process.exit(exitCode);
375
+ break;
376
+ }
377
+
378
+ case "metrics": {
379
+ const { exitCode } = await metrics(args.slice(1));
380
+ if (exitCode) process.exit(exitCode);
381
+ break;
382
+ }
383
+
384
+ case "report": {
385
+ const { exitCode } = await report(args.slice(1));
386
+ if (exitCode) process.exit(exitCode);
387
+ break;
388
+ }
389
+
390
+ case "tools":
391
+ case "toolkit": {
392
+ const { exitCode } = await tools(args.slice(1));
393
+ if (exitCode) process.exit(exitCode);
394
+ break;
395
+ }
396
+
397
+ case "mcp": {
398
+ // Lazy: the stdio server takes over the process, and nothing else in
399
+ // the CLI should pay for loading it.
400
+ const { serveStdio } = await import("./mcp.js");
401
+ serveStdio();
402
+ break;
403
+ }
404
+
331
405
  case "audit-fix":
332
406
  case "auditFix":
333
407
  case "fix-imports":
@@ -410,6 +484,19 @@ async function main() {
410
484
  // which are devDependencies: a consumer installing `flet-box` from npm
411
485
  // has neither, so loading them at module scope would break every CLI
412
486
  // command for them. Import them when a server is actually started.
487
+ if (flags.verify) {
488
+ console.log(
489
+ c("dim", "🔎 verify is watching src/ — every edit is checked against the gates"),
490
+ );
491
+ watchVerify({
492
+ root: process.cwd(),
493
+ onReport: (report) => {
494
+ console.log("");
495
+ for (const line of formatReport(report)) console.log(line);
496
+ console.log("");
497
+ },
498
+ });
499
+ }
413
500
  await (await import("./commands/runServer.js")).runDevServer({
414
501
  spaMode: false,
415
502
  hotReload: flags.hot,