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,76 @@
1
+ /**
2
+ * @file bin/commands/tools.js
3
+ * @description `flet-box tools` — the diagnosis toolkit, printed as a map.
4
+ *
5
+ * The catalogue lives in `bin/utils/toolkit.js` and is pure data. This file is
6
+ * its human mouth: the question each tool answers, the exact command, and what
7
+ * it reads or writes. `--emit` writes the same list to `.fletbox/api/tools.json`
8
+ * so an assistant that cannot run a shell can still discover it, and `--json`
9
+ * prints it for a script.
10
+ *
11
+ * Node core only: this file ships inside every app `flet-box create` builds.
12
+ */
13
+
14
+ import fs from "node:fs";
15
+ import path from "node:path";
16
+ import { TOOLS, catalogJson, CATALOG_VERSION } from "../utils/toolkit.js";
17
+ import { c, banner, section } from "../utils/colors.js";
18
+
19
+ /**
20
+ * The `flet-box tools` command.
21
+ *
22
+ * @param {string[]} argv Flags after the command name.
23
+ * @param {{print?: (line: string) => void, root?: string}} [io]
24
+ * @returns {Promise<{exitCode: number}>}
25
+ */
26
+ export const tools = async (argv = [], io = {}) => {
27
+ const print = io.print ?? ((line) => console.log(line));
28
+ const rootIndex = argv.indexOf("--root");
29
+ const root = path.resolve(
30
+ rootIndex >= 0 && argv[rootIndex + 1] ? argv[rootIndex + 1] : io.root || process.cwd(),
31
+ );
32
+
33
+ if (argv.includes("--emit")) {
34
+ const dir = path.join(root, ".fletbox", "api");
35
+ fs.mkdirSync(dir, { recursive: true });
36
+ const target = path.join(dir, "tools.json");
37
+ fs.writeFileSync(target, `${JSON.stringify(catalogJson(), null, 2)}\n`, "utf8");
38
+ print(`${c("green", "✅")} Wrote ${path.relative(root, target)} — ${TOOLS.length} read-only tool(s)`);
39
+ return { exitCode: 0 };
40
+ }
41
+
42
+ if (argv.includes("--json")) {
43
+ print(JSON.stringify(catalogJson(), null, 2));
44
+ return { exitCode: 0 };
45
+ }
46
+
47
+ print(banner("🧰 FletBox toolkit", `${TOOLS.length} read-only tool(s) · catalogue v${CATALOG_VERSION}`));
48
+ print(
49
+ c(
50
+ "gray",
51
+ " Pick the question you have; the command under it is the answer. Nothing here edits, deletes or publishes.",
52
+ ),
53
+ );
54
+
55
+ for (const tool of TOOLS) {
56
+ print("");
57
+ print(` ${c("brightCyan", tool.id.padEnd(16))} ${c("bold", tool.question)}`);
58
+ print(` ${c("gray", " ".repeat(16))} ${c("brightWhite", tool.usage)}`);
59
+ print(
60
+ ` ${c("gray", " ".repeat(16))} output: ${tool.output.join(", ")}` +
61
+ (tool.writes.length ? ` · writes: ${tool.writes.join("; ")}` : ` · ${c("green", "writes nothing")}`),
62
+ );
63
+ for (const when of tool.when) print(` ${c("gray", " ".repeat(16))} · ${c("gray", when)}`);
64
+ }
65
+
66
+ print("");
67
+ print(section("•", "as data", "#67e8f9"));
68
+ print(` ${c("gray", "flet-box tools --emit →")} .fletbox/api/tools.json`);
69
+ print(` ${c("gray", "flet-box tools --json →")} the same catalogue on stdout`);
70
+ print(
71
+ ` ${c("gray", "flet-box mcp →")} the same tools over MCP stdio, for an assistant without a shell`,
72
+ );
73
+ return { exitCode: 0 };
74
+ };
75
+
76
+ export default tools;
@@ -28,6 +28,7 @@ import { c, banner, section } from "../utils/colors.js";
28
28
  import { neutralise } from "../utils/neutralise.js";
29
29
  import { membersOf, propsInterfaceFor, resolveInterfaceProps } from "../utils/dtsProps.js";
30
30
  import { STRUCTURAL_PROPS, loadApi, nearest } from "./checkApi.js";
31
+ import { deprecationFor } from "../../src/widget-factory/deprecations.js";
31
32
 
32
33
  /**
33
34
  * Index of the character closing the bracket opened at `open`, or -1.
@@ -59,7 +60,7 @@ const matchPair = (text, open) => {
59
60
  * @param {string} packageDir Installed package root.
60
61
  * @returns {Map<string, string>} name → absolute file
61
62
  */
62
- const definitionIndex = (packageDir) => {
63
+ export const definitionIndex = (packageDir) => {
63
64
  const index = new Map();
64
65
  const reexports = [];
65
66
  const src = path.join(packageDir, "src");
@@ -213,6 +214,11 @@ const readProps = (file) => {
213
214
  spread,
214
215
  child: /\bchild\b/.test(code),
215
216
  children: /\bchildren\b/.test(code),
217
+ // Every identifier the file mentions. A prop can be read somewhere other
218
+ // than the destructuring — `ThemeProvider` takes `theme` as a direct
219
+ // parameter, `Inspector` reads `widget` inside the view it delegates to —
220
+ // and a prop the file never mentions at all is the only honest "dead".
221
+ identifiers: new Set(code.match(/\b[A-Za-z_$][\w$]*\b/g) || []),
216
222
  };
217
223
  };
218
224
 
@@ -318,7 +324,7 @@ const INLINE = "(inline)";
318
324
  * @param {string} name
319
325
  * @returns {string|null} interface name, `INLINE`, or null
320
326
  */
321
- const propsSource = (api, name) => {
327
+ export const propsSource = (api, name) => {
322
328
  const iface = propsInterfaceFor(api.interfaces, name);
323
329
  if (iface) return iface;
324
330
  const first = api.signatures?.get(name)?.params?.[0];
@@ -333,7 +339,7 @@ const propsSource = (api, name) => {
333
339
  * @param {Map<string, string>} definitions
334
340
  * @returns {object|null} null when the package has no such export
335
341
  */
336
- const unpackWidget = (api, name, definitions) => {
342
+ export const unpackWidget = (api, name, definitions) => {
337
343
  const typedBy = propsSource(api, name);
338
344
  const ifaceName = typedBy && typedBy !== INLINE ? typedBy : `${name}Props`;
339
345
  const file = definitions.get(name);
@@ -353,7 +359,9 @@ const unpackWidget = (api, name, definitions) => {
353
359
  ? api.interfaces.get(ifaceName).props
354
360
  : new Map();
355
361
  const aliases = aliasGroups(api);
356
- const source = file ? readProps(file) : { read: new Map(), spread: false, child: false, children: false };
362
+ const source = file
363
+ ? readProps(file)
364
+ : { read: new Map(), spread: false, child: false, children: false, identifiers: new Set() };
357
365
 
358
366
  const props = [];
359
367
  const names = new Set([...declared.keys(), ...source.read.keys()]);
@@ -380,8 +388,16 @@ const unpackWidget = (api, name, definitions) => {
380
388
  props.push(entry);
381
389
  }
382
390
 
391
+ // Dead means nobody mentions it: a prop can be read outside the
392
+ // destructuring — as a direct parameter, or inside a view the widget
393
+ // delegates to — and a name the file never mentions is the honest case.
383
394
  const dead = props.filter(
384
- (c_) => c_.declared && !c_.engine && !c_.read && !c_.structural,
395
+ (c_) =>
396
+ c_.declared &&
397
+ !c_.engine &&
398
+ !c_.read &&
399
+ !c_.structural &&
400
+ !source.identifiers.has(c_.prop),
385
401
  );
386
402
  const invented = props.filter(
387
403
  (c_) => !c_.declared && !c_.engine && c_.read && !c_.structural,
@@ -567,8 +583,8 @@ const renderWidget = (data, api) => {
567
583
  const renderDiff = (data) => {
568
584
  const { dead, invented, hidden, docOnly, codeOnly } = data.gaps;
569
585
  const rows = [
570
- ["DEAD", dead.map((p) => p.prop), "in index.d.ts, handled by nothing"],
571
- ["INVENTED", invented.map((p) => p.prop), "read by the widget, absent from every type"],
586
+ ["DEAD", dead.map((p) => p.prop), "in index.d.ts, handled by nothing — wire it in the widget or drop it from the declaration"],
587
+ ["INVENTED", invented.map((p) => p.prop), `read by the widget, absent from every type — declare it in ${data.interface || `${data.name}Props`}`],
572
588
  ["UNDOCUMENTED", hidden.map((p) => p.prop), "the engine routes it, the interface hides it"],
573
589
  ["DOC-ONLY", docOnly, "the doc page promises it, the code has never heard of it"],
574
590
  ["CODE-ONLY", codeOnly, "the code accepts it, the doc page omits it"],
@@ -838,14 +854,19 @@ const exampleValue = (p, api) => {
838
854
  };
839
855
 
840
856
  const RANK = ["text", "label", "title", "placeholder", "value", "data", "items", "options", "rows", "src", "name"];
841
- /** Boilerplate a pasted example never needs to demonstrate. */
842
- const SKIP = new Set(["ref", "key", "id", "className", "style"]);
857
+ /**
858
+ * Props a pasted example never needs to demonstrate: identity and style plumbing,
859
+ * and the two sizes. A number sampled blind collapses the widget it is pasted
860
+ * into — `Switch({ width: 16 })` is 16 units wide over a 24-unit knob — so the
861
+ * canonical call lets each widget's own default size it.
862
+ */
863
+ const SKIP = new Set(["ref", "key", "id", "className", "style", "width", "height"]);
843
864
 
844
865
  /**
845
866
  * The order an example should demonstrate props in: what it says, what it does,
846
867
  * how it looks. Props outside these groups come last.
847
868
  */
848
- const GROUPS = [RANK, ["width", "height", "padding", "margin", "gap", "bgColor", "color", "borderRadius"]];
869
+ const GROUPS = [RANK, ["padding", "margin", "gap", "bgColor", "color", "borderRadius"]];
849
870
 
850
871
  /** Within the callback group a renderer says more than a hook, a hook more than an option. */
851
872
  const callbackRank = (name) => (name.startsWith("render") ? 0 : name.startsWith("on") ? 1 : 2);
@@ -868,6 +889,9 @@ const buildExample = (data, api) => {
868
889
  (p) =>
869
890
  !p.structural &&
870
891
  (required(p) || ((p.read || p.engine) && !dead.has(p.prop))) &&
892
+ // The example is the call an app pastes, so it carries the current spelling
893
+ // only: `{ onPress, onToggle }` in one call teaches the retired name.
894
+ !deprecationFor(data.name, p.prop) &&
871
895
  !SKIP.has(p.prop),
872
896
  );
873
897
  const groupOf = (p) => {
@@ -891,7 +915,11 @@ const buildExample = (data, api) => {
891
915
  (p.optional === false ? -100000 : 0) +
892
916
  groupOf(p) * 1000 +
893
917
  within(p) * 10 +
894
- (p.own ? 0 : 5);
918
+ (p.own ? 0 : 5) +
919
+ // When two declared names reach one target the camelCase one wins: `onclick`
920
+ // is a deliberate alias, but the example is what gets pasted, and the name
921
+ // every page of the docs uses is `onClick`.
922
+ (p.prop === p.prop.toLowerCase() ? 1 : 0);
895
923
  // One prop per engine target: `text`, `label`, `title` and `caption` all write
896
924
  // textContent, so an example carrying two of them is noise, not a demo.
897
925
  const seen = new Set();