flet-box 1.1.1 → 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.
- package/AGENTS_FOR_CODE_GEN.md +284 -0
- package/CONTRIBUTING.md +238 -0
- package/README.md +17 -13
- package/bin/cli.js +88 -1
- package/bin/commands/auditFix.js +181 -27
- package/bin/commands/checkApi.js +14 -3
- package/bin/commands/checkStructure.js +368 -0
- package/bin/commands/checkSyntax.js +293 -0
- package/bin/commands/checkUi.js +666 -14
- package/bin/commands/create.js +18 -1
- package/bin/commands/createBundle.js +20 -8
- package/bin/commands/metrics.js +448 -0
- package/bin/commands/release.js +18 -0
- package/bin/commands/report.js +849 -0
- package/bin/commands/tools.js +76 -0
- package/bin/commands/unpack.js +39 -11
- package/bin/commands/verify.js +879 -0
- package/bin/mcp.js +280 -0
- package/bin/utils/bundleConfig.js +47 -3
- package/bin/utils/contract.js +27 -0
- package/bin/utils/dtsProps.js +5 -1
- package/bin/utils/migrationGuide.js +146 -0
- package/bin/utils/moduleExports.js +9 -5
- package/bin/utils/templates.js +248 -30
- package/bin/utils/toolkit.js +191 -0
- package/docs/README.md +26 -1
- package/docs/arquitectura.md +1 -1
- package/docs/cli/README.md +201 -11
- package/docs/guides/README.md +6 -0
- package/docs/guides/accessibility.md +2 -1
- package/docs/guides/ai-toolkit.md +166 -0
- package/docs/guides/compatibility.md +1 -1
- package/docs/guides/first-app.md +6 -7
- package/docs/guides/migration.md +92 -0
- package/docs/guides/minification-and-protection.md +2 -1
- package/docs/guides/mobile-and-platforms.md +1 -1
- package/docs/guides/online-updates.md +20 -6
- package/docs/server/deployment.md +1 -1
- package/docs/tools/styling.md +2 -2
- package/docs/tools/theme.md +139 -4
- package/docs/widget/Accordion.md +74 -8
- package/docs/widget/AdMob.md +68 -3
- package/docs/widget/AdSense.md +67 -2
- package/docs/widget/AlertDialog.md +1 -1
- package/docs/widget/AnimatedBox.md +80 -39
- package/docs/widget/AnimatedText.md +74 -18
- package/docs/widget/Audio.md +73 -14
- package/docs/widget/Avatar.md +69 -4
- package/docs/widget/Badge.md +68 -3
- package/docs/widget/BottomSheet.md +2 -2
- package/docs/widget/Button.md +69 -3
- package/docs/widget/Card.md +69 -4
- package/docs/widget/Carousel.md +67 -2
- package/docs/widget/Chart.md +67 -2
- package/docs/widget/Checkbox.md +74 -8
- package/docs/widget/Chip.md +68 -3
- package/docs/widget/CircularBar.md +74 -15
- package/docs/widget/CircularChart.md +71 -10
- package/docs/widget/CodeViewer.md +67 -2
- package/docs/widget/Column.md +68 -3
- package/docs/widget/Container.md +68 -3
- package/docs/widget/DataTable.md +67 -2
- package/docs/widget/Divider.md +67 -2
- package/docs/widget/DraggBox.md +67 -2
- package/docs/widget/Dropdown.md +76 -10
- package/docs/widget/DroppBox.md +67 -2
- package/docs/widget/FloatingActionButton.md +68 -3
- package/docs/widget/GridView.md +67 -2
- package/docs/widget/Icon.md +71 -5
- package/docs/widget/Image.md +68 -3
- package/docs/widget/Input.md +68 -3
- package/docs/widget/Inspector.md +2 -2
- package/docs/widget/InstallButton.md +73 -13
- package/docs/widget/ListTile.md +69 -3
- package/docs/widget/ListView.md +67 -2
- package/docs/widget/Maps.md +68 -3
- package/docs/widget/Markdown.md +74 -15
- package/docs/widget/MatrixRain.md +72 -11
- package/docs/widget/Menu.md +209 -0
- package/docs/widget/Modal.md +3 -3
- package/docs/widget/Pagination.md +68 -3
- package/docs/widget/ParallaxBox.md +72 -12
- package/docs/widget/ProgressBar.md +69 -4
- package/docs/widget/QRCode.md +67 -2
- package/docs/widget/README.md +54 -50
- package/docs/widget/Radio.md +76 -10
- package/docs/widget/Rating.md +70 -6
- package/docs/widget/Row.md +68 -3
- package/docs/widget/Skeleton.md +68 -3
- package/docs/widget/Slider.md +75 -9
- package/docs/widget/SnackBar.md +2 -2
- package/docs/widget/Stack.md +68 -3
- package/docs/widget/Stepper.md +68 -3
- package/docs/widget/Switch.md +74 -8
- package/docs/widget/Text.md +71 -4
- package/docs/widget/ThemePicker.md +197 -0
- package/docs/widget/Tooltip.md +72 -7
- package/docs/widget/TreeView.md +70 -10
- package/docs/widget/Video.md +71 -10
- package/package.json +10 -3
- package/skills/fletbox-design/SKILL.md +89 -5
- package/skills/fletbox-motion/SKILL.md +1 -1
- package/skills/fletbox-rules/SKILL.md +56 -4
- package/src/core/runApp.js +12 -0
- package/src/index.d.ts +374 -101
- package/src/index.js +9 -0
- package/src/navigations/AdaptiveScaffold.js +2 -2
- package/src/navigations/BottomNavigation.js +1 -1
- package/src/navigations/Scaffold.js +18 -6
- package/src/tools/useState.js +11 -4
- package/src/utils/colors.js +275 -0
- package/src/utils/errorBoundary.js +288 -0
- package/src/utils/index.js +10 -2
- package/src/utils/stackPosition.js +39 -24
- package/src/utils/syntaxHighlight.js +4 -1
- package/src/utils/themes.js +279 -3
- package/src/widget-factory/addChildren.js +17 -10
- package/src/widget-factory/assignProps.js +18 -6
- package/src/widget-factory/createWidget.js +7 -0
- package/src/widget-factory/deprecations.js +77 -0
- package/src/widget-factory/translateProps.js +11 -1
- package/src/widget-factory/warnings.js +1 -27
- package/src/widget-factory/widgetFactory.js +59 -17
- package/src/widgets/Accordion.js +21 -7
- package/src/widgets/Badge.js +11 -2
- package/src/widgets/Button.js +15 -3
- package/src/widgets/Carousel.js +12 -4
- package/src/widgets/Chart.js +7 -2
- package/src/widgets/Checkbox.js +1 -2
- package/src/widgets/Chip.js +24 -10
- package/src/widgets/CircularBar.js +9 -3
- package/src/widgets/CircularChart.js +5 -3
- package/src/widgets/Container.js +6 -0
- package/src/widgets/Divider.js +3 -1
- package/src/widgets/DraggBox.js +9 -0
- package/src/widgets/Dropdown.js +3 -3
- package/src/widgets/DroppBox.js +5 -0
- package/src/widgets/FloatingActionButton.js +1 -1
- package/src/widgets/Icon.js +5 -1
- package/src/widgets/InstallButton.js +4 -2
- package/src/widgets/Markdown.js +171 -103
- package/src/widgets/Menu.js +340 -0
- package/src/widgets/Modal.js +7 -1
- package/src/widgets/Pagination.js +10 -5
- package/src/widgets/ProgressBar.js +5 -4
- package/src/widgets/QRCode.js +4 -0
- package/src/widgets/Radio.js +1 -2
- package/src/widgets/Rating.js +9 -0
- package/src/widgets/Slider.js +4 -1
- package/src/widgets/SnackBar.js +2 -2
- package/src/widgets/Switch.js +1 -2
- package/src/widgets/Text.js +9 -5
- package/src/widgets/ThemePicker.js +234 -0
- package/src/widgets/TreeView.js +2 -1
- package/src/widgets/index.js +2 -0
- 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`.
|
package/CONTRIBUTING.md
ADDED
|
@@ -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
|
@@ -38,21 +38,24 @@ Real DOM · No Virtual DOM · No build config to learn.
|
|
|
38
38
|
|
|
39
39
|
## Installation
|
|
40
40
|
|
|
41
|
-
FletBox
|
|
41
|
+
FletBox is on npm, and it drags nothing in with it — `"dependencies": {}`:
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
git clone https://github.com/kuko53348/flet-box.git
|
|
47
|
-
cd flet-box
|
|
48
|
-
npm link # link the flet-box CLI globally
|
|
44
|
+
npm install -g flet-box # the CLI, once (Node >= 20.9)
|
|
49
45
|
|
|
50
46
|
flet-box create appName # scaffold your first app
|
|
51
47
|
cd appName
|
|
52
|
-
npm
|
|
53
|
-
flet-box run
|
|
48
|
+
npm install # pulls flet-box itself from the registry
|
|
49
|
+
flet-box run # dev server with hot reload
|
|
54
50
|
```
|
|
55
51
|
|
|
52
|
+
Nothing to install at all: `npx flet-box create appName` runs the same scaffolder
|
|
53
|
+
straight from the registry, and the project it writes declares `flet-box` as a
|
|
54
|
+
normal dependency.
|
|
55
|
+
|
|
56
|
+
Cloning the repository and `npm link`ing it is still how you *develop the framework
|
|
57
|
+
itself* — see [CONTRIBUTING](CONTRIBUTING.md).
|
|
58
|
+
|
|
56
59
|
## Quick start
|
|
57
60
|
|
|
58
61
|
```javascript
|
|
@@ -80,7 +83,7 @@ runApp(App);
|
|
|
80
83
|
## Features
|
|
81
84
|
|
|
82
85
|
- Declarative, widget-based UI · reactive state with `useState` · router for SPAs
|
|
83
|
-
- Zero runtime dependencies; ESM only, Node >=
|
|
86
|
+
- Zero runtime dependencies; ESM only, Node >= 20.9 · real DOM, no Virtual DOM
|
|
84
87
|
- Local storage, HTTP services, theme, PWA, and production builds
|
|
85
88
|
- Android & iOS from the CLI (APK, simulator `.app`, `.xcarchive`)
|
|
86
89
|
- Utility API for layout, text, color, animation, and more
|
|
@@ -137,12 +140,11 @@ invent against.
|
|
|
137
140
|
|
|
138
141
|
## Android and iOS
|
|
139
142
|
|
|
140
|
-
`flet-box create` ships the Capacitor config, so the same SPA builds for both platforms.
|
|
143
|
+
`flet-box create` ships the Capacitor config, so the same SPA builds for both platforms. The generated project already declares `flet-box` as a dependency, so one install covers everything:
|
|
141
144
|
|
|
142
145
|
```bash
|
|
143
146
|
flet-box create my-app && cd my-app
|
|
144
|
-
npm
|
|
145
|
-
npm install # Capacitor CLI, sharp, platform package
|
|
147
|
+
npm install # flet-box from the registry, plus the Capacitor toolchain
|
|
146
148
|
```
|
|
147
149
|
|
|
148
150
|
- **Android** — Node >= 20.9, a JDK, and the Android SDK. `flet-box build android` → debug APK; `flet-box build android --release [--aab]` → unsigned release artifact to sign yourself (`apksigner` / Play App Signing).
|
|
@@ -160,6 +162,8 @@ They are **one continuous book** — the cover maps every page: [The FletBox Boo
|
|
|
160
162
|
|
|
161
163
|
Rather than read, click: **[the live widget gallery](https://kuko53348.github.io/flet-box-widgets-exemples/)** gives every widget its own page, renders each example in the browser it was written for, and prints the exact call underneath it. The theme toggle in the corner shows the same screen in light and dark, which is the honest way to judge a default color.
|
|
162
164
|
|
|
165
|
+
And the book itself is an app: **[`flet-box-docs`](https://www.npmjs.com/package/flet-box-docs)** (`npm install flet-box-docs`) renders these same pages with the framework's own `Markdown` widget, with an index, a theme toggle, an offline cache and Android/iOS wrappers.
|
|
166
|
+
|
|
163
167
|
- [Your first app](docs/guides/first-app.md) — build a working todo app in 20 minutes
|
|
164
168
|
- [The widget book](docs/widget/README.md) — every widget with property tables and examples
|
|
165
169
|
- [State, Router & Services](docs/guides/state.md) — shared state, routing, storage, and HTTP
|
|
@@ -169,7 +173,7 @@ Rather than read, click: **[the live widget gallery](https://kuko53348.github.io
|
|
|
169
173
|
- [SQLite and the API server](docs/server/data-services.md) — where your data lives + a full SQLite API example
|
|
170
174
|
- [Mobile and platforms](docs/guides/mobile-and-platforms.md) — Android, iOS, PWA, and desktop
|
|
171
175
|
- [Architecture](docs/arquitectura.md) — the widget contract and the single rendering engine
|
|
172
|
-
- [Contributing](
|
|
176
|
+
- [Contributing](CONTRIBUTING.md) — how to add code and docs
|
|
173
177
|
|
|
174
178
|
## Support & sponsorship
|
|
175
179
|
|