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.
- package/AGENTS_FOR_CODE_GEN.md +284 -0
- package/CONTRIBUTING.md +238 -0
- package/README.md +2 -2
- 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 +14 -0
- package/bin/commands/createBundle.js +2 -2
- 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/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 +225 -18
- package/bin/utils/toolkit.js +191 -0
- package/docs/README.md +5 -1
- package/docs/arquitectura.md +1 -1
- package/docs/cli/README.md +168 -2
- 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/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 +2 -0
- 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
package/bin/utils/templates.js
CHANGED
|
@@ -36,6 +36,23 @@ export const indexHtml = (appName = "FletBox App") => `<!DOCTYPE html>
|
|
|
36
36
|
<meta property="og:title" content="${appName}">
|
|
37
37
|
<meta property="og:description" content="${appName} built with Flet-Box.">
|
|
38
38
|
<meta property="og:type" content="website">
|
|
39
|
+
<meta property="og:url" content="./">
|
|
40
|
+
<meta property="og:image" content="./src/assets/icon-512.png">
|
|
41
|
+
<meta property="og:image:alt" content="${appName}">
|
|
42
|
+
|
|
43
|
+
<meta name="twitter:card" content="summary">
|
|
44
|
+
<meta name="twitter:title" content="${appName}">
|
|
45
|
+
<meta name="twitter:description" content="${appName} built with Flet-Box.">
|
|
46
|
+
<meta name="twitter:url" content="./">
|
|
47
|
+
<meta name="twitter:image" content="./src/assets/icon-512.png">
|
|
48
|
+
<meta name="twitter:image:alt" content="${appName}">
|
|
49
|
+
|
|
50
|
+
<!-- The share tags stay relative on purpose: this one file runs on :8000,
|
|
51
|
+
through a tunnel and on Pages. 'flet-box build' rewrites og:image,
|
|
52
|
+
og:url, twitter:image and twitter:url to absolute URLs at the site
|
|
53
|
+
origin in the copy it publishes, because a scraper does not resolve
|
|
54
|
+
a relative path the way a browser does. Replace the icon with a real
|
|
55
|
+
cover if you have one: a square icon is what a 'summary' card expects. -->
|
|
39
56
|
|
|
40
57
|
<meta name="theme-color" content="${SHELL_BRAND}">
|
|
41
58
|
|
|
@@ -344,6 +361,7 @@ import {
|
|
|
344
361
|
Slider,
|
|
345
362
|
Divider,
|
|
346
363
|
Tooltip,
|
|
364
|
+
ThemePicker,
|
|
347
365
|
Icon,
|
|
348
366
|
SnackBar,
|
|
349
367
|
AlertDialog,
|
|
@@ -353,7 +371,8 @@ import {
|
|
|
353
371
|
setTheme,
|
|
354
372
|
getTheme,
|
|
355
373
|
setBaseFontSize,
|
|
356
|
-
getBaseFontSize
|
|
374
|
+
getBaseFontSize,
|
|
375
|
+
listAccents
|
|
357
376
|
} from 'flet-box';
|
|
358
377
|
|
|
359
378
|
// Key-based RAM keeps a value across re-renders and hot reloads; localStorage is
|
|
@@ -451,6 +470,28 @@ export const AboutScreen = () => {
|
|
|
451
470
|
activeColor: colors.primary,
|
|
452
471
|
onPress: (value) => setTheme(value ? 'dark' : 'light')
|
|
453
472
|
}),
|
|
473
|
+
// The accent is the reader's answer, not the build's. This app opens on
|
|
474
|
+
// neutral (themes.js applies it at boot) and this row is how they leave it:
|
|
475
|
+
// one square per family, split diagonally into what the hue wears on a light
|
|
476
|
+
// page and what it wears on a dark one, with the tick already on the family
|
|
477
|
+
// painted. A tap repaints every widget on screen and stores the choice, so
|
|
478
|
+
// this does not wait for the Save button below — that keeps the profile.
|
|
479
|
+
// Five squares is a decision; \`listAccents()\` has twelve, so the filter is
|
|
480
|
+
// the brand's answer to what this app is willing to look like.
|
|
481
|
+
Column({
|
|
482
|
+
gap: 8,
|
|
483
|
+
children: [
|
|
484
|
+
Text({ text: 'Accent color', size: 16, weight: 'bold', color: colors.text }),
|
|
485
|
+
ThemePicker({
|
|
486
|
+
label: 'Accent color',
|
|
487
|
+
accents: listAccents().filter((family) =>
|
|
488
|
+
['neutral', 'blue', 'teal', 'rose', 'orange'].includes(family.id)
|
|
489
|
+
),
|
|
490
|
+
onChange: (id) =>
|
|
491
|
+
SnackBar({ message: 'Accent: ' + id, type: 'success' }).show()
|
|
492
|
+
})
|
|
493
|
+
]
|
|
494
|
+
}),
|
|
454
495
|
Column({
|
|
455
496
|
gap: 8,
|
|
456
497
|
children: [
|
|
@@ -591,12 +632,15 @@ export const themesJs = () => `// database/themes.js
|
|
|
591
632
|
// framework's own \`color().contrastRatio()\` against the surface it is written on:
|
|
592
633
|
// the number sits next to the pair that needs it, because a token that cannot
|
|
593
634
|
// carry AA text is decoration, not a color.
|
|
594
|
-
import { registerPalette } from 'flet-box';
|
|
635
|
+
import { applyAccentPreference, registerPalette } from 'flet-box';
|
|
595
636
|
|
|
596
637
|
export const lightColors = {
|
|
597
|
-
//
|
|
598
|
-
//
|
|
599
|
-
|
|
638
|
+
// The brand a new app shows is neutral: no hue, the page's own ink. White on
|
|
639
|
+
// this fill measures 17.85:1 and the fill as words on this page measures the
|
|
640
|
+
// same, so a button, an active tab and a link all pass in both directions
|
|
641
|
+
// before anyone has chosen a color. The reader changes it on the Preferences
|
|
642
|
+
// screen (\`ThemePicker\`), which paints a hue and remembers it.
|
|
643
|
+
primary: '#0f172a',
|
|
600
644
|
secondary: '#8b5cf6',
|
|
601
645
|
success: '#10b981',
|
|
602
646
|
warning: '#f59e0b',
|
|
@@ -629,10 +673,14 @@ export const lightColors = {
|
|
|
629
673
|
border: '#e2e8f0',
|
|
630
674
|
overlay: 'rgba(0, 0, 0, 0.5)',
|
|
631
675
|
shadow: 'rgba(0, 0, 0, 0.1)',
|
|
632
|
-
// What to write on a brand fill, and the soft
|
|
633
|
-
// \`colors.primary + '15'\`.
|
|
676
|
+
// What to write on a brand fill, and the soft plate that replaces
|
|
677
|
+
// \`colors.primary + '15'\`. The plate is a lifted gray rather than a tint of
|
|
678
|
+
// a hue, because the brand has no hue: the page ink sits on it at 14.56:1, and
|
|
679
|
+
// since the neutral brand *is* that ink a chip written in \`colors.primary\`
|
|
680
|
+
// measures the same. One limit: \`textSecondary\` below (#64748b) on this plate
|
|
681
|
+
// is 3.88:1, so a caption inside a \`primaryBg\` area uses \`colors.text\`.
|
|
634
682
|
onPrimary: '#ffffff',
|
|
635
|
-
primaryBg: '#
|
|
683
|
+
primaryBg: '#e7e8ea',
|
|
636
684
|
// The translucent surface for a card with \`backdropFilter\`. A token, so the
|
|
637
685
|
// app never hand-writes an \`rgba()\` the clean-UI gate would reject.
|
|
638
686
|
glass: 'rgba(255, 255, 255, 0.66)',
|
|
@@ -647,7 +695,10 @@ export const lightColors = {
|
|
|
647
695
|
};
|
|
648
696
|
|
|
649
697
|
export const darkColors = {
|
|
650
|
-
|
|
698
|
+
// The other half of the same neutral: here the brand is the page's near-white
|
|
699
|
+
// ink. It reads 12.02:1 on this background and 9.85:1 on a card, so it is
|
|
700
|
+
// legal as words and as a glyph, and it carries its own label below.
|
|
701
|
+
primary: '#cbd5e1',
|
|
651
702
|
secondary: '#a78bfa',
|
|
652
703
|
success: '#34d399',
|
|
653
704
|
warning: '#fbbf24',
|
|
@@ -677,9 +728,14 @@ export const darkColors = {
|
|
|
677
728
|
border: '#334155',
|
|
678
729
|
overlay: 'rgba(0, 0, 0, 0.6)',
|
|
679
730
|
shadow: 'rgba(0, 0, 0, 0.3)',
|
|
680
|
-
// #
|
|
681
|
-
|
|
682
|
-
|
|
731
|
+
// #cbd5e1 is a light fill: white on it is 1.48:1, so its words are the page's
|
|
732
|
+
// own near-black and measure 13.40:1. The plate sits one step off the page
|
|
733
|
+
// (1.18:1) and one very quiet step off a card (1.03:1), which is what a brand
|
|
734
|
+
// with no hue costs: a tinted banner works on the background, and on a card
|
|
735
|
+
// the mark is \`colors.border\` or the ink itself. Ink on the plate: 13.81:1,
|
|
736
|
+
// secondary ink 5.90:1, the brand 10.19:1.
|
|
737
|
+
onPrimary: '#09090b',
|
|
738
|
+
primaryBg: '#252629',
|
|
683
739
|
glass: 'rgba(30, 41, 59, 0.66)',
|
|
684
740
|
glassBorder: 'rgba(226, 232, 240, 0.22)',
|
|
685
741
|
error: '#f87171',
|
|
@@ -697,6 +753,21 @@ export const darkColors = {
|
|
|
697
753
|
// the reader's answer (\`setTheme\`, \`toggleTheme\`), not the designer's.
|
|
698
754
|
registerPalette('light', lightColors);
|
|
699
755
|
registerPalette('dark', darkColors);
|
|
756
|
+
|
|
757
|
+
// The palette above answers what a theme *is*; the accent layer is what the reader
|
|
758
|
+
// picks, and it owns exactly three tokens: \`primary\`, \`onPrimary\` and
|
|
759
|
+
// \`primaryBg\`. A new app opens on \`neutral\` — the same three values the palettes
|
|
760
|
+
// carry, so the two layers agree and nothing repaints a frame after boot.
|
|
761
|
+
//
|
|
762
|
+
// \`applyAccentPreference\` is the app's verb, not the reader's: it paints only when
|
|
763
|
+
// nobody has chosen yet and writes no preference down, so the Preferences screen's
|
|
764
|
+
// \`ThemePicker\` opens with the neutral square marked as the answer, and a hue
|
|
765
|
+
// picked on a previous visit still wins over this line. \`setAccent\` would answer
|
|
766
|
+
// for the reader and overwrite their choice on every load.
|
|
767
|
+
//
|
|
768
|
+
// An app with a brand of its own keeps the three tokens above and changes the id
|
|
769
|
+
// here to a family it registers with \`registerAccent\`.
|
|
770
|
+
applyAccentPreference('neutral');
|
|
700
771
|
`;
|
|
701
772
|
|
|
702
773
|
export const runSh = () => `#!/bin/bash
|
|
@@ -858,10 +929,13 @@ export const packageJson = (name) => `{
|
|
|
858
929
|
"version": "1.0.0",
|
|
859
930
|
"type": "module",
|
|
860
931
|
"scripts": {
|
|
932
|
+
"predev": "flet-box check-api --emit-api && flet-box unpack --all --md",
|
|
861
933
|
"dev": "bash run.sh",
|
|
934
|
+
"verify": "flet-box verify",
|
|
862
935
|
"check": "node scripts/check-app.mjs",
|
|
863
936
|
"ui": "node scripts/check-ui.mjs",
|
|
864
937
|
"fix": "flet-box audit-fix",
|
|
938
|
+
"metrics": "flet-box metrics",
|
|
865
939
|
"api": "flet-box unpack --all --md",
|
|
866
940
|
"build": "flet-box createBundle www",
|
|
867
941
|
"start": "npm run dev",
|
|
@@ -907,6 +981,22 @@ export const capacitorConfig = (appName, appId) => `${JSON.stringify(
|
|
|
907
981
|
// The user replaces TU-USUARIO/TU-REPO with their real public GitHub names.
|
|
908
982
|
export const OTA_PLACEHOLDER_URL = "https://TU-USUARIO.github.io/TU-REPO/";
|
|
909
983
|
|
|
984
|
+
// The pictures a created app keeps in `assets/`. `flet-box create` makes that
|
|
985
|
+
// folder and writes `assets/logo.png` into it, so it is where an app's own
|
|
986
|
+
// images live — but `createBundle` copies only `src/assets` (icons, fonts) by
|
|
987
|
+
// itself. Undeclared, every bundled build served those screens with broken
|
|
988
|
+
// images while `npm run dev`, which serves the project root, looked perfect.
|
|
989
|
+
// One glob per extension: the asset matcher understands `*`, `**` and `?`,
|
|
990
|
+
// not braces. Images only — a source document parked in `assets/` (the PDF a
|
|
991
|
+
// catalogue was written from) must stay out of the bundle.
|
|
992
|
+
const PROJECT_ASSET_PATTERNS = [
|
|
993
|
+
"assets/**/*.png",
|
|
994
|
+
"assets/**/*.jpg",
|
|
995
|
+
"assets/**/*.jpeg",
|
|
996
|
+
"assets/**/*.svg",
|
|
997
|
+
"assets/**/*.webp",
|
|
998
|
+
];
|
|
999
|
+
|
|
910
1000
|
// OTA manifest written to every generated project. The loader (www/loader.js)
|
|
911
1001
|
// reads `update.url` via www/config.json to know where to fetch updates.
|
|
912
1002
|
export const fletboxConfig = (url = OTA_PLACEHOLDER_URL) => `${JSON.stringify(
|
|
@@ -918,6 +1008,7 @@ export const fletboxConfig = (url = OTA_PLACEHOLDER_URL) => `${JSON.stringify(
|
|
|
918
1008
|
mandatory: false,
|
|
919
1009
|
minNativeVersion: "0.0.0",
|
|
920
1010
|
notes: "",
|
|
1011
|
+
assets: PROJECT_ASSET_PATTERNS,
|
|
921
1012
|
},
|
|
922
1013
|
},
|
|
923
1014
|
null,
|
|
@@ -1628,6 +1719,7 @@ android/capacitor-cordova-android-plugins/
|
|
|
1628
1719
|
package-lock.json
|
|
1629
1720
|
.DS_Store
|
|
1630
1721
|
*.log
|
|
1722
|
+
.fletbox/reports/
|
|
1631
1723
|
`;
|
|
1632
1724
|
|
|
1633
1725
|
export const readme = (name, _template, appId) => `# ${name}
|
|
@@ -1672,6 +1764,8 @@ framework from scratch:
|
|
|
1672
1764
|
Claude Code and Cursor.
|
|
1673
1765
|
- \`.fletbox/api/cheatsheet.md\` — every widget and its real props, generated from the
|
|
1674
1766
|
installed package. This is the lookup an agent reads instead of the framework.
|
|
1767
|
+
A prop marked \`(deprecated → x)\` still works, but it is the name being retired;
|
|
1768
|
+
write \`x\`. The old spelling will not fail — nothing else will tell you.
|
|
1675
1769
|
- \`.fletbox/api/api.json\` — the same surface as data: per prop its engine target,
|
|
1676
1770
|
default, aliases and a \`sample\` value the types prove works. Write calls from this.
|
|
1677
1771
|
- \`.fletbox/memory/\` — notes the agent keeps between sessions, so it does not
|
|
@@ -1687,7 +1781,8 @@ npm run ui # clean-UI gate: tokens, scale, tap targets,
|
|
|
1687
1781
|
# contrast — each finding cites the rule it broke
|
|
1688
1782
|
npm run ui -- --tokens # measured contrast of every token, on both surfaces
|
|
1689
1783
|
npm run ui -- --strict # warnings (a one-off tint, an image with no alt) fail too
|
|
1690
|
-
npm run fix # rewrite
|
|
1784
|
+
npm run fix # rewrite what it can prove: imports, prop typos,
|
|
1785
|
+
# renamed props
|
|
1691
1786
|
npm run api # regenerate .fletbox/api/api.json and api.md
|
|
1692
1787
|
flet-box unpack Button # each prop, where it really goes, and what is dead
|
|
1693
1788
|
flet-box unpack Button --example # a paste-ready call using only verified props
|
|
@@ -2078,10 +2173,12 @@ export const AppBarComponent = ({ title, isMobile = false, showBack = false, onB
|
|
|
2078
2173
|
size: 24,
|
|
2079
2174
|
height: 40,
|
|
2080
2175
|
color: colors.text,
|
|
2176
|
+
'aria-label': 'Open the menu',
|
|
2081
2177
|
onclick: () => openDrawer()
|
|
2082
2178
|
})
|
|
2083
2179
|
}) : null,
|
|
2084
|
-
// An icon-only control still needs a name
|
|
2180
|
+
// An icon-only control still needs a name, and a Tooltip is a description
|
|
2181
|
+
// (aria-describedby), not a name: the aria-label is what a screen reader says.
|
|
2085
2182
|
actions: [
|
|
2086
2183
|
Tooltip({
|
|
2087
2184
|
text: isDark ? 'Switch to light' : 'Switch to dark',
|
|
@@ -2091,6 +2188,7 @@ export const AppBarComponent = ({ title, isMobile = false, showBack = false, onB
|
|
|
2091
2188
|
size: 24,
|
|
2092
2189
|
height: 40,
|
|
2093
2190
|
color: colors.text,
|
|
2191
|
+
'aria-label': isDark ? 'Switch to light' : 'Switch to dark',
|
|
2094
2192
|
onclick: () => toggleTheme()
|
|
2095
2193
|
})
|
|
2096
2194
|
})
|
|
@@ -2433,6 +2531,13 @@ export const agentsMd = (appName, template, version) =>
|
|
|
2433
2531
|
'fixes every size and color you may use, and the checker in section 5',
|
|
2434
2532
|
'proves you right or wrong in seconds.',
|
|
2435
2533
|
'',
|
|
2534
|
+
'If you are an agent **writing code** in this app rather than reading it,',
|
|
2535
|
+
'[AGENTS_FOR_CODE_GEN.md](AGENTS_FOR_CODE_GEN.md) is the shorter contract:',
|
|
2536
|
+
'the golden rule (a prop that is not in the cheatsheet does not exist), what',
|
|
2537
|
+
'may never be written, the ten design rules that survive a review, what gets',
|
|
2538
|
+
'rejected and why, and the verification protocol. Read that one first; this',
|
|
2539
|
+
'file is its long form.',
|
|
2540
|
+
'',
|
|
2436
2541
|
'## 0. Start here — the skills folder',
|
|
2437
2542
|
'',
|
|
2438
2543
|
'Everything this framework knows about building a professional screen is grouped',
|
|
@@ -2488,7 +2593,7 @@ export const agentsMd = (appName, template, version) =>
|
|
|
2488
2593
|
'// Correct',
|
|
2489
2594
|
"Text({ text: 'Hello', size: 18, color: colors.primary })",
|
|
2490
2595
|
"Button({ text: 'Save', onPress: save, bgColor: colors.primary })",
|
|
2491
|
-
'
|
|
2596
|
+
'Container({ child: Text({ text: count }), padding: 12 })',
|
|
2492
2597
|
'```',
|
|
2493
2598
|
'',
|
|
2494
2599
|
'```js',
|
|
@@ -2506,7 +2611,7 @@ export const agentsMd = (appName, template, version) =>
|
|
|
2506
2611
|
' at runtime and nothing fetches a CDN.',
|
|
2507
2612
|
'- No `.jsx`, `.tsx`, `.vue` or `.svelte` files. Plain `.js`, ESM imports with a',
|
|
2508
2613
|
' explicit `.js` extension.',
|
|
2509
|
-
'- Styling is props (`bgColor`, `
|
|
2614
|
+
'- Styling is props (`bgColor`, `borderRadius`, `padding`), not class strings or',
|
|
2510
2615
|
' `styled-components`.',
|
|
2511
2616
|
'- `child` takes one widget; `children` takes an array or string. Swapping them',
|
|
2512
2617
|
' is the most common silent bug.',
|
|
@@ -2611,6 +2716,11 @@ export const agentsMd = (appName, template, version) =>
|
|
|
2611
2716
|
'',
|
|
2612
2717
|
'- A screen uses **one** brand color. Two accent colors competing on one view',
|
|
2613
2718
|
' means one of them is wrong.',
|
|
2719
|
+
'- The brand is an **accent**, not a value on the screen: this app opens on the',
|
|
2720
|
+
' \`neutral\` family (\`applyAccentPreference\` at the bottom of',
|
|
2721
|
+
' \`src/database/themes.js\`) and the reader can move it on the Preferences',
|
|
2722
|
+
' screen, where \`ThemePicker\` lives. To ship a hue, change that id or register',
|
|
2723
|
+
' a family with \`registerAccent\` — never write the hex into a screen.',
|
|
2614
2724
|
'- Never type a hex or `rgb()` literal. If a token is missing, add it to a',
|
|
2615
2725
|
' `theme.js` in this app that re-exports `colors` — do not scatter the value.',
|
|
2616
2726
|
'- A frosted surface is `colors.glass` with `colors.glassBorder` plus a',
|
|
@@ -2628,7 +2738,7 @@ export const agentsMd = (appName, template, version) =>
|
|
|
2628
2738
|
'',
|
|
2629
2739
|
'**Space and type: an 8-based scale.** Use these numbers and nothing between',
|
|
2630
2740
|
'them. They are plain props (`padding`, `gap`, `margin`, `width`, `height`,',
|
|
2631
|
-
'`size`, `
|
|
2741
|
+
'`size`, `borderRadius`) — the factory takes the number, no unit and no `px` string.',
|
|
2632
2742
|
'',
|
|
2633
2743
|
'```',
|
|
2634
2744
|
'spacing 4 · hairline, icon inset',
|
|
@@ -2656,29 +2766,76 @@ export const agentsMd = (appName, template, version) =>
|
|
|
2656
2766
|
'- `useState` bindings, `flex` and `gap` do the layout. Reach for an absolute',
|
|
2657
2767
|
' offset only for overlays, and use `stackPosition` rather than raw coordinates.',
|
|
2658
2768
|
'',
|
|
2769
|
+
'**The phone range is the test device — strict.** Every phone is `xs` in the',
|
|
2770
|
+
'breakpoint table, so nothing in the shell changes between 320 and 430; the',
|
|
2771
|
+
'layout has to. Compose at `360×640` and prove the screen at `320×568` (the',
|
|
2772
|
+
'floor), `375×667`, `390×844`, `393×852`, `412×915` and `430×932` (the',
|
|
2773
|
+
'ceiling), in portrait. At 320 nothing overflows or clips: no horizontal',
|
|
2774
|
+
'scroll, no label cut because its row ran out of room. A heading whose text',
|
|
2775
|
+
'the app does not control — a lesson name, a chapter, a user title — is `24`',
|
|
2776
|
+
'on a phone and `32` only from `atLeast("md")`: at 360 a long name at `32` is',
|
|
2777
|
+
'four lines and pushes the controls under it off the first screen. Fixed',
|
|
2778
|
+
'chrome is inside the 320 budget: an app bar title plus its actions, a tab',
|
|
2779
|
+
'strip, a labeled bottom bar. If one needs more than 320, shorten what it',
|
|
2780
|
+
'says — never let it scroll sideways.',
|
|
2781
|
+
'',
|
|
2659
2782
|
'## 5. Verification loop (required)',
|
|
2660
2783
|
'',
|
|
2661
2784
|
'After every edit, before you say a task is done:',
|
|
2662
2785
|
'',
|
|
2663
2786
|
'```bash',
|
|
2787
|
+
'npm run verify # parses first, then every gate, then your test scripts',
|
|
2788
|
+
' # — one report, one exit code; this is the command to',
|
|
2789
|
+
' # run after every edit',
|
|
2790
|
+
'npm run verify -- --fix # the same, after the mechanical repair: it rewrites',
|
|
2791
|
+
' # a mistyped prop or a wrong relative path first, so a',
|
|
2792
|
+
' # typo never reaches you as work to do',
|
|
2664
2793
|
'npm run check # static gate: invented widgets/props, positional args,',
|
|
2665
2794
|
' # foreign imports, and broken imports between your own',
|
|
2666
2795
|
' # modules (a name a file never exports)',
|
|
2667
2796
|
'npm run ui # clean-UI gate: the tokens and the scale above, 40px',
|
|
2668
2797
|
' # targets, labels, contrast — every finding cites the',
|
|
2669
2798
|
' # rule number it broke',
|
|
2799
|
+
'npm run metrics # the north metric: how much code passed the gate with',
|
|
2800
|
+
' # no correction, and which failures keep coming back',
|
|
2801
|
+
'flet-box report # lost? every file of this project, its verdict, and',
|
|
2802
|
+
' # the one next step — read-only, writes no metrics row',
|
|
2803
|
+
'flet-box tools # the map: which command answers which question',
|
|
2670
2804
|
'npm run dev # serve on :8000 — check the UI actually renders',
|
|
2671
2805
|
'```',
|
|
2672
2806
|
'',
|
|
2673
|
-
'
|
|
2807
|
+
'`npm run verify` parses every module first (`node --check`, the parser the',
|
|
2808
|
+
'runtime uses: a missing `)` comes back as `file:line:column` with a caret',
|
|
2809
|
+
'under the character and a hint), then runs those two gates, the type gate',
|
|
2810
|
+
'(your own `jsconfig.json` through TypeScript, the same service the editor',
|
|
2811
|
+
'uses: a widget declared as a bare `HTMLElement` makes `widget.update()` an',
|
|
2812
|
+
'error here and there), the structural gate (a cycle between two of your own',
|
|
2813
|
+
'modules, a module outside the project folders, markup written by hand) and',
|
|
2814
|
+
'the prop contract in parallel, and',
|
|
2815
|
+
'prints one report: the gate that failed, the file at `line:column`, and what',
|
|
2816
|
+
'to do about it. A project that declares its own test scripts gets those run',
|
|
2817
|
+
'too; a created app declares none.',
|
|
2818
|
+
'All of them read the installed package, so they are always in sync with what',
|
|
2674
2819
|
'runs. They exit non-zero on a real error: fix the code, not the checker. A',
|
|
2675
2820
|
'warning is allowed to stay only when you can justify it in one sentence.',
|
|
2676
2821
|
'',
|
|
2822
|
+
'Fix what the report names, and nothing else. Every finding carries a file and',
|
|
2823
|
+
'a line: change that line. Do not rewrite the file to make it pass, and do not',
|
|
2824
|
+
'reformat or rename a line the report did not name. If the same error survives',
|
|
2825
|
+
'two attempts, the diagnosis was wrong: revert and hand the report back.',
|
|
2826
|
+
'',
|
|
2677
2827
|
'`npm run ui` proves what is *written*; the rules it cites are the numbered ones',
|
|
2678
2828
|
'in [skills/fletbox-design/SKILL.md](skills/fletbox-design/SKILL.md),',
|
|
2679
2829
|
'and that file is the part a checker cannot do — hierarchy, copy, whether the',
|
|
2680
2830
|
'screen solves the problem. Read it before writing a screen, not after.',
|
|
2681
2831
|
'',
|
|
2832
|
+
'Every `npm run verify` also appends a row to',
|
|
2833
|
+
'`.fletbox/metrics/generations.md` — green or not — and `npm run metrics`',
|
|
2834
|
+
'reads those rows: the share of code that passed the gate with no correction',
|
|
2835
|
+
'at all, plus the failures that recur most often. When the same code comes',
|
|
2836
|
+
'back a third time with no rule behind it, the command fails: that is the',
|
|
2837
|
+
'signal to write the rule, not to fix the same line again.',
|
|
2838
|
+
'',
|
|
2682
2839
|
'When the error is a mistyped name, `npm run fix` rewrites it for you — a',
|
|
2683
2840
|
'relative path that does not resolve, a name the target module never exports,',
|
|
2684
2841
|
'an identifier used without its `import`, and a prop key the API does not know',
|
|
@@ -2686,6 +2843,13 @@ export const agentsMd = (appName, template, version) =>
|
|
|
2686
2843
|
'one real name is close enough; anything ambiguous is printed untouched for you',
|
|
2687
2844
|
'to decide. Run `npm run check` again after it, and read the diff.',
|
|
2688
2845
|
'',
|
|
2846
|
+
'When you are lost, do not guess which gate to run: `flet-box report` names',
|
|
2847
|
+
'every file of this project (clean ones included), its verdict, its widgets,',
|
|
2848
|
+
'and a ranked next step; `flet-box tools` is the catalogue that maps a',
|
|
2849
|
+
'question to the command. Both are read-only — no repair, no metrics row —',
|
|
2850
|
+
'and the only file they can write is the report page, when `--html` asks for',
|
|
2851
|
+
'it, into `.fletbox/reports/` (generated output, not committed).',
|
|
2852
|
+
'',
|
|
2689
2853
|
'## 6. Memory — do not relearn the same thing',
|
|
2690
2854
|
'',
|
|
2691
2855
|
'The full format lives in [skills/fletbox-memory/SKILL.md](skills/fletbox-memory/SKILL.md).',
|
|
@@ -2708,8 +2872,14 @@ export const agentsMd = (appName, template, version) =>
|
|
|
2708
2872
|
'| Command | What it does |',
|
|
2709
2873
|
'| --- | --- |',
|
|
2710
2874
|
'| `npm run dev` | static dev server on port 8000 |',
|
|
2875
|
+
'| `npm run verify` | parses every module, then every gate, then your test scripts: one report, one exit code |',
|
|
2711
2876
|
'| `npm run check` | anti-hallucination gate over `src/` |',
|
|
2712
2877
|
'| `npm run fix` | rewrite mistyped imports and prop names it can prove |',
|
|
2878
|
+
'| `npm run metrics` | the north metric: how much code passed the gate with no correction |',
|
|
2879
|
+
'| `flet-box report` | every file with its verdict, the widget ledger, and a ranked next step (read-only) |',
|
|
2880
|
+
'| `flet-box report --html` | the same report as one self-contained page in `.fletbox/reports/` |',
|
|
2881
|
+
'| `flet-box tools` | the catalogue: which command answers which question (`--emit` writes `.fletbox/api/tools.json`) |',
|
|
2882
|
+
'| `flet-box mcp` | the same read-only tools over MCP stdio, for an assistant with no shell |',
|
|
2713
2883
|
'| `npm run check -- --props Button` | print the real props of a widget |',
|
|
2714
2884
|
'| `flet-box unpack Button` | everything Button accepts **and that actually works** |',
|
|
2715
2885
|
'| `flet-box unpack Button --example` | a paste-ready call using only live props |',
|
|
@@ -2828,6 +2998,43 @@ export const memoryStatusMd = (appName, template) => {
|
|
|
2828
2998
|
].join('\n');
|
|
2829
2999
|
};
|
|
2830
3000
|
|
|
3001
|
+
export const metricsLogMd = () =>
|
|
3002
|
+
[
|
|
3003
|
+
'# Generations',
|
|
3004
|
+
'',
|
|
3005
|
+
'The log of every `npm run verify` run in this app, newest last. One row per',
|
|
3006
|
+
'run: when it happened, whether the gates were green, which gates failed and',
|
|
3007
|
+
'which codes they reported.',
|
|
3008
|
+
'',
|
|
3009
|
+
'A **generation** is a run of consecutive failing rows that ends at the first',
|
|
3010
|
+
'green one — the rows before it are the corrections that were needed. A green',
|
|
3011
|
+
'row that follows a green row is a generation that needed none.',
|
|
3012
|
+
'',
|
|
3013
|
+
'**The north metric** is the share of generations whose *first* run was green:',
|
|
3014
|
+
'the code that passed the gate with no correction at all. It is measured by',
|
|
3015
|
+
'`npm run metrics`, which also prints the three failures that recurred most',
|
|
3016
|
+
'often and the rule each one should belong to. That list is the loop: a code',
|
|
3017
|
+
'that keeps coming back and has no rule behind it is a rule that has not been',
|
|
3018
|
+
'written yet.',
|
|
3019
|
+
'',
|
|
3020
|
+
'## Gate defects',
|
|
3021
|
+
'',
|
|
3022
|
+
'A code listed here was reported by the gate, not by the code it checked. The',
|
|
3023
|
+
'window is the first and the last day it accounts for, so the runs inside it',
|
|
3024
|
+
'count as passed and never feed the rule loop: that is the framework\'s debt,',
|
|
3025
|
+
'not a correction this app needed. `Fixed` empty (`—`) means the defect is',
|
|
3026
|
+
'still open. A run after `Fixed` counts again — a defect that comes back is',
|
|
3027
|
+
'this app\'s finding until the window is reopened. Remove a row to hand its',
|
|
3028
|
+
'findings back.',
|
|
3029
|
+
'',
|
|
3030
|
+
'| Code | Seen | Fixed | Where |',
|
|
3031
|
+
'| --- | --- | --- | --- |',
|
|
3032
|
+
'',
|
|
3033
|
+
'| When | Project | Result | Gates | Codes |',
|
|
3034
|
+
'| --- | --- | --- | --- | --- |',
|
|
3035
|
+
'',
|
|
3036
|
+
].join('\n');
|
|
3037
|
+
|
|
2831
3038
|
export const memoryIndexMd = (appName) =>
|
|
2832
3039
|
[
|
|
2833
3040
|
'# Memory index',
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file bin/utils/toolkit.js
|
|
3
|
+
* @description The diagnosis toolkit, as data — one entry per question an agent
|
|
4
|
+
* may ask, and the command that answers it.
|
|
5
|
+
*
|
|
6
|
+
* The problem this file solves is not "there are no checkers". There are ten,
|
|
7
|
+
* and each one is right about its own slice. The problem is that an agent does
|
|
8
|
+
* not know which one to run for the question in front of it, so it runs the one
|
|
9
|
+
* it remembers and reports a number that answers something else.
|
|
10
|
+
*
|
|
11
|
+
* So the tools are listed here with the question each one answers, in the
|
|
12
|
+
* language a person uses when they are stuck, and every entry names the exact
|
|
13
|
+
* command. The CLI prints this list (`flet-box tools`), the report links every
|
|
14
|
+
* finding back to the tool that proves it, and the MCP server (`flet-box mcp`)
|
|
15
|
+
* exposes the same entries to an assistant that has no shell. One source, three
|
|
16
|
+
* mouths.
|
|
17
|
+
*
|
|
18
|
+
* The rule for membership: a tool in this catalogue **reads**. No entry may
|
|
19
|
+
* repair, rename, delete, build or write — those are decisions, and the assistant
|
|
20
|
+
* asks a human before taking one. `writes` is therefore empty on every entry;
|
|
21
|
+
* the field exists so a future write tool cannot be added silently.
|
|
22
|
+
*
|
|
23
|
+
* Pure data: this module imports nothing. Renderers live in
|
|
24
|
+
* `bin/commands/tools.js` and `bin/mcp.js`.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/** Bumped only when an entry's shape changes, so a cached copy can be noticed. */
|
|
28
|
+
export const CATALOG_VERSION = 1;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Every read-only tool, ordered from "where do I start" to "one narrow check".
|
|
32
|
+
*
|
|
33
|
+
* `params` is the JSON-Schema source used by the MCP server:
|
|
34
|
+
* `{ name, type: "string"|"boolean", required?, description }`.
|
|
35
|
+
*/
|
|
36
|
+
export const TOOLS = [
|
|
37
|
+
{
|
|
38
|
+
id: "report",
|
|
39
|
+
title: "Project report",
|
|
40
|
+
question: "Where is the work? Which file is wrong, how wrong, and what is missing?",
|
|
41
|
+
cli: "flet-box report",
|
|
42
|
+
usage: "flet-box report [--json | --md | --html] [--tests]",
|
|
43
|
+
output: ["text", "json", "md", "html"],
|
|
44
|
+
writes: ["--html only: .fletbox/reports/report.html (or --out <file>)"],
|
|
45
|
+
params: [
|
|
46
|
+
{ name: "tests", type: "boolean", description: "Also run the project's own test scripts (slow)." },
|
|
47
|
+
{ name: "root", type: "string", description: "Project folder; defaults to the current directory." },
|
|
48
|
+
],
|
|
49
|
+
when: [
|
|
50
|
+
"after any edit, before saying the work is done",
|
|
51
|
+
"when a screen renders but is wrong and the file is not obvious",
|
|
52
|
+
"when the user asks what is missing or what to do next",
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "widget_props",
|
|
57
|
+
title: "Widget props",
|
|
58
|
+
question: "Which props does this widget really accept, with types and deprecations?",
|
|
59
|
+
cli: "flet-box check-api --props <Widget>",
|
|
60
|
+
usage: "flet-box check-api --props Text",
|
|
61
|
+
output: ["text"],
|
|
62
|
+
writes: [],
|
|
63
|
+
params: [
|
|
64
|
+
{ name: "widget", type: "string", required: true, description: "Widget name, e.g. Text or Container." },
|
|
65
|
+
{ name: "root", type: "string", description: "Project folder; defaults to the current directory." },
|
|
66
|
+
],
|
|
67
|
+
when: ["before writing a call", "when a prop silently does nothing", "when the cheatsheet looks wrong"],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "widget_contract",
|
|
71
|
+
title: "Widget contract",
|
|
72
|
+
question: "Do the declaration, the engine and the docs agree about this widget — and which props are dead?",
|
|
73
|
+
cli: "flet-box unpack <Widget> --json",
|
|
74
|
+
usage: "flet-box unpack Text --json",
|
|
75
|
+
output: ["text", "json"],
|
|
76
|
+
writes: [],
|
|
77
|
+
params: [
|
|
78
|
+
{ name: "widget", type: "string", required: true, description: "Widget name, e.g. Text or Container." },
|
|
79
|
+
{ name: "root", type: "string", description: "Project folder; defaults to the current directory." },
|
|
80
|
+
],
|
|
81
|
+
when: ["when a prop is typed but does nothing", "when reading the framework's own gaps"],
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: "list_widgets",
|
|
85
|
+
title: "List widgets",
|
|
86
|
+
question: "What exists in this version of the framework?",
|
|
87
|
+
cli: "flet-box check-api --list",
|
|
88
|
+
usage: "flet-box check-api --list",
|
|
89
|
+
output: ["text"],
|
|
90
|
+
writes: [],
|
|
91
|
+
params: [{ name: "root", type: "string", description: "Project folder; defaults to the current directory." }],
|
|
92
|
+
when: ["before inventing a name", "when unsure whether a helper is real"],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: "check",
|
|
96
|
+
title: "API check",
|
|
97
|
+
question: "Is any widget, prop or import invented?",
|
|
98
|
+
cli: "flet-box check-api",
|
|
99
|
+
usage: "flet-box check-api [--json] [--full]",
|
|
100
|
+
output: ["text", "json"],
|
|
101
|
+
writes: [],
|
|
102
|
+
params: [
|
|
103
|
+
{ name: "full", type: "boolean", description: "List every warning site instead of collapsing repeated ones." },
|
|
104
|
+
{ name: "root", type: "string", description: "Project folder; defaults to the current directory." },
|
|
105
|
+
],
|
|
106
|
+
when: ["when a screen is blank or a widget renders nothing", "after copying code from another project"],
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: "design",
|
|
110
|
+
title: "Design check",
|
|
111
|
+
question: "Are the tokens, the scale, the tap targets, the labels and the contrast legal?",
|
|
112
|
+
cli: "flet-box check-ui",
|
|
113
|
+
usage: "flet-box check-ui [--json] [--strict] [--tokens]",
|
|
114
|
+
output: ["text", "json"],
|
|
115
|
+
writes: [],
|
|
116
|
+
params: [
|
|
117
|
+
{ name: "tokens", type: "boolean", description: "Print the measured contrast of every colour token and exit." },
|
|
118
|
+
{ name: "strict", type: "boolean", description: "Fail on contrast warnings too." },
|
|
119
|
+
{ name: "root", type: "string", description: "Project folder; defaults to the current directory." },
|
|
120
|
+
],
|
|
121
|
+
when: ["after building or editing a screen", "when the user says it looks off"],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: "syntax",
|
|
125
|
+
title: "Parse check",
|
|
126
|
+
question: "Does every module parse?",
|
|
127
|
+
cli: "flet-box verify --only syntax --no-tests",
|
|
128
|
+
usage: "flet-box verify --only syntax --no-tests [--json]",
|
|
129
|
+
output: ["text", "json"],
|
|
130
|
+
writes: [],
|
|
131
|
+
params: [{ name: "root", type: "string", description: "Project folder; defaults to the current directory." }],
|
|
132
|
+
when: ["when the app is blank and the console says nothing", "before blaming the framework"],
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
id: "types",
|
|
136
|
+
title: "Type check",
|
|
137
|
+
question: "What does the editor complain about?",
|
|
138
|
+
cli: "flet-box verify --only types --no-tests",
|
|
139
|
+
usage: "flet-box verify --only types --no-tests [--json]",
|
|
140
|
+
output: ["text", "json"],
|
|
141
|
+
writes: [],
|
|
142
|
+
params: [{ name: "root", type: "string", description: "Project folder; defaults to the current directory." }],
|
|
143
|
+
when: ["when a widget method or prop is flagged in the editor", "after upgrading the package"],
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: "metrics",
|
|
147
|
+
title: "Recurring failures",
|
|
148
|
+
question: "Which mistakes keep coming back, and does a rule own each one?",
|
|
149
|
+
cli: "flet-box metrics",
|
|
150
|
+
usage: "flet-box metrics [--log <file>]",
|
|
151
|
+
output: ["text"],
|
|
152
|
+
writes: [],
|
|
153
|
+
params: [{ name: "root", type: "string", description: "Project folder; defaults to the current directory." }],
|
|
154
|
+
when: ["when the same finding keeps returning", "when deciding which rule to write"],
|
|
155
|
+
},
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* One entry by id.
|
|
160
|
+
*
|
|
161
|
+
* @param {string} id
|
|
162
|
+
* @returns {object|null}
|
|
163
|
+
*/
|
|
164
|
+
export const toolById = (id) => TOOLS.find((tool) => tool.id === id) || null;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The catalogue as the JSON a tool reads: stable, relative to nothing, and with
|
|
168
|
+
* the invariant stated in the payload so a consumer can assert it.
|
|
169
|
+
*
|
|
170
|
+
* @returns {object}
|
|
171
|
+
*/
|
|
172
|
+
export const catalogJson = () => ({
|
|
173
|
+
tool: "flet-box tools",
|
|
174
|
+
version: CATALOG_VERSION,
|
|
175
|
+
// No entry edits a source, deletes, builds or publishes. The only file any of
|
|
176
|
+
// them can produce is the report's own HTML, and only when `--html` asks.
|
|
177
|
+
readOnly: true,
|
|
178
|
+
tools: TOOLS.map((tool) => ({
|
|
179
|
+
id: tool.id,
|
|
180
|
+
title: tool.title,
|
|
181
|
+
question: tool.question,
|
|
182
|
+
cli: tool.cli,
|
|
183
|
+
usage: tool.usage,
|
|
184
|
+
output: tool.output,
|
|
185
|
+
writes: tool.writes,
|
|
186
|
+
// The arguments are part of the map: a reader that cannot ask the MCP
|
|
187
|
+
// server still has to know that `widget_props` needs a widget name.
|
|
188
|
+
params: tool.params,
|
|
189
|
+
when: tool.when,
|
|
190
|
+
})),
|
|
191
|
+
});
|