flet-box 0.0.0-stage → 1.0.1

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 (179) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +190 -2
  3. package/bin/cli.js +394 -0
  4. package/bin/commands/buildAndroid.js +200 -0
  5. package/bin/commands/buildIOS.js +237 -0
  6. package/bin/commands/create.js +406 -0
  7. package/bin/commands/createBundle.js +375 -0
  8. package/bin/commands/createComponent.js +115 -0
  9. package/bin/commands/createScreen.js +161 -0
  10. package/bin/commands/killServer.js +107 -0
  11. package/bin/commands/package.js +131 -0
  12. package/bin/commands/release.js +189 -0
  13. package/bin/commands/runBundle.js +62 -0
  14. package/bin/commands/runCloudflare.js +89 -0
  15. package/bin/commands/runNative.js +109 -0
  16. package/bin/commands/runServer.js +390 -0
  17. package/bin/ota/loader.js +361 -0
  18. package/bin/package.json +45 -0
  19. package/bin/utils/bundleConfig.js +178 -0
  20. package/bin/utils/capacitor.js +137 -0
  21. package/bin/utils/colors.js +215 -0
  22. package/bin/utils/helpers.js +80 -0
  23. package/bin/utils/spinner.js +96 -0
  24. package/bin/utils/templates.js +1823 -0
  25. package/bin/utils/version.js +108 -0
  26. package/package.json +54 -4
  27. package/src/animations/AnimatedBox.js +607 -0
  28. package/src/animations/AnimatedText.js +105 -0
  29. package/src/animations/MatrixRain.js +220 -0
  30. package/src/animations/ParallaxBox.js +369 -0
  31. package/src/animations/animate.js +332 -0
  32. package/src/animations/index.js +25 -0
  33. package/src/app.js +41 -0
  34. package/src/assets/icon-1024-maskable.png +0 -0
  35. package/src/assets/icon-192-maskable.png +0 -0
  36. package/src/assets/icon-192.png +0 -0
  37. package/src/assets/icon-512-maskable.png +0 -0
  38. package/src/assets/icon-512.png +0 -0
  39. package/src/assets/logo.png +0 -0
  40. package/src/core/App.js +44 -0
  41. package/src/core/hmr-client.js +142 -0
  42. package/src/core/index.js +34 -0
  43. package/src/core/pwa.js +100 -0
  44. package/src/core/runApp.js +430 -0
  45. package/src/create-index.sh +237 -0
  46. package/src/fonts/MaterialIcons-Regular.woff2 +0 -0
  47. package/src/fonts/icons.css +23 -0
  48. package/src/index.d.ts +1930 -0
  49. package/src/index.js +288 -0
  50. package/src/navigations/AdaptiveScaffold.js +149 -0
  51. package/src/navigations/AppBar.js +331 -0
  52. package/src/navigations/BottomNavigation.js +298 -0
  53. package/src/navigations/CollapsibleSideBar.js +139 -0
  54. package/src/navigations/Drawer.js +333 -0
  55. package/src/navigations/DrawerItem.js +260 -0
  56. package/src/navigations/Router.js +475 -0
  57. package/src/navigations/Scaffold.js +537 -0
  58. package/src/navigations/Tabs.js +384 -0
  59. package/src/navigations/index.js +57 -0
  60. package/src/services/RamStore.js +213 -0
  61. package/src/services/Session.js +310 -0
  62. package/src/services/Storage.js +309 -0
  63. package/src/services/http.js +350 -0
  64. package/src/services/index.js +57 -0
  65. package/src/tools/animation.js +42 -0
  66. package/src/tools/array.js +95 -0
  67. package/src/tools/border.js +28 -0
  68. package/src/tools/clipboard.js +104 -0
  69. package/src/tools/color.js +329 -0
  70. package/src/tools/createList.js +215 -0
  71. package/src/tools/delay.js +55 -0
  72. package/src/tools/device.js +138 -0
  73. package/src/tools/dict.js +297 -0
  74. package/src/tools/dimensions.js +118 -0
  75. package/src/tools/filter.js +41 -0
  76. package/src/tools/flex.js +26 -0
  77. package/src/tools/gradient.js +34 -0
  78. package/src/tools/grid.js +57 -0
  79. package/src/tools/index.js +38 -0
  80. package/src/tools/mapList.js +107 -0
  81. package/src/tools/margin.js +93 -0
  82. package/src/tools/memo.js +60 -0
  83. package/src/tools/os.js +104 -0
  84. package/src/tools/padding.js +93 -0
  85. package/src/tools/print.js +69 -0
  86. package/src/tools/random.js +299 -0
  87. package/src/tools/ref.js +70 -0
  88. package/src/tools/rgba.js +4 -0
  89. package/src/tools/shadow.js +23 -0
  90. package/src/tools/string.js +85 -0
  91. package/src/tools/time.js +674 -0
  92. package/src/tools/transform.js +68 -0
  93. package/src/tools/transition.js +48 -0
  94. package/src/tools/useState.js +268 -0
  95. package/src/tools/uuid.js +42 -0
  96. package/src/utils/TextInputValidator.js +256 -0
  97. package/src/utils/composeUpdate.js +65 -0
  98. package/src/utils/getWidgetProps.js +35 -0
  99. package/src/utils/index.js +76 -0
  100. package/src/utils/markdownParser.js +368 -0
  101. package/src/utils/mediaTime.js +79 -0
  102. package/src/utils/navigation.js +263 -0
  103. package/src/utils/scriptInjector.js +73 -0
  104. package/src/utils/stackPosition.js +31 -0
  105. package/src/utils/stopWebRefresh.js +77 -0
  106. package/src/utils/styleInjector.js +38 -0
  107. package/src/utils/syntaxHighlight.js +339 -0
  108. package/src/utils/themes.js +347 -0
  109. package/src/utils/units.js +87 -0
  110. package/src/utils/visualEffects.js +169 -0
  111. package/src/widget-factory/addChildren.js +114 -0
  112. package/src/widget-factory/assignProps.js +256 -0
  113. package/src/widget-factory/createWidget.js +28 -0
  114. package/src/widget-factory/effects.js +132 -0
  115. package/src/widget-factory/index.js +7 -0
  116. package/src/widget-factory/lifecycle.js +210 -0
  117. package/src/widget-factory/makeParentable.js +89 -0
  118. package/src/widget-factory/pixTool.js +40 -0
  119. package/src/widget-factory/processProps.js +122 -0
  120. package/src/widget-factory/reactivity.js +98 -0
  121. package/src/widget-factory/remTool.js +34 -0
  122. package/src/widget-factory/stackPosition.js +87 -0
  123. package/src/widget-factory/stringTool.js +26 -0
  124. package/src/widget-factory/tools.js +249 -0
  125. package/src/widget-factory/translateProps.js +1049 -0
  126. package/src/widget-factory/warnings.js +284 -0
  127. package/src/widget-factory/widgetFactory.js +200 -0
  128. package/src/widgets/Accordion.js +562 -0
  129. package/src/widgets/AdMob.js +315 -0
  130. package/src/widgets/AdSense.js +268 -0
  131. package/src/widgets/AlertDialog.js +162 -0
  132. package/src/widgets/Audio.js +311 -0
  133. package/src/widgets/Avatar.js +210 -0
  134. package/src/widgets/Badge.js +189 -0
  135. package/src/widgets/BottomSheet.js +287 -0
  136. package/src/widgets/Button.js +247 -0
  137. package/src/widgets/Card.js +33 -0
  138. package/src/widgets/Carousel.js +301 -0
  139. package/src/widgets/Chart.js +632 -0
  140. package/src/widgets/Checkbox.js +106 -0
  141. package/src/widgets/Chip.js +153 -0
  142. package/src/widgets/CircularBar.js +352 -0
  143. package/src/widgets/CircularChart.js +549 -0
  144. package/src/widgets/CodeViewer.js +236 -0
  145. package/src/widgets/Column.js +29 -0
  146. package/src/widgets/Container.js +31 -0
  147. package/src/widgets/DataTable.js +257 -0
  148. package/src/widgets/Divider.js +93 -0
  149. package/src/widgets/DraggBox.js +115 -0
  150. package/src/widgets/Dropdown.js +505 -0
  151. package/src/widgets/DroppBox.js +180 -0
  152. package/src/widgets/FloatingActionButton.js +91 -0
  153. package/src/widgets/GridView.js +20 -0
  154. package/src/widgets/Icon.js +33 -0
  155. package/src/widgets/Image.js +15 -0
  156. package/src/widgets/Input.js +565 -0
  157. package/src/widgets/Inspector.js +254 -0
  158. package/src/widgets/InstallButton.js +110 -0
  159. package/src/widgets/ListTile.js +224 -0
  160. package/src/widgets/ListView.js +503 -0
  161. package/src/widgets/Markdown.js +1236 -0
  162. package/src/widgets/Modal.js +496 -0
  163. package/src/widgets/Pagination.js +195 -0
  164. package/src/widgets/ProgressBar.js +178 -0
  165. package/src/widgets/QRCode.js +467 -0
  166. package/src/widgets/Radio.js +132 -0
  167. package/src/widgets/Rating.js +261 -0
  168. package/src/widgets/Row.js +29 -0
  169. package/src/widgets/Skeleton.js +243 -0
  170. package/src/widgets/Slider.js +527 -0
  171. package/src/widgets/SnackBar.js +227 -0
  172. package/src/widgets/Stack.js +29 -0
  173. package/src/widgets/Stepper.js +296 -0
  174. package/src/widgets/Switch.js +102 -0
  175. package/src/widgets/Text.js +98 -0
  176. package/src/widgets/Tooltip.js +267 -0
  177. package/src/widgets/TreeView.js +370 -0
  178. package/src/widgets/Video.js +92 -0
  179. package/src/widgets/index.js +52 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Maenys Javier Quesada Reyes
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,191 @@
1
- # Temporary Holding Version
1
+ <div align="center">
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ <img src="src/assets/logo.png" alt="FletBox" width="110" />
4
+
5
+ <h1>FletBox</h1>
6
+
7
+ <b>Ship web, PWA, Android &amp; iOS from one codebase — with zero runtime dependencies.</b><br>
8
+ A declarative, Flutter/Flet-like UI framework in plain vanilla JavaScript.<br>
9
+ Real DOM · No Virtual DOM · No build config to learn.
10
+
11
+ <br>
12
+
13
+ <a href="https://github.com/sponsors/kuko53348"><img src="https://img.shields.io/badge/%E2%9D%A4%20Sponsor-FletBox-ff69b4?style=for-the-badge&logo=githubsponsors" alt="Sponsor FletBox"></a>
14
+ <a href="https://www.youtube.com/@flet-box"><img src="https://img.shields.io/badge/YouTube-@flet--box-red?style=for-the-badge&logo=youtube" alt="YouTube @flet-box"></a>
15
+
16
+ <br><br>
17
+
18
+ <a href="https://github.com/kuko53348/flet-box/blob/HEAD/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square" alt="License: MIT"></a>
19
+ <img src="https://img.shields.io/badge/dependencies-0-brightgreen?style=flat-square" alt="Zero dependencies">
20
+ <img src="https://img.shields.io/badge/platforms-web%20%7C%20PWA%20%7C%20android%20%7C%20ios-007acc?style=flat-square" alt="Platforms">
21
+ <a href="https://github.com/kuko53348/flet-box"><img src="https://img.shields.io/github/stars/kuko53348/flet-box?style=social" alt="GitHub stars"></a>
22
+
23
+ <br><br>
24
+
25
+ <a href="https://kuko53348.github.io/flet-box-docs/">📖 Docs</a> •
26
+ <a href="#quick-start">Quick start</a> •
27
+ <a href="https://www.youtube.com/@flet-box">▶ YouTube</a> •
28
+ <a href="#support--sponsorship">❤️ Sponsor</a>
29
+
30
+ </div>
31
+
32
+ ---
33
+
34
+ **Stop paying the framework tax.** FletBox has no runtime dependencies, no Virtual DOM, and nothing to install beyond Node. Write widgets in the syntax you already know — plain JavaScript — and deploy to browser, PWA, Android, and iOS from the same source.
35
+
36
+ > ▶ **Learn it on YouTube:** [@flet-box](https://www.youtube.com/@flet-box) — tutorials, walkthroughs, and new widgets.
37
+
38
+ ## Installation
39
+
40
+ FletBox isn't on the npm registry, so clone it and link the CLI locally:
41
+
42
+ ```bash
43
+ brew install node # macOS/Linux: ensure Node >= 18
44
+
45
+ git clone https://github.com/kuko53348/flet-box.git
46
+ cd flet-box
47
+ npm link # link the flet-box CLI globally
48
+
49
+ flet-box create appName # scaffold your first app
50
+ cd appName
51
+ npm link flet-box # link FletBox into the new project
52
+ flet-box run-spa # run as SPA with hot reload
53
+ ```
54
+
55
+ ## Quick start
56
+
57
+ ```javascript
58
+ import { runApp, Container, Text, Button, useState } from "flet-box";
59
+
60
+ const App = () => {
61
+ const [count, setCount] = useState("count", 0);
62
+
63
+ return Container({
64
+ width: "100%",
65
+ minHeight: "100vh",
66
+ display: "flex",
67
+ justifyContent: "center",
68
+ alignItems: "center",
69
+ child: [
70
+ Text({ text: `Counter: ${count}`, fontSize: 24, fontWeight: 700 }),
71
+ Button({ text: "Increment", onClick: () => setCount((v) => v + 1) }),
72
+ ],
73
+ });
74
+ };
75
+
76
+ runApp(App);
77
+ ```
78
+
79
+ ## Features
80
+
81
+ - Declarative, widget-based UI · reactive state with `useState` · router for SPAs
82
+ - Zero runtime dependencies; ESM only, Node >= 18 · real DOM, no Virtual DOM
83
+ - Local storage, HTTP services, theme, PWA, and production builds
84
+ - Android & iOS from the CLI (APK, simulator `.app`, `.xcarchive`)
85
+ - Utility API for layout, text, color, animation, and more
86
+
87
+ ## Commands
88
+
89
+ ```bash
90
+ npm run dev # dev server on port 8000
91
+ npm run build # production bundle with esbuild
92
+
93
+ # inside a generated project
94
+ flet-box createBundle www # bundle the app into www/
95
+ flet-box build android # Android debug APK
96
+ flet-box build ios # iOS Simulator app (macOS + Xcode)
97
+ flet-box build ios --archive # unsigned iOS archive, sign in Xcode
98
+ ```
99
+
100
+ ## Android and iOS
101
+
102
+ `flet-box create` ships the Capacitor config, so the same SPA builds for both platforms. FletBox isn't on the npm registry — link it before installing:
103
+
104
+ ```bash
105
+ flet-box create my-app && cd my-app
106
+ npm link flet-box # or: npm install ../path/to/flet-box
107
+ npm install # Capacitor CLI, sharp, platform package
108
+ ```
109
+
110
+ - **Android** — Node >= 20.9, a JDK, and the Android SDK. `flet-box build android` → debug APK; `flet-box build android --release [--aab]` → unsigned release artifact to sign yourself (`apksigner` / Play App Signing).
111
+ - **iOS** — macOS, the **full** Xcode app (not Command Line Tools), a simulator runtime, and CocoaPods. `flet-box build ios` → simulator; `flet-box build ios --archive` → sign in Xcode.
112
+ - Drop a 1024×1024 icon at `assets/logo.png` and rebuild — launcher icons, launch images, and the launch screen regenerate automatically.
113
+ - Capacitor serves from `https://localhost`, so a remote API must allow that origin in CORS.
114
+
115
+ Full guide, including manual `npx cap` workflows: [Mobile and platforms](docs/guides/mobile-and-platforms.md).
116
+
117
+ ## Documentation
118
+
119
+ Read the docs online: **[The FletBox Book — kuko53348.github.io/flet-box-docs](https://kuko53348.github.io/flet-box-docs/)**.
120
+
121
+ They are **one continuous book** — the cover maps every page: [The FletBox Book source](docs/README.md).
122
+
123
+ - [Your first app](docs/guides/first-app.md) — build a working todo app in 20 minutes
124
+ - [The widget book](docs/widget/README.md) — every widget with property tables and examples
125
+ - [State, Router & Services](docs/guides/state.md) — shared state, routing, storage, and HTTP
126
+ - [Tools & Utilities](docs/tools/README.md) — every helper function with signatures
127
+ - [The CLI](docs/cli/README.md) — create, run, and build projects
128
+ - [FletBox Server](docs/server/README.md) — backend toolkit: API, auth, security, data services
129
+ - [SQLite and the API server](docs/server/data-services.md) — where your data lives + a full SQLite API example
130
+ - [Mobile and platforms](docs/guides/mobile-and-platforms.md) — Android, iOS, PWA, and desktop
131
+ - [Architecture](docs/arquitectura.md) — the widget contract and the single rendering engine
132
+ - [Contributing](docs/CONTRIBUTING.md) — how to add code and docs
133
+
134
+ ## Support & sponsorship
135
+
136
+ <div align="center">
137
+
138
+ <a href="https://github.com/sponsors/kuko53348">
139
+ <img src="https://img.shields.io/badge/%E2%9D%A4%20Become_a_Sponsor-Support_FletBox-ff69b4?style=for-the-badge&logo=githubsponsors" alt="Become a Sponsor">
140
+ </a>
141
+
142
+ <br><br>
143
+
144
+ **FletBox is MIT-licensed and free forever — for individuals and companies alike.**
145
+
146
+ Zero dependencies. One developer. No corporate budget. Sponsoring is what keeps
147
+ it independent — and what turns "nights and weekends" into a framework you can
148
+ build a business on.
149
+
150
+ ### ► [Sponsor FletBox on GitHub](https://github.com/sponsors/kuko53348) ◄
151
+
152
+ </div>
153
+
154
+ | 💎 Sponsor tier | What you get |
155
+ |---|---|
156
+ | ☕ **$3 / mo** — Coffee | My thanks + the good karma of keeping open source free |
157
+ | 🚀 **$10 / mo** — Supporter | Shout-out in the Sponsors wall + priority on issue replies |
158
+ | 🏢 **$50 / mo** — Backer | Your name/logo in this README & docs + feature-request voting |
159
+ | 🤝 **Custom** — Partner | Logo + link, priority support, and a say in the roadmap — [email me](mailto:kuko53348@gmail.com) |
160
+
161
+ Your sponsorship funds **new widgets, the full docs book, test coverage, the mobile toolchain, and the time to ship it all.** One-time contributions welcome on the [sponsorship page](https://github.com/sponsors/kuko53348).
162
+
163
+ ### Other ways to support
164
+
165
+ - 💳 **Crypto** (Polygon / MATIC-POL): `0x6d437bB66af8d2c44670eA18F059BE1417Dcd7bA`
166
+ - 💼 **Commercial support, consulting, or priority help:** [kuko53348@gmail.com](mailto:kuko53348@gmail.com)
167
+ - ▶ **Subscribe on YouTube:** [@flet-box](https://www.youtube.com/@flet-box)
168
+ - ⭐ **Star the repo** and tell a colleague — visibility is free and it helps enormously
169
+
170
+ <sub>Living in Cuba makes platforms like Mastercard hard to access, so crypto support is especially valuable. Every contribution — a sponsor tier, a donation, a star, or spreading the word — keeps FletBox growing. Thank you. 🙏</sub>
171
+
172
+ ### ❤️ Our sponsors
173
+
174
+ <!-- Add sponsor logos/links here as they come in. Example:
175
+ <a href="https://github.com/their-username"><img src="https://github.com/their-username.png" width="60" height="60" alt="@their-username"></a>
176
+ -->
177
+
178
+ <div align="center">
179
+
180
+ _This space is empty — **be the first!**_
181
+
182
+ <a href="https://github.com/sponsors/kuko53348">
183
+ <img src="https://img.shields.io/badge/Your_name_here-Sponsor_FletBox-ff69b4?style=for-the-badge" alt="Your name here">
184
+ </a>
185
+
186
+ </div>
187
+
188
+ ## License
189
+
190
+ MIT — free to use, modify, redistribute, and build commercial products with.
191
+ See [LICENSE](LICENSE). Copyright (c) 2026 Maenys Javier Quesada Reyes.
package/bin/cli.js ADDED
@@ -0,0 +1,394 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @file bin/cli.js
5
+ * @description FletBox CLI entry point with hot reload support.
6
+ *
7
+ * Parses process.argv, sets up the global Ctrl+C handler, and dispatches
8
+ * to the appropriate command handler. All commands are lazy-imported so
9
+ * startup stays fast even on slow disks.
10
+ */
11
+
12
+ import { createRequire } from "module";
13
+ import fs from "fs";
14
+ import path from "path";
15
+ import { createProject } from "./commands/create.js";
16
+ import { packageManager } from "./commands/package.js";
17
+ import { createBundle } from "./commands/createBundle.js";
18
+ import { buildAndroid } from "./commands/buildAndroid.js";
19
+ import { buildIOS } from "./commands/buildIOS.js";
20
+ import { runNative } from "./commands/runNative.js";
21
+ import { runDevServer } from "./commands/runServer.js";
22
+ import { createScreen } from "./commands/createScreen.js";
23
+ import { createComponent } from "./commands/createComponent.js";
24
+ import { killServer } from "./commands/killServer.js";
25
+ import { runBundle } from "./commands/runBundle.js";
26
+ import { runCloudflareDemo } from "./commands/runCloudflare.js";
27
+ import { release } from "./commands/release.js";
28
+ import { c, gradient, section, banner, divider, width, setupCtrlC } from "./utils/colors.js";
29
+
30
+ const require = createRequire(import.meta.url);
31
+ const { version } = require("../package.json");
32
+
33
+ // Register the global Ctrl+C handler early so every command benefits from it.
34
+ setupCtrlC();
35
+
36
+ const args = process.argv.slice(2);
37
+ const command = args[0];
38
+ const projectName = args[1];
39
+
40
+ // Clear the terminal only when attached to an interactive session to avoid
41
+ // polluting piped output (e.g. CI logs).
42
+ if (process.stdout.isTTY) {
43
+ console.clear();
44
+ }
45
+
46
+ /**
47
+ * Prints the full help message, including all available commands and options.
48
+ * Rendered with gradient colors when the terminal supports truecolor.
49
+ *
50
+ * @returns {void}
51
+ */
52
+ const showHelp = () => {
53
+ const cmd = (name, desc, alias = "") =>
54
+ ` ${c("bold", gradient(name, "#22d3ee", "#6366f1"))}${" ".repeat(Math.max(1, 22 - width(name)))}${c("gray", desc)}${alias ? c("dim", " (" + alias + ")") : ""}`;
55
+
56
+ const opt = (name, desc) =>
57
+ ` ${c("brightCyan", name)}${" ".repeat(Math.max(1, 24 - name.length))}${c("gray", desc)}`;
58
+
59
+ console.log(`
60
+ ${banner("🚀 FletBox CLI", `v${version}`)}
61
+
62
+ ${section("🧭", "COMMANDS")}
63
+ ${cmd("create <name>", "Create a new project")}
64
+ ${cmd("create --adaptive", "responsive (mobile + desktop)")}
65
+ ${cmd("create --blank", "empty project")}
66
+ ${cmd("create --full", "AppBar+Drawer+BottomNav")}
67
+ ${cmd("create --sidebar", "desktop (Sidebar+AppBar)")}
68
+ ${cmd("create --repo <user/repo>", "set GitHub repo for OTA updates")}
69
+ ${cmd("screen <name|num>", "Create screen(s) (1-10)")}
70
+ ${cmd("component <name|num>", "Create component(s) (1-10)")}
71
+ ${cmd("createBundle [dir]", "Unified production bundle")}
72
+ ${cmd(" └─ [dir]", "target folder (default www)")}
73
+ ${cmd("release [--patch]", "Bump version, rebuild, tag (OTA release)")}
74
+ ${cmd(" └─ --minor|--major", "bump that position instead of patch")}
75
+ ${cmd(" └─ --push", "push the commit + tag to origin")}
76
+ ${cmd("build android", "Build Android debug APK")}
77
+ ${cmd("build android --release", "Build unsigned release APK/AAB to sign yourself")}
78
+ ${cmd("build ios", "Build iOS Simulator app (macOS + Xcode)")}
79
+ ${cmd("build ios --archive", "Build unsigned device archive (.xcarchive)")}
80
+ ${cmd("run-native <android|ios>", "Compile & launch on emulator/device")}
81
+ ${cmd(" └─ --device", "use a physical device instead (alias: rn)")}
82
+ ${cmd(" └─ --livereload", "hot reload on the device")}
83
+ ${cmd("run", "Static file server")}
84
+ ${cmd("run --hot", "Dev server with hot reload")}
85
+ ${cmd("run-spa", "SPA + hot reload")}
86
+ ${cmd("cloudflare", "Serve demo on a public Cloudflare HTTPS tunnel")}
87
+ ${cmd(" └─ alias", "cf | demo | tunnel")}
88
+ ${cmd("run-bundle [dir]", "Build & serve the bundle (single app.js)")}
89
+ ${cmd("pkg", "Open the package manager")}
90
+ ${cmd("kill-server [port]", "Kill servers on a port")}
91
+ ${cmd("--version | --help", "Version and this help")}
92
+
93
+ ${section("⚙️", "OPTIONS")}
94
+ ${opt("--port <n>, -p <n>", "Port (default 8000) — asked if not provided")}
95
+ ${opt("--hot", "Hot reload with run")}
96
+ ${opt("--quiet", "No request logging")}
97
+ ${opt("--dry-run | --list", "kill-server: just list, do not kill")}
98
+
99
+ ${section("✨", "EXAMPLES")}
100
+ ${c("gray", "flet-box create my-app --adaptive")}
101
+ ${c("gray", "flet-box build android # create a debug APK")}
102
+ ${c("gray", "flet-box build android --release --aab # unsigned .aab for Play")}
103
+ ${c("gray", "flet-box build ios # create an iOS Simulator app")}
104
+ ${c("gray", "flet-box build ios --archive # create an .xcarchive to sign in Xcode")}
105
+ ${c("gray", "flet-box run-native android # build & launch on an emulator")}
106
+ ${c("gray", "flet-box run-native ios --device --livereload # hot reload on iPhone")}
107
+ ${c("gray", "cd my-app && flet-box run-spa")}
108
+ ${c("gray", "flet-box run-bundle # build & preview production")}
109
+ ${c("gray", "flet-box cloudflare # public HTTPS demo link in console")}
110
+ ${c("gray", "flet-box kill-server 8000 # kill the parasite server")}
111
+
112
+ ${divider("", "#3730a3")}
113
+ ${c("dim", " FletBox — ultra-light vanilla-JS UI framework")}
114
+ `);
115
+ };
116
+
117
+ /**
118
+ * Parses the CLI flags from `process.argv`.
119
+ *
120
+ * Recognised flags:
121
+ * - `--port <n>` / `-p <n>` — override the default port (8000).
122
+ * - `--quiet` — suppress per-request HTTP logging.
123
+ * - `--hot` / `--hot-reload`— enable hot-module-replacement.
124
+ * - `--dry-run` / `--list` / `--check` — list-only mode for `kill-server`.
125
+ *
126
+ * Invalid port values produce a warning and fall back to the default.
127
+ *
128
+ * @returns {{ port: number, portExplicit: boolean, logRequests: boolean, hot: boolean, dryRun: boolean }}
129
+ */
130
+ const parseFlags = () => {
131
+ const flags = {
132
+ port: 8000,
133
+ portExplicit: false,
134
+ logRequests: true,
135
+ hot: false,
136
+ dryRun: false,
137
+ };
138
+
139
+ for (let i = 0; i < args.length; i++) {
140
+ const arg = args[i];
141
+
142
+ if ((arg === "--port" || arg === "-p") && args[i + 1]) {
143
+ const p = Number(args[i + 1]);
144
+ if (Number.isInteger(p) && p > 0 && p < 65536) {
145
+ flags.port = p;
146
+ flags.portExplicit = true;
147
+ } else {
148
+ console.warn(
149
+ c("yellow", `⚠️ Invalid port "${args[i + 1]}", using ${flags.port}`),
150
+ );
151
+ }
152
+ i++;
153
+ } else if (arg === "--quiet") {
154
+ flags.logRequests = false;
155
+ } else if (arg === "--hot" || arg === "--hot-reload") {
156
+ flags.hot = true;
157
+ } else if (arg === "--dry-run" || arg === "--list" || arg === "--check") {
158
+ flags.dryRun = true;
159
+ }
160
+ }
161
+
162
+ return flags;
163
+ };
164
+
165
+ /**
166
+ * Guards commands that must run inside a FletBox project directory.
167
+ * Exits with a descriptive error when `src/app.js` is not found in the
168
+ * current working directory — this prevents confusing failures later.
169
+ *
170
+ * @returns {void}
171
+ */
172
+ const ensureProject = () => {
173
+ const inProject = fs.existsSync(path.join(process.cwd(), "src", "app.js"));
174
+ if (!inProject) {
175
+ console.error(c("red", "❌ Not inside a FletBox project"));
176
+ console.log(
177
+ c(
178
+ "gray",
179
+ "Run this inside a project folder with src/app.js (create one with: flet-box create <name>)",
180
+ ),
181
+ );
182
+ process.exit(1);
183
+ }
184
+ };
185
+
186
+ /**
187
+ * Main async entry point. Parses flags, then delegates to the matching
188
+ * command handler via a `switch` on the first positional argument.
189
+ *
190
+ * Unrecognised commands print the help message and exit with code 1.
191
+ *
192
+ * @async
193
+ * @returns {Promise<void>}
194
+ */
195
+ async function main() {
196
+ try {
197
+ const flags = parseFlags();
198
+
199
+ switch (command) {
200
+ case "create":
201
+ case "new":
202
+ if (!projectName) {
203
+ console.error(c("red", "❌ Error: Project name required"));
204
+ console.log(
205
+ c(
206
+ "gray",
207
+ "Usage: flet-box create <project-name> [--adaptive|--blank|--full|--sidebar|--repo user/repo]",
208
+ ),
209
+ );
210
+ process.exit(1);
211
+ }
212
+ if (!/^[a-z0-9-]+$/i.test(projectName)) {
213
+ console.error(
214
+ c(
215
+ "red",
216
+ "❌ Error: Invalid project name. Use only letters, numbers, and hyphens.",
217
+ ),
218
+ );
219
+ process.exit(1);
220
+ }
221
+ // Forward all remaining args (template flags like --adaptive) to createProject.
222
+ await createProject(projectName, args.slice(2));
223
+ break;
224
+
225
+ case "screen":
226
+ case "screens": {
227
+ ensureProject();
228
+ const screenInput = args[1];
229
+ if (!screenInput) {
230
+ console.error(c("red", "❌ Error: Screen name or number required"));
231
+ console.log(c("gray", "Usage: flet-box screen <name>"));
232
+ console.log(c("gray", " flet-box screen <number> (1-10)"));
233
+ process.exit(1);
234
+ }
235
+ await createScreen(screenInput);
236
+ break;
237
+ }
238
+
239
+ case "component":
240
+ case "comp": {
241
+ ensureProject();
242
+ const componentInput = args[1];
243
+ if (!componentInput) {
244
+ console.error(
245
+ c("red", "❌ Error: Component name or number required"),
246
+ );
247
+ console.log(c("gray", "Usage: flet-box component <name>"));
248
+ console.log(c("gray", " flet-box component <number> (1-10)"));
249
+ process.exit(1);
250
+ }
251
+ await createComponent(componentInput);
252
+ break;
253
+ }
254
+
255
+ case "createBundle":
256
+ case "bundle":
257
+ await createBundle(args[1]);
258
+ break;
259
+
260
+ case "release":
261
+ ensureProject();
262
+ await release(args.slice(1));
263
+ break;
264
+
265
+ case "build":
266
+ if (args[1] === "android") {
267
+ buildAndroid(args.slice(2));
268
+ } else if (args[1] === "ios") {
269
+ buildIOS(args.slice(2));
270
+ } else {
271
+ await createBundle(args[1]);
272
+ }
273
+ break;
274
+
275
+ case "run-native":
276
+ case "runNative":
277
+ case "rn":
278
+ runNative(args.slice(1));
279
+ break;
280
+
281
+ case "pkg":
282
+ case "package":
283
+ case "manager":
284
+ await packageManager();
285
+ break;
286
+
287
+ case "kill-server":
288
+ case "kill":
289
+ case "killServer":
290
+ case "killsrv":
291
+ await killServer(
292
+ args[1] && /^\d+$/.test(args[1]) ? args[1] : flags.port,
293
+ { dryRun: flags.dryRun },
294
+ );
295
+ break;
296
+
297
+ case "run":
298
+ case "dev":
299
+ case "serve":
300
+ ensureProject();
301
+ if (args[1] === "android" || args[1] === "ios") {
302
+ console.log(
303
+ c(
304
+ "dim",
305
+ `ℹ️ \`flet-box run ${args[1]}\` launches the native app — see \`flet-box run-native ${args[1]}\`.`,
306
+ ),
307
+ );
308
+ runNative(args.slice(1));
309
+ break;
310
+ }
311
+ await runDevServer({
312
+ spaMode: false,
313
+ hotReload: flags.hot,
314
+ port: flags.port,
315
+ askPort: !flags.portExplicit,
316
+ logRequests: flags.logRequests,
317
+ });
318
+ break;
319
+
320
+ case "run-bundle":
321
+ case "runBundle":
322
+ case "preview": {
323
+ ensureProject();
324
+ // Accept an explicit target directory as the second argument, but reject
325
+ // anything that looks like a flag (starts with "-") to avoid misparses.
326
+ const targetDir =
327
+ args[1] &&
328
+ /^[a-zA-Z0-9_][a-zA-Z0-9_-]*$/.test(args[1]) &&
329
+ !args[1].startsWith("-")
330
+ ? args[1]
331
+ : "www";
332
+ await runBundle({
333
+ target: targetDir,
334
+ port: flags.port,
335
+ askPort: !flags.portExplicit,
336
+ logRequests: flags.logRequests,
337
+ });
338
+ break;
339
+ }
340
+
341
+ case "run-spa":
342
+ case "runSpa":
343
+ case "spa":
344
+ ensureProject();
345
+ await runDevServer({
346
+ spaMode: true,
347
+ hotReload: true,
348
+ port: flags.port,
349
+ askPort: !flags.portExplicit,
350
+ logRequests: flags.logRequests,
351
+ });
352
+ break;
353
+
354
+ case "cloudflare":
355
+ case "cf":
356
+ case "demo":
357
+ case "tunnel":
358
+ ensureProject();
359
+ await runCloudflareDemo({
360
+ port: flags.port,
361
+ logRequests: flags.logRequests,
362
+ });
363
+ break;
364
+
365
+ case "--version":
366
+ case "-v":
367
+ console.log(`flet-box v${version}`);
368
+ break;
369
+
370
+ case "--help":
371
+ case "-h":
372
+ case "help":
373
+ showHelp();
374
+ break;
375
+
376
+ default:
377
+ if (command) {
378
+ console.error(c("red", `❌ Unknown command: ${command}`));
379
+ console.log("");
380
+ showHelp();
381
+ } else {
382
+ showHelp();
383
+ }
384
+ process.exit(1);
385
+ }
386
+ } catch (error) {
387
+ console.error(c("red", "❌ CLI Error:"), error.message);
388
+ // Show the full stack trace only when DEBUG is set, to keep normal output clean.
389
+ if (process.env.DEBUG) console.error(error);
390
+ process.exit(1);
391
+ }
392
+ }
393
+
394
+ main();