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
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,
|
|
@@ -1296,14 +1387,21 @@ export const githubWorkflow = (appName = "FletBox App", siteUrl = null) => {
|
|
|
1296
1387
|
const site = /^https?:\/\//i.test(String(siteUrl || ""))
|
|
1297
1388
|
? String(siteUrl).replace(/\/?$/, "/")
|
|
1298
1389
|
: `https://TU-USUARIO.github.io/${slug}/`;
|
|
1299
|
-
// Only when the owner is still a guess
|
|
1300
|
-
//
|
|
1301
|
-
//
|
|
1390
|
+
// Only when the owner is still a guess. The build no longer trusts the
|
|
1391
|
+
// placeholder in fletbox.config.json (createBundle derives the real Pages
|
|
1392
|
+
// address from this repository), so what is left to correct here is the
|
|
1393
|
+
// address a human reads before reading a single step.
|
|
1302
1394
|
const todo = site.includes("TU-USUARIO")
|
|
1303
1395
|
? `# The owner above is a placeholder: this project was created without \`--repo\`,
|
|
1304
|
-
# so the folder name is only a guess at the repo name.
|
|
1305
|
-
#
|
|
1306
|
-
#
|
|
1396
|
+
# so the folder name is only a guess at the repo name. Put the real owner here
|
|
1397
|
+
# once the repository exists.
|
|
1398
|
+
#
|
|
1399
|
+
# \`fletbox.config.json\` may keep its own placeholder: the build recognises it as
|
|
1400
|
+
# "not configured" and derives the address from this repo's repository field or
|
|
1401
|
+
# git remote, so an installed app polls the site this workflow deploys. The URL
|
|
1402
|
+
# that landed in the manifest is printed by the build and by the
|
|
1403
|
+
# \`Where this lives\` step below. Set \`update.url\` by hand only when this app
|
|
1404
|
+
# will live on another host.
|
|
1307
1405
|
`
|
|
1308
1406
|
: "";
|
|
1309
1407
|
return `name: Deploy ${appName}
|
|
@@ -1621,6 +1719,7 @@ android/capacitor-cordova-android-plugins/
|
|
|
1621
1719
|
package-lock.json
|
|
1622
1720
|
.DS_Store
|
|
1623
1721
|
*.log
|
|
1722
|
+
.fletbox/reports/
|
|
1624
1723
|
`;
|
|
1625
1724
|
|
|
1626
1725
|
export const readme = (name, _template, appId) => `# ${name}
|
|
@@ -1665,6 +1764,8 @@ framework from scratch:
|
|
|
1665
1764
|
Claude Code and Cursor.
|
|
1666
1765
|
- \`.fletbox/api/cheatsheet.md\` — every widget and its real props, generated from the
|
|
1667
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.
|
|
1668
1769
|
- \`.fletbox/api/api.json\` — the same surface as data: per prop its engine target,
|
|
1669
1770
|
default, aliases and a \`sample\` value the types prove works. Write calls from this.
|
|
1670
1771
|
- \`.fletbox/memory/\` — notes the agent keeps between sessions, so it does not
|
|
@@ -1680,7 +1781,8 @@ npm run ui # clean-UI gate: tokens, scale, tap targets,
|
|
|
1680
1781
|
# contrast — each finding cites the rule it broke
|
|
1681
1782
|
npm run ui -- --tokens # measured contrast of every token, on both surfaces
|
|
1682
1783
|
npm run ui -- --strict # warnings (a one-off tint, an image with no alt) fail too
|
|
1683
|
-
npm run fix # rewrite
|
|
1784
|
+
npm run fix # rewrite what it can prove: imports, prop typos,
|
|
1785
|
+
# renamed props
|
|
1684
1786
|
npm run api # regenerate .fletbox/api/api.json and api.md
|
|
1685
1787
|
flet-box unpack Button # each prop, where it really goes, and what is dead
|
|
1686
1788
|
flet-box unpack Button --example # a paste-ready call using only verified props
|
|
@@ -1779,9 +1881,12 @@ Two workflow files ship with the project, and they answer to different triggers:
|
|
|
1779
1881
|
The address this project publishes to is written at the top of
|
|
1780
1882
|
\`.github/workflows/build.yml\`. \`flet-box create <name> --repo <user>/<repo>\`
|
|
1781
1883
|
fills it in from the repo; without \`--repo\` it stays
|
|
1782
|
-
\`https://TU-USUARIO.github.io/<name>/\`, and the header
|
|
1783
|
-
|
|
1784
|
-
|
|
1884
|
+
\`https://TU-USUARIO.github.io/<name>/\`, and the header explains what is still a
|
|
1885
|
+
guess. The build ignores that placeholder in \`fletbox.config.json\` and derives
|
|
1886
|
+
the real address from this repository, so the OTA feed is correct either way.
|
|
1887
|
+
|
|
1888
|
+
The Pages address is a sub-path, so \`index.html\` uses relative asset paths and
|
|
1889
|
+
works without changes.
|
|
1785
1890
|
|
|
1786
1891
|
**Why a push does not update anyone's app:** this same address is the OTA feed
|
|
1787
1892
|
installed apps poll, and the update check compares bundle **hashes**, not version
|
|
@@ -1871,9 +1976,10 @@ updates:
|
|
|
1871
1976
|
\`\`\`
|
|
1872
1977
|
|
|
1873
1978
|
- The GitHub repository **must be public**.
|
|
1874
|
-
-
|
|
1875
|
-
|
|
1876
|
-
\`
|
|
1979
|
+
- \`TU-USUARIO\`/\`TU-REPO\` are a placeholder, not a setting: the build sees them as
|
|
1980
|
+
"not configured" and derives the Pages address from this project's \`repository\`
|
|
1981
|
+
field or git remote, so pushing the folder is enough. Edit \`update.url\` only
|
|
1982
|
+
when the app lives on another HTTPS host that serves your \`config.json\` + bundle.
|
|
1877
1983
|
- Enable GitHub Pages: **Settings → Pages → Source: GitHub Actions**. Without
|
|
1878
1984
|
that the deploy step is green and no site exists.
|
|
1879
1985
|
- Cut a release with \`flet-box release --push\` (bumps the version, rebuilds
|
|
@@ -2067,10 +2173,12 @@ export const AppBarComponent = ({ title, isMobile = false, showBack = false, onB
|
|
|
2067
2173
|
size: 24,
|
|
2068
2174
|
height: 40,
|
|
2069
2175
|
color: colors.text,
|
|
2176
|
+
'aria-label': 'Open the menu',
|
|
2070
2177
|
onclick: () => openDrawer()
|
|
2071
2178
|
})
|
|
2072
2179
|
}) : null,
|
|
2073
|
-
// 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.
|
|
2074
2182
|
actions: [
|
|
2075
2183
|
Tooltip({
|
|
2076
2184
|
text: isDark ? 'Switch to light' : 'Switch to dark',
|
|
@@ -2080,6 +2188,7 @@ export const AppBarComponent = ({ title, isMobile = false, showBack = false, onB
|
|
|
2080
2188
|
size: 24,
|
|
2081
2189
|
height: 40,
|
|
2082
2190
|
color: colors.text,
|
|
2191
|
+
'aria-label': isDark ? 'Switch to light' : 'Switch to dark',
|
|
2083
2192
|
onclick: () => toggleTheme()
|
|
2084
2193
|
})
|
|
2085
2194
|
})
|
|
@@ -2422,6 +2531,13 @@ export const agentsMd = (appName, template, version) =>
|
|
|
2422
2531
|
'fixes every size and color you may use, and the checker in section 5',
|
|
2423
2532
|
'proves you right or wrong in seconds.',
|
|
2424
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
|
+
'',
|
|
2425
2541
|
'## 0. Start here — the skills folder',
|
|
2426
2542
|
'',
|
|
2427
2543
|
'Everything this framework knows about building a professional screen is grouped',
|
|
@@ -2477,7 +2593,7 @@ export const agentsMd = (appName, template, version) =>
|
|
|
2477
2593
|
'// Correct',
|
|
2478
2594
|
"Text({ text: 'Hello', size: 18, color: colors.primary })",
|
|
2479
2595
|
"Button({ text: 'Save', onPress: save, bgColor: colors.primary })",
|
|
2480
|
-
'
|
|
2596
|
+
'Container({ child: Text({ text: count }), padding: 12 })',
|
|
2481
2597
|
'```',
|
|
2482
2598
|
'',
|
|
2483
2599
|
'```js',
|
|
@@ -2495,7 +2611,7 @@ export const agentsMd = (appName, template, version) =>
|
|
|
2495
2611
|
' at runtime and nothing fetches a CDN.',
|
|
2496
2612
|
'- No `.jsx`, `.tsx`, `.vue` or `.svelte` files. Plain `.js`, ESM imports with a',
|
|
2497
2613
|
' explicit `.js` extension.',
|
|
2498
|
-
'- Styling is props (`bgColor`, `
|
|
2614
|
+
'- Styling is props (`bgColor`, `borderRadius`, `padding`), not class strings or',
|
|
2499
2615
|
' `styled-components`.',
|
|
2500
2616
|
'- `child` takes one widget; `children` takes an array or string. Swapping them',
|
|
2501
2617
|
' is the most common silent bug.',
|
|
@@ -2600,6 +2716,11 @@ export const agentsMd = (appName, template, version) =>
|
|
|
2600
2716
|
'',
|
|
2601
2717
|
'- A screen uses **one** brand color. Two accent colors competing on one view',
|
|
2602
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.',
|
|
2603
2724
|
'- Never type a hex or `rgb()` literal. If a token is missing, add it to a',
|
|
2604
2725
|
' `theme.js` in this app that re-exports `colors` — do not scatter the value.',
|
|
2605
2726
|
'- A frosted surface is `colors.glass` with `colors.glassBorder` plus a',
|
|
@@ -2617,7 +2738,7 @@ export const agentsMd = (appName, template, version) =>
|
|
|
2617
2738
|
'',
|
|
2618
2739
|
'**Space and type: an 8-based scale.** Use these numbers and nothing between',
|
|
2619
2740
|
'them. They are plain props (`padding`, `gap`, `margin`, `width`, `height`,',
|
|
2620
|
-
'`size`, `
|
|
2741
|
+
'`size`, `borderRadius`) — the factory takes the number, no unit and no `px` string.',
|
|
2621
2742
|
'',
|
|
2622
2743
|
'```',
|
|
2623
2744
|
'spacing 4 · hairline, icon inset',
|
|
@@ -2645,29 +2766,76 @@ export const agentsMd = (appName, template, version) =>
|
|
|
2645
2766
|
'- `useState` bindings, `flex` and `gap` do the layout. Reach for an absolute',
|
|
2646
2767
|
' offset only for overlays, and use `stackPosition` rather than raw coordinates.',
|
|
2647
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
|
+
'',
|
|
2648
2782
|
'## 5. Verification loop (required)',
|
|
2649
2783
|
'',
|
|
2650
2784
|
'After every edit, before you say a task is done:',
|
|
2651
2785
|
'',
|
|
2652
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',
|
|
2653
2793
|
'npm run check # static gate: invented widgets/props, positional args,',
|
|
2654
2794
|
' # foreign imports, and broken imports between your own',
|
|
2655
2795
|
' # modules (a name a file never exports)',
|
|
2656
2796
|
'npm run ui # clean-UI gate: the tokens and the scale above, 40px',
|
|
2657
2797
|
' # targets, labels, contrast — every finding cites the',
|
|
2658
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',
|
|
2659
2804
|
'npm run dev # serve on :8000 — check the UI actually renders',
|
|
2660
2805
|
'```',
|
|
2661
2806
|
'',
|
|
2662
|
-
'
|
|
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',
|
|
2663
2819
|
'runs. They exit non-zero on a real error: fix the code, not the checker. A',
|
|
2664
2820
|
'warning is allowed to stay only when you can justify it in one sentence.',
|
|
2665
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
|
+
'',
|
|
2666
2827
|
'`npm run ui` proves what is *written*; the rules it cites are the numbered ones',
|
|
2667
2828
|
'in [skills/fletbox-design/SKILL.md](skills/fletbox-design/SKILL.md),',
|
|
2668
2829
|
'and that file is the part a checker cannot do — hierarchy, copy, whether the',
|
|
2669
2830
|
'screen solves the problem. Read it before writing a screen, not after.',
|
|
2670
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
|
+
'',
|
|
2671
2839
|
'When the error is a mistyped name, `npm run fix` rewrites it for you — a',
|
|
2672
2840
|
'relative path that does not resolve, a name the target module never exports,',
|
|
2673
2841
|
'an identifier used without its `import`, and a prop key the API does not know',
|
|
@@ -2675,6 +2843,13 @@ export const agentsMd = (appName, template, version) =>
|
|
|
2675
2843
|
'one real name is close enough; anything ambiguous is printed untouched for you',
|
|
2676
2844
|
'to decide. Run `npm run check` again after it, and read the diff.',
|
|
2677
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
|
+
'',
|
|
2678
2853
|
'## 6. Memory — do not relearn the same thing',
|
|
2679
2854
|
'',
|
|
2680
2855
|
'The full format lives in [skills/fletbox-memory/SKILL.md](skills/fletbox-memory/SKILL.md).',
|
|
@@ -2697,8 +2872,14 @@ export const agentsMd = (appName, template, version) =>
|
|
|
2697
2872
|
'| Command | What it does |',
|
|
2698
2873
|
'| --- | --- |',
|
|
2699
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 |',
|
|
2700
2876
|
'| `npm run check` | anti-hallucination gate over `src/` |',
|
|
2701
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 |',
|
|
2702
2883
|
'| `npm run check -- --props Button` | print the real props of a widget |',
|
|
2703
2884
|
'| `flet-box unpack Button` | everything Button accepts **and that actually works** |',
|
|
2704
2885
|
'| `flet-box unpack Button --example` | a paste-ready call using only live props |',
|
|
@@ -2817,6 +2998,43 @@ export const memoryStatusMd = (appName, template) => {
|
|
|
2817
2998
|
].join('\n');
|
|
2818
2999
|
};
|
|
2819
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
|
+
|
|
2820
3038
|
export const memoryIndexMd = (appName) =>
|
|
2821
3039
|
[
|
|
2822
3040
|
'# Memory index',
|