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.
Files changed (156) hide show
  1. package/AGENTS_FOR_CODE_GEN.md +284 -0
  2. package/CONTRIBUTING.md +238 -0
  3. package/README.md +17 -13
  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 +18 -1
  11. package/bin/commands/createBundle.js +20 -8
  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/bundleConfig.js +47 -3
  20. package/bin/utils/contract.js +27 -0
  21. package/bin/utils/dtsProps.js +5 -1
  22. package/bin/utils/migrationGuide.js +146 -0
  23. package/bin/utils/moduleExports.js +9 -5
  24. package/bin/utils/templates.js +248 -30
  25. package/bin/utils/toolkit.js +191 -0
  26. package/docs/README.md +26 -1
  27. package/docs/arquitectura.md +1 -1
  28. package/docs/cli/README.md +201 -11
  29. package/docs/guides/README.md +6 -0
  30. package/docs/guides/accessibility.md +2 -1
  31. package/docs/guides/ai-toolkit.md +166 -0
  32. package/docs/guides/compatibility.md +1 -1
  33. package/docs/guides/first-app.md +6 -7
  34. package/docs/guides/migration.md +92 -0
  35. package/docs/guides/minification-and-protection.md +2 -1
  36. package/docs/guides/mobile-and-platforms.md +1 -1
  37. package/docs/guides/online-updates.md +20 -6
  38. package/docs/server/deployment.md +1 -1
  39. package/docs/tools/styling.md +2 -2
  40. package/docs/tools/theme.md +139 -4
  41. package/docs/widget/Accordion.md +74 -8
  42. package/docs/widget/AdMob.md +68 -3
  43. package/docs/widget/AdSense.md +67 -2
  44. package/docs/widget/AlertDialog.md +1 -1
  45. package/docs/widget/AnimatedBox.md +80 -39
  46. package/docs/widget/AnimatedText.md +74 -18
  47. package/docs/widget/Audio.md +73 -14
  48. package/docs/widget/Avatar.md +69 -4
  49. package/docs/widget/Badge.md +68 -3
  50. package/docs/widget/BottomSheet.md +2 -2
  51. package/docs/widget/Button.md +69 -3
  52. package/docs/widget/Card.md +69 -4
  53. package/docs/widget/Carousel.md +67 -2
  54. package/docs/widget/Chart.md +67 -2
  55. package/docs/widget/Checkbox.md +74 -8
  56. package/docs/widget/Chip.md +68 -3
  57. package/docs/widget/CircularBar.md +74 -15
  58. package/docs/widget/CircularChart.md +71 -10
  59. package/docs/widget/CodeViewer.md +67 -2
  60. package/docs/widget/Column.md +68 -3
  61. package/docs/widget/Container.md +68 -3
  62. package/docs/widget/DataTable.md +67 -2
  63. package/docs/widget/Divider.md +67 -2
  64. package/docs/widget/DraggBox.md +67 -2
  65. package/docs/widget/Dropdown.md +76 -10
  66. package/docs/widget/DroppBox.md +67 -2
  67. package/docs/widget/FloatingActionButton.md +68 -3
  68. package/docs/widget/GridView.md +67 -2
  69. package/docs/widget/Icon.md +71 -5
  70. package/docs/widget/Image.md +68 -3
  71. package/docs/widget/Input.md +68 -3
  72. package/docs/widget/Inspector.md +2 -2
  73. package/docs/widget/InstallButton.md +73 -13
  74. package/docs/widget/ListTile.md +69 -3
  75. package/docs/widget/ListView.md +67 -2
  76. package/docs/widget/Maps.md +68 -3
  77. package/docs/widget/Markdown.md +74 -15
  78. package/docs/widget/MatrixRain.md +72 -11
  79. package/docs/widget/Menu.md +209 -0
  80. package/docs/widget/Modal.md +3 -3
  81. package/docs/widget/Pagination.md +68 -3
  82. package/docs/widget/ParallaxBox.md +72 -12
  83. package/docs/widget/ProgressBar.md +69 -4
  84. package/docs/widget/QRCode.md +67 -2
  85. package/docs/widget/README.md +54 -50
  86. package/docs/widget/Radio.md +76 -10
  87. package/docs/widget/Rating.md +70 -6
  88. package/docs/widget/Row.md +68 -3
  89. package/docs/widget/Skeleton.md +68 -3
  90. package/docs/widget/Slider.md +75 -9
  91. package/docs/widget/SnackBar.md +2 -2
  92. package/docs/widget/Stack.md +68 -3
  93. package/docs/widget/Stepper.md +68 -3
  94. package/docs/widget/Switch.md +74 -8
  95. package/docs/widget/Text.md +71 -4
  96. package/docs/widget/ThemePicker.md +197 -0
  97. package/docs/widget/Tooltip.md +72 -7
  98. package/docs/widget/TreeView.md +70 -10
  99. package/docs/widget/Video.md +71 -10
  100. package/package.json +10 -3
  101. package/skills/fletbox-design/SKILL.md +89 -5
  102. package/skills/fletbox-motion/SKILL.md +1 -1
  103. package/skills/fletbox-rules/SKILL.md +56 -4
  104. package/src/core/runApp.js +12 -0
  105. package/src/index.d.ts +374 -101
  106. package/src/index.js +9 -0
  107. package/src/navigations/AdaptiveScaffold.js +2 -2
  108. package/src/navigations/BottomNavigation.js +1 -1
  109. package/src/navigations/Scaffold.js +18 -6
  110. package/src/tools/useState.js +11 -4
  111. package/src/utils/colors.js +275 -0
  112. package/src/utils/errorBoundary.js +288 -0
  113. package/src/utils/index.js +10 -2
  114. package/src/utils/stackPosition.js +39 -24
  115. package/src/utils/syntaxHighlight.js +4 -1
  116. package/src/utils/themes.js +279 -3
  117. package/src/widget-factory/addChildren.js +17 -10
  118. package/src/widget-factory/assignProps.js +18 -6
  119. package/src/widget-factory/createWidget.js +7 -0
  120. package/src/widget-factory/deprecations.js +77 -0
  121. package/src/widget-factory/translateProps.js +11 -1
  122. package/src/widget-factory/warnings.js +1 -27
  123. package/src/widget-factory/widgetFactory.js +59 -17
  124. package/src/widgets/Accordion.js +21 -7
  125. package/src/widgets/Badge.js +11 -2
  126. package/src/widgets/Button.js +15 -3
  127. package/src/widgets/Carousel.js +12 -4
  128. package/src/widgets/Chart.js +7 -2
  129. package/src/widgets/Checkbox.js +1 -2
  130. package/src/widgets/Chip.js +24 -10
  131. package/src/widgets/CircularBar.js +9 -3
  132. package/src/widgets/CircularChart.js +5 -3
  133. package/src/widgets/Container.js +6 -0
  134. package/src/widgets/Divider.js +3 -1
  135. package/src/widgets/DraggBox.js +9 -0
  136. package/src/widgets/Dropdown.js +3 -3
  137. package/src/widgets/DroppBox.js +5 -0
  138. package/src/widgets/FloatingActionButton.js +1 -1
  139. package/src/widgets/Icon.js +5 -1
  140. package/src/widgets/InstallButton.js +4 -2
  141. package/src/widgets/Markdown.js +171 -103
  142. package/src/widgets/Menu.js +340 -0
  143. package/src/widgets/Modal.js +7 -1
  144. package/src/widgets/Pagination.js +10 -5
  145. package/src/widgets/ProgressBar.js +5 -4
  146. package/src/widgets/QRCode.js +4 -0
  147. package/src/widgets/Radio.js +1 -2
  148. package/src/widgets/Rating.js +9 -0
  149. package/src/widgets/Slider.js +4 -1
  150. package/src/widgets/SnackBar.js +2 -2
  151. package/src/widgets/Switch.js +1 -2
  152. package/src/widgets/Text.js +9 -5
  153. package/src/widgets/ThemePicker.js +234 -0
  154. package/src/widgets/TreeView.js +2 -1
  155. package/src/widgets/index.js +2 -0
  156. package/docs/CONTRIBUTING.md +0 -156
@@ -0,0 +1,234 @@
1
+ /**
2
+ * @file ThemePicker.js
3
+ * @description A grid of two-tone swatches that picks the screen's accent color.
4
+ *
5
+ * One control, twelve choices, no color theory. Each swatch is a square split
6
+ * down its diagonal: the upper-left half is the family's accent in the light
7
+ * theme, the lower-right half is the same family in the dark one. That is the
8
+ * iOS Settings shape, and it is the only honest way to show a *pair* — a FletBox
9
+ * accent is not one color but the two stops one hue wears in the two themes, and
10
+ * a reader who sees both knows what the app will look like before tapping.
11
+ *
12
+ * Tapping calls `setAccent`, which repaints every widget already on screen and
13
+ * writes the choice to `localStorage`, so the next visit opens in the same color.
14
+ * With `surfaces: true` the tap lays the family over the page as well — background,
15
+ * panels, cards and hairlines all read off the same ramp — so the picker chooses a
16
+ * *theme* and not only a brand color. The picker subscribes to the theme, so a
17
+ * toggle somewhere else on the screen moves the selection here without this widget
18
+ * being asked.
19
+ */
20
+ import { WidgetFactory } from "../widget-factory/index.js";
21
+ import { Text } from "./Text.js";
22
+ import { Icon } from "./Icon.js";
23
+ import {
24
+ colors,
25
+ getAccent,
26
+ getAccentSurfaces,
27
+ setAccent,
28
+ listAccents,
29
+ subscribeTheme,
30
+ } from "../utils/themes.js";
31
+ import { readableInk } from "../utils/readableInk.js";
32
+ import { composeUpdate } from "../utils/composeUpdate.js";
33
+ import { TAP_MINIMUM, LABEL_TEXT, PILL } from "../tools/scale.js";
34
+
35
+ /**
36
+ * @param {Object} props
37
+ * @returns {HTMLElement} The picker's root row.
38
+ */
39
+ export const ThemePicker = (props = {}) => {
40
+ const {
41
+ accents = listAccents(),
42
+ size = TAP_MINIMUM,
43
+ showLabels = false,
44
+ label = "Accent color",
45
+ disabled = false,
46
+ surfaces = false,
47
+ onChange,
48
+ ...rest
49
+ } = props;
50
+
51
+ // What the cells are built from, and what a rebuild replaces. Reading these from
52
+ // the closure instead of the destructured props is what lets `update()` change
53
+ // the list: a cell built at construction time would keep painting the family it
54
+ // was born with.
55
+ let choices = accents;
56
+ let labelled = showLabels;
57
+ let off = disabled;
58
+ // Whether a tap lays the family over the page too, or only over the brand.
59
+ let tinted = surfaces;
60
+
61
+ // The diagonal split. `135deg` runs from the top-left corner, so the first stop
62
+ // owns the half a reader's eye reaches first — which is the light theme, the one
63
+ // a picker is usually opened under.
64
+ const swatchPaint = (accent) =>
65
+ `linear-gradient(135deg, ${accent.light} 0 50%, ${accent.dark} 50% 100%)`;
66
+
67
+ // The rim is the only thing that says "this one is on", so it is the one thing
68
+ // that must not be a color nobody can see: `colors.border` measures 1.3:1 on
69
+ // white and 1.2:1 on a dark card. An unselected swatch wears `gray500` — 3:1 on
70
+ // both surfaces, the same choice `Checkbox` makes for an empty box — and the
71
+ // selected one wears the page's own ink, the strongest value the theme has. The
72
+ // tick badge is ink too, and `readableInk` picks whichever of black and white
73
+ // reads on it, so the selected state is unmistakable without spending the accent
74
+ // on it — which rule 28 forbids anyway.
75
+ const rimFor = (selected) =>
76
+ selected ? colors.text : off ? colors.border : colors.gray500;
77
+
78
+ const cells = [];
79
+
80
+ const paintCell = (cell, selected) => {
81
+ cell.swatch.style.border = `2px solid ${rimFor(selected)}`;
82
+ cell.badge.style.display = selected ? "flex" : "none";
83
+ cell.root.setAttribute("aria-checked", String(selected));
84
+ };
85
+
86
+ const paintAll = () => {
87
+ const active = getAccent();
88
+ cells.forEach((cell) => paintCell(cell, cell.accent.id === active));
89
+ };
90
+
91
+ const buildCell = (accent) => {
92
+ const tickSize = Math.max(14, Math.round(size * 0.42));
93
+
94
+ // The tick only ever paints on its own badge, so the pair it has to clear is
95
+ // badge ink on badge plate — a value this file controls, not a user's color.
96
+ const badge = WidgetFactory({
97
+ tag: "span",
98
+ display: "none",
99
+ alignItems: "center",
100
+ justifyContent: "center",
101
+ width: tickSize,
102
+ height: tickSize,
103
+ borderRadius: PILL,
104
+ backgroundColor: colors.text,
105
+ child: Icon({
106
+ name: "check",
107
+ size: Math.max(10, Math.round(size * 0.28)),
108
+ color: readableInk(colors.text, colors.onPrimary),
109
+ }),
110
+ });
111
+
112
+ const swatch = WidgetFactory({
113
+ tag: "span",
114
+ width: size,
115
+ height: size,
116
+ gradient: swatchPaint(accent),
117
+ border: `2px solid ${rimFor(false)}`,
118
+ borderRadius: 8,
119
+ boxSizing: "border-box",
120
+ display: "flex",
121
+ alignItems: "center",
122
+ justifyContent: "center",
123
+ flexShrink: 0,
124
+ transition: "border-color 0.2s ease",
125
+ child: badge,
126
+ });
127
+
128
+ const root = WidgetFactory({
129
+ // The square is a `span` inside a row, so nothing here is a radio unless the
130
+ // widget says so. `role` reaches the factory before `applyEffects`, which is
131
+ // what keeps the engine from labelling the control `button`.
132
+ role: "radio",
133
+ "aria-checked": "false",
134
+ "aria-label": accent.label,
135
+ widgetName: "ThemePicker",
136
+ display: "inline-flex",
137
+ flexDirection: "column",
138
+ alignItems: "center",
139
+ gap: labelled ? 4 : undefined,
140
+ minWidth: size,
141
+ minHeight: size,
142
+ padding: labelled ? 4 : 0,
143
+ cursor: off ? "not-allowed" : "pointer",
144
+ opacity: off ? 0.5 : 1,
145
+ // Pressing a caption must not scale the caption: the root is the hit box and
146
+ // the square is the thing that reads as selected.
147
+ disableTransform: labelled || undefined,
148
+ child: labelled
149
+ ? [
150
+ swatch,
151
+ Text({
152
+ text: accent.label,
153
+ size: LABEL_TEXT,
154
+ color: off ? colors.textDisabled : colors.textSecondary,
155
+ }),
156
+ ]
157
+ : swatch,
158
+ });
159
+
160
+ const cell = { accent, root, swatch, badge };
161
+
162
+ if (off) {
163
+ root.setAttribute("aria-disabled", "true");
164
+ } else {
165
+ // Routed through the factory rather than assigned to `onclick`: `applyEffects`
166
+ // is what gives this cell Enter, Space and a Tab stop, and it only sees
167
+ // handlers that arrive as props.
168
+ root.update({
169
+ onPress: () => {
170
+ // Re-tapping the ticked cell still has to work when the cell changed
171
+ // meaning: the same id paints brand-only or a whole theme depending on
172
+ // `surfaces`, and that flag can arrive through `update()`.
173
+ if (getAccent() === accent.id && getAccentSurfaces() === tinted) return;
174
+ setAccent(accent.id, { surfaces: tinted });
175
+ if (onChange) onChange(accent.id);
176
+ },
177
+ });
178
+ }
179
+
180
+ return cell;
181
+ };
182
+
183
+ const rebuild = () => {
184
+ cells.forEach((cell) => cell.root.remove());
185
+ cells.length = 0;
186
+ choices.forEach((accent) => {
187
+ const cell = buildCell(accent);
188
+ cells.push(cell);
189
+ group.appendChild(cell.root);
190
+ });
191
+ paintAll();
192
+ };
193
+
194
+ const group = WidgetFactory({
195
+ widgetName: "ThemePicker",
196
+ // A row of colored squares is not a control. Named, it is one, and the twelve
197
+ // radios inside it are read as options of a single question.
198
+ role: "radiogroup",
199
+ "aria-label": label,
200
+ display: "flex",
201
+ flexWrap: "wrap",
202
+ alignItems: "flex-start",
203
+ gap: 8,
204
+ ...rest,
205
+ });
206
+
207
+ rebuild();
208
+
209
+ // The selection is the theme's state, not this widget's. A toggle in the app bar,
210
+ // an OS flip, or a `setAccent` from a settings screen all move what is checked
211
+ // here, and none of them can be reached by a prop.
212
+ const unsubscribe = subscribeTheme(paintAll);
213
+ group.onUnmount(() => {
214
+ unsubscribe();
215
+ cells.length = 0;
216
+ });
217
+
218
+ composeUpdate(
219
+ group,
220
+ ["accents", "showLabels", "label", "disabled", "surfaces"],
221
+ (next) => {
222
+ if (next.label !== undefined) group.setAttribute("aria-label", next.label);
223
+ if (next.accents !== undefined) choices = next.accents;
224
+ if (next.showLabels !== undefined) labelled = next.showLabels;
225
+ if (next.disabled !== undefined) off = next.disabled;
226
+ if (next.surfaces !== undefined) tinted = next.surfaces;
227
+ if (next.accents || next.showLabels || next.disabled || next.surfaces) rebuild();
228
+ },
229
+ );
230
+
231
+ return group;
232
+ };
233
+
234
+ export default ThemePicker;
@@ -13,7 +13,8 @@ export const TreeView = (props) => {
13
13
  nodes = [],
14
14
  onSelect,
15
15
  onToggle,
16
- /** @deprecated Use onPress instead */
16
+ // The tree's own name for the selection callback. `onPress` is the
17
+ // framework-wide spelling and works here too — it receives the node.
17
18
  onPress: onPressProp,
18
19
  expandedNodes = [],
19
20
  indent = 16,
@@ -37,9 +37,11 @@ export { DroppBox } from "./DroppBox.js";
37
37
  export { DataTable } from "./DataTable.js";
38
38
  export { Carousel } from "./Carousel.js";
39
39
  export { Tooltip } from "./Tooltip.js";
40
+ export { Menu } from "./Menu.js";
40
41
  export { Pagination } from "./Pagination.js";
41
42
  export { TreeView } from "./TreeView.js";
42
43
  export { Stepper } from "./Stepper.js";
44
+ export { ThemePicker } from "./ThemePicker.js";
43
45
  export { Skeleton } from "./Skeleton.js";
44
46
  export { Chart } from "./Chart.js";
45
47
  export { QRCode } from "./QRCode.js";
@@ -1,156 +0,0 @@
1
- # Contributing to FletBox
2
-
3
- Thank you for helping improve FletBox. The project is designed to stay friendly to developers, so contributions should keep the API readable, predictable, and practical.
4
-
5
- ## Project areas
6
-
7
- ```text
8
- src/core/ Runtime, mounting, PWA, and HMR
9
- src/widgets/ UI widgets
10
- src/widget-factory/Widget creation and prop processing
11
- src/navigations/ Scaffold, drawer, tabs, and router
12
- src/services/ Browser storage and HTTP helpers
13
- src/tools/ State and general-purpose tools
14
- src/utils/ Themes, parsing, effects, units, and validation
15
- bin/ CLI and project generators
16
- docs/ User and API documentation
17
- ```
18
-
19
- ## Before changing code
20
-
21
- 1. Read the owning module and its nearest tests or examples.
22
- 2. Check `src/index.js` to understand the public export.
23
- 3. Check `src/index.d.ts` when changing public types.
24
- 4. Search for existing aliases and related widgets.
25
- 5. Keep unrelated refactors out of the change.
26
-
27
- ## Adding a widget
28
-
29
- A widget should normally include:
30
-
31
- - A file under `src/widgets/`.
32
- - An export in `src/widgets/index.js`.
33
- - An export in `src/index.js`.
34
- - A prop interface and function declaration in `src/index.d.ts`.
35
- - Basic, everyday, and full examples.
36
- - A documentation page under `docs/widget/`.
37
- - Accessibility notes when it is interactive.
38
- - Cleanup for timers, listeners, observers, and subscriptions.
39
-
40
- Use the existing `WidgetFactory` patterns instead of creating a second rendering system.
41
-
42
- ## Adding a prop
43
-
44
- When adding a public prop:
45
-
46
- 1. Implement it in the widget.
47
- 2. Add its TypeScript type.
48
- 3. Add its default value and behavior documentation.
49
- 4. Add an example that proves the behavior.
50
- 5. Check whether it should have a friendly alias.
51
- 6. Test both the preferred name and aliases when aliases are intentional.
52
-
53
- FletBox supports developer-friendly aliases, but each alias must have one clear meaning.
54
-
55
- ## Documentation pattern
56
-
57
- Widget pages follow this structure:
58
-
59
- 1. Overview.
60
- 2. Learn it in one minute.
61
- 3. When to use.
62
- 4. Common props.
63
- 5. Full prop list.
64
- 6. How props work.
65
- 7. Example usage.
66
- 8. Basic, everyday, and full examples.
67
- 9. Common layout and styling examples.
68
- 10. Beginner tips.
69
- 11. Common mistakes.
70
- 12. Behavior notes.
71
- 13. Accessibility.
72
- 14. Related widgets.
73
-
74
- Regenerate widget documentation with:
75
-
76
- ```bash
77
- python3 scripts/generate_widget_docs.py
78
- ```
79
-
80
- The generator uses `src/index.d.ts` and the supplied snippet library as references.
81
-
82
- ## Runtime and types must agree
83
-
84
- A public API is not complete until these three surfaces agree:
85
-
86
- ```text
87
- runtime implementation
88
- src/index.js exports
89
- src/index.d.ts declarations
90
- ```
91
-
92
- If a function is declared but not exported, or exported but not typed, do not ship it as stable: fix the source of truth.
93
-
94
- `npm test` enforces this with `scripts/check-api-surface.mjs`. It imports `src/index.js` for real, so it fails when a runtime export has no declaration, when a declared value does not exist at runtime, when an `interface`/`type` is not `export`ed (a consumer could not `import type` it), or when the barrel re-exports a name that no longer exists. It also fails when the package stops being importable in Node, so never touch `window` / `document` at module scope — guard with `typeof window !== "undefined"`.
95
-
96
- ## Cleanup rules
97
-
98
- Components that register resources must release them:
99
-
100
- - `window` and `document` listeners.
101
- - Timers and animation frames.
102
- - `ResizeObserver` and `MutationObserver`.
103
- - Router subscriptions.
104
- - Storage subscriptions.
105
- - WebSocket connections.
106
-
107
- Use the widget cleanup lifecycle when available.
108
-
109
- ## Validation checklist
110
-
111
- Before opening a pull request:
112
-
113
- ```bash
114
- npm test
115
- node --check path/to/changed-file.js
116
- python3 -m py_compile scripts/generate_widget_docs.py
117
- python3 scripts/generate_widget_docs.py
118
- ```
119
-
120
- `npm test` runs the two static checkers described above — the widget-structure contract (`scripts/check-widgets.mjs`) and the API surface coupling (`scripts/check-api-surface.mjs`) — and must pass before a pull request.
121
-
122
- The behavioral tests are dependency-free browser harnesses in `tests/`. Serve the repo with the dev server (`npm run dev` or `node bin/cli.js run-spa`), then open:
123
-
124
- - `/tests/index.html` — widget-factory regression contract
125
- - `/tests/smoke.html` — instantiates every widget
126
- - `/tests/doccheck.html` — chapters 1-2 documented behavior
127
- - `/tests/doccheck-all.html` — chapters 3-7 documented behavior
128
- - `/tests/propscan.html` — custom-prop scanner
129
-
130
- Each harness writes a global (`window.__TESTS`, `window.__SMOKE`, `window.__DOCCHECK`) and must pass with 0 failures.
131
-
132
- Also verify the feature in a real browser when it changes DOM behavior. For build changes, run:
133
-
134
- ```bash
135
- npm run build
136
- ```
137
-
138
- For CLI changes, test:
139
-
140
- ```bash
141
- node bin/cli.js --help
142
- node bin/cli.js --version
143
- ```
144
-
145
- ## Pull request guidance
146
-
147
- A good pull request explains:
148
-
149
- - What user problem it solves.
150
- - Which files changed.
151
- - Which public API changed.
152
- - Whether types and docs were updated.
153
- - Which commands or tests were run.
154
- - Any remaining compatibility or migration concern.
155
-
156
- Avoid committing generated output, secrets, local databases, APKs, logs, or credentials.