bootstack 0.1.0__py3-none-any.whl

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 (471) hide show
  1. bootstack/__init__.py +157 -0
  2. bootstack/__main__.py +5 -0
  3. bootstack/_core/__init__.py +21 -0
  4. bootstack/_core/capabilities/__init__.py +45 -0
  5. bootstack/_core/capabilities/after.py +103 -0
  6. bootstack/_core/capabilities/bind.py +192 -0
  7. bootstack/_core/capabilities/bindtags.py +112 -0
  8. bootstack/_core/capabilities/busy.py +72 -0
  9. bootstack/_core/capabilities/clipboard.py +89 -0
  10. bootstack/_core/capabilities/focus.py +118 -0
  11. bootstack/_core/capabilities/grab.py +65 -0
  12. bootstack/_core/capabilities/grid.py +211 -0
  13. bootstack/_core/capabilities/localization.py +231 -0
  14. bootstack/_core/capabilities/pack.py +140 -0
  15. bootstack/_core/capabilities/place.py +113 -0
  16. bootstack/_core/capabilities/selection.py +136 -0
  17. bootstack/_core/capabilities/signals.py +244 -0
  18. bootstack/_core/capabilities/winfo.py +315 -0
  19. bootstack/_core/colorutils.py +234 -0
  20. bootstack/_core/exceptions.py +25 -0
  21. bootstack/_core/images.py +463 -0
  22. bootstack/_core/mixins/__init__.py +1 -0
  23. bootstack/_core/mixins/ttk_state.py +35 -0
  24. bootstack/_core/mixins/widget.py +132 -0
  25. bootstack/_core/paths.py +49 -0
  26. bootstack/_core/publisher.py +149 -0
  27. bootstack/_core/variables.py +62 -0
  28. bootstack/_runtime/__init__.py +3 -0
  29. bootstack/_runtime/app.py +930 -0
  30. bootstack/_runtime/base_window.py +945 -0
  31. bootstack/_runtime/events.py +399 -0
  32. bootstack/_runtime/shortcuts.py +496 -0
  33. bootstack/_runtime/tk_patch.py +43 -0
  34. bootstack/_runtime/toplevel.py +276 -0
  35. bootstack/_runtime/utility.py +457 -0
  36. bootstack/_runtime/visual_focus.py +240 -0
  37. bootstack/_runtime/window_utilities.py +1123 -0
  38. bootstack/assets/__init__.py +21 -0
  39. bootstack/assets/bootstack.ico +0 -0
  40. bootstack/assets/bootstack.png +0 -0
  41. bootstack/assets/elements/__init__.py +0 -0
  42. bootstack/assets/elements/badge-pill.png +0 -0
  43. bootstack/assets/elements/badge-square.png +0 -0
  44. bootstack/assets/elements/button-compact.png +0 -0
  45. bootstack/assets/elements/button-default.png +0 -0
  46. bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
  47. bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
  48. bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
  49. bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
  50. bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
  51. bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
  52. bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
  53. bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
  54. bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
  55. bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
  56. bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
  57. bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
  58. bootstack/assets/elements/card.png +0 -0
  59. bootstack/assets/elements/checkbox-checked.png +0 -0
  60. bootstack/assets/elements/checkbox-indeterminate.png +0 -0
  61. bootstack/assets/elements/checkbox-unchecked.png +0 -0
  62. bootstack/assets/elements/field.png +0 -0
  63. bootstack/assets/elements/input-addon-compact.png +0 -0
  64. bootstack/assets/elements/input-addon-default.png +0 -0
  65. bootstack/assets/elements/input-compact.png +0 -0
  66. bootstack/assets/elements/input-default.png +0 -0
  67. bootstack/assets/elements/list-item-separated.png +0 -0
  68. bootstack/assets/elements/list-item.png +0 -0
  69. bootstack/assets/elements/listrow-compact.png +0 -0
  70. bootstack/assets/elements/listrow-default.png +0 -0
  71. bootstack/assets/elements/manifest.toml +361 -0
  72. bootstack/assets/elements/menu-item.png +0 -0
  73. bootstack/assets/elements/navitem-compact.png +0 -0
  74. bootstack/assets/elements/navitem-default.png +0 -0
  75. bootstack/assets/elements/progressbar-h-compact.png +0 -0
  76. bootstack/assets/elements/progressbar-h-default.png +0 -0
  77. bootstack/assets/elements/progressbar-v-compact.png +0 -0
  78. bootstack/assets/elements/progressbar-v-default.png +0 -0
  79. bootstack/assets/elements/radiobutton.png +0 -0
  80. bootstack/assets/elements/scrollbar-horizontal.png +0 -0
  81. bootstack/assets/elements/scrollbar-vertical.png +0 -0
  82. bootstack/assets/elements/slider-handle.png +0 -0
  83. bootstack/assets/elements/slider-track-h.png +0 -0
  84. bootstack/assets/elements/slider-track-v.png +0 -0
  85. bootstack/assets/elements/switch-off.png +0 -0
  86. bootstack/assets/elements/switch-on.png +0 -0
  87. bootstack/assets/elements/tab-h.png +0 -0
  88. bootstack/assets/elements/tab-v.png +0 -0
  89. bootstack/assets/icons/bootstrap.ttf +0 -0
  90. bootstack/assets/icons/glyphmap.json +2080 -0
  91. bootstack/assets/icons/icon_metrics.json +12470 -0
  92. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
  93. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +856 -0
  94. bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
  95. bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +878 -0
  96. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
  97. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +856 -0
  98. bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
  99. bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +856 -0
  100. bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
  101. bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +856 -0
  102. bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
  103. bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +878 -0
  104. bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
  105. bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +856 -0
  106. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
  107. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +856 -0
  108. bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
  109. bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +854 -0
  110. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
  111. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +845 -0
  112. bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
  113. bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +844 -0
  114. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
  115. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +917 -0
  116. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
  117. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +845 -0
  118. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
  119. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +844 -0
  120. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
  121. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +844 -0
  122. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
  123. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +845 -0
  124. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
  125. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +845 -0
  126. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
  127. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +845 -0
  128. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
  129. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +845 -0
  130. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
  131. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +845 -0
  132. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
  133. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +845 -0
  134. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
  135. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +845 -0
  136. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
  137. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +845 -0
  138. bootstack/cli/__init__.py +133 -0
  139. bootstack/cli/__main__.py +6 -0
  140. bootstack/cli/add.py +395 -0
  141. bootstack/cli/appicon.py +285 -0
  142. bootstack/cli/build.py +115 -0
  143. bootstack/cli/config.py +313 -0
  144. bootstack/cli/demo.py +564 -0
  145. bootstack/cli/dev.py +153 -0
  146. bootstack/cli/doctor.py +195 -0
  147. bootstack/cli/icons.py +98 -0
  148. bootstack/cli/promote.py +120 -0
  149. bootstack/cli/pyinstaller.py +268 -0
  150. bootstack/cli/run.py +95 -0
  151. bootstack/cli/start.py +117 -0
  152. bootstack/cli/templates/__init__.py +931 -0
  153. bootstack/clipboard.py +48 -0
  154. bootstack/constants.py +318 -0
  155. bootstack/data/README.md +615 -0
  156. bootstack/data/__init__.py +78 -0
  157. bootstack/data/_observable.py +276 -0
  158. bootstack/data/base.py +780 -0
  159. bootstack/data/file_source.py +367 -0
  160. bootstack/data/memory_source.py +450 -0
  161. bootstack/data/query.py +367 -0
  162. bootstack/data/readers.py +289 -0
  163. bootstack/data/sqlite_source.py +869 -0
  164. bootstack/data/types.py +354 -0
  165. bootstack/data/writers.py +232 -0
  166. bootstack/dev/__init__.py +36 -0
  167. bootstack/dev/_capture.py +141 -0
  168. bootstack/dev/_env.py +43 -0
  169. bootstack/dev/_registry.py +140 -0
  170. bootstack/dev/_reloader.py +351 -0
  171. bootstack/dev/_reset.py +50 -0
  172. bootstack/dev/_watcher.py +91 -0
  173. bootstack/dialogs/__init__.py +948 -0
  174. bootstack/dialogs/_impl/__init__.py +47 -0
  175. bootstack/dialogs/_impl/colorchooser.py +588 -0
  176. bootstack/dialogs/_impl/datedialog.py +450 -0
  177. bootstack/dialogs/_impl/dialog.py +594 -0
  178. bootstack/dialogs/_impl/filterdialog.py +358 -0
  179. bootstack/dialogs/_impl/fontdialog.py +364 -0
  180. bootstack/dialogs/_impl/formdialog.py +564 -0
  181. bootstack/dialogs/_impl/message.py +486 -0
  182. bootstack/dialogs/_impl/query.py +570 -0
  183. bootstack/errors.py +67 -0
  184. bootstack/events/__init__.py +111 -0
  185. bootstack/events/_event.py +135 -0
  186. bootstack/events/_payloads.py +539 -0
  187. bootstack/events/_subscription.py +38 -0
  188. bootstack/i18n/README.md +77 -0
  189. bootstack/i18n/__init__.py +23 -0
  190. bootstack/i18n/catalog.py +121 -0
  191. bootstack/i18n/intl_format.py +584 -0
  192. bootstack/i18n/msgcat.py +425 -0
  193. bootstack/i18n/specs.py +156 -0
  194. bootstack/images.py +563 -0
  195. bootstack/py.typed +1 -0
  196. bootstack/scheduling/__init__.py +11 -0
  197. bootstack/scheduling/_schedule.py +218 -0
  198. bootstack/shortcuts.py +21 -0
  199. bootstack/signals/README.md +98 -0
  200. bootstack/signals/__init__.py +10 -0
  201. bootstack/signals/integration.py +100 -0
  202. bootstack/signals/signal.py +353 -0
  203. bootstack/signals/types.py +6 -0
  204. bootstack/store.py +286 -0
  205. bootstack/streams/__init__.py +12 -0
  206. bootstack/streams/_stream.py +321 -0
  207. bootstack/style/__init__.py +38 -0
  208. bootstack/style/builders/__init__.py +51 -0
  209. bootstack/style/builders/badge.py +46 -0
  210. bootstack/style/builders/button.py +339 -0
  211. bootstack/style/builders/buttongroup.py +311 -0
  212. bootstack/style/builders/calendar.py +271 -0
  213. bootstack/style/builders/checkbutton.py +110 -0
  214. bootstack/style/builders/combobox.py +113 -0
  215. bootstack/style/builders/contextmenu.py +268 -0
  216. bootstack/style/builders/entry.py +82 -0
  217. bootstack/style/builders/expander.py +148 -0
  218. bootstack/style/builders/field.py +335 -0
  219. bootstack/style/builders/frame.py +50 -0
  220. bootstack/style/builders/label.py +28 -0
  221. bootstack/style/builders/labelframe.py +34 -0
  222. bootstack/style/builders/listview.py +369 -0
  223. bootstack/style/builders/menubar.py +91 -0
  224. bootstack/style/builders/menubutton.py +359 -0
  225. bootstack/style/builders/panedwindow.py +25 -0
  226. bootstack/style/builders/progressbar.py +67 -0
  227. bootstack/style/builders/radiobutton.py +99 -0
  228. bootstack/style/builders/scale.py +64 -0
  229. bootstack/style/builders/scrollbar.py +225 -0
  230. bootstack/style/builders/separator.py +49 -0
  231. bootstack/style/builders/sidenav.py +643 -0
  232. bootstack/style/builders/sizegrip.py +15 -0
  233. bootstack/style/builders/spinbox.py +119 -0
  234. bootstack/style/builders/switch.py +70 -0
  235. bootstack/style/builders/tabitem.py +204 -0
  236. bootstack/style/builders/togglegroup.py +294 -0
  237. bootstack/style/builders/toolbutton.py +275 -0
  238. bootstack/style/builders/tooltip.py +26 -0
  239. bootstack/style/builders/treeview.py +193 -0
  240. bootstack/style/builders/utils.py +455 -0
  241. bootstack/style/builders_tk/__init__.py +16 -0
  242. bootstack/style/builders_tk/defaults.py +229 -0
  243. bootstack/style/element.py +173 -0
  244. bootstack/style/fonts.py +123 -0
  245. bootstack/style/style.py +609 -0
  246. bootstack/style/style_builder_base.py +716 -0
  247. bootstack/style/style_builder_mixed.py +93 -0
  248. bootstack/style/style_builder_tk.py +109 -0
  249. bootstack/style/style_builder_ttk.py +353 -0
  250. bootstack/style/style_resolver.py +447 -0
  251. bootstack/style/theme.py +245 -0
  252. bootstack/style/theme_provider.py +471 -0
  253. bootstack/style/themes/__init__.py +128 -0
  254. bootstack/style/tk_patch.py +5 -0
  255. bootstack/style/token_maps.py +41 -0
  256. bootstack/style/types.py +32 -0
  257. bootstack/style/typography.py +523 -0
  258. bootstack/style/utility.py +746 -0
  259. bootstack/types.py +39 -0
  260. bootstack/validation/__init__.py +6 -0
  261. bootstack/validation/types.py +15 -0
  262. bootstack/validation/validation_result.py +17 -0
  263. bootstack/validation/validation_rules.py +205 -0
  264. bootstack/widgets/__init__.py +74 -0
  265. bootstack/widgets/_core/__init__.py +31 -0
  266. bootstack/widgets/_core/app_config.py +266 -0
  267. bootstack/widgets/_core/base.py +461 -0
  268. bootstack/widgets/_core/container.py +334 -0
  269. bootstack/widgets/_core/context.py +35 -0
  270. bootstack/widgets/_core/events.py +130 -0
  271. bootstack/widgets/_core/field_mixin.py +353 -0
  272. bootstack/widgets/_core/icon_image_props.py +72 -0
  273. bootstack/widgets/_core/image_binding.py +93 -0
  274. bootstack/widgets/_core/navmodel.py +345 -0
  275. bootstack/widgets/_core/options.py +210 -0
  276. bootstack/widgets/_core/selection_group.py +130 -0
  277. bootstack/widgets/_core/window_controls.py +96 -0
  278. bootstack/widgets/_core/window_menu.py +246 -0
  279. bootstack/widgets/_impl/__init__.py +1 -0
  280. bootstack/widgets/_impl/_internal/__init__.py +0 -0
  281. bootstack/widgets/_impl/_internal/wrapper_base.py +307 -0
  282. bootstack/widgets/_impl/_parts/__init__.py +11 -0
  283. bootstack/widgets/_impl/_parts/numberentry_part.py +385 -0
  284. bootstack/widgets/_impl/_parts/spinnerentry_part.py +434 -0
  285. bootstack/widgets/_impl/_parts/textentry_part.py +406 -0
  286. bootstack/widgets/_impl/composites/__init__.py +33 -0
  287. bootstack/widgets/_impl/composites/_dateutils.py +33 -0
  288. bootstack/widgets/_impl/composites/_image_fit.py +105 -0
  289. bootstack/widgets/_impl/composites/accordion.py +381 -0
  290. bootstack/widgets/_impl/composites/avatar.py +191 -0
  291. bootstack/widgets/_impl/composites/buttongroup.py +371 -0
  292. bootstack/widgets/_impl/composites/calendar.py +972 -0
  293. bootstack/widgets/_impl/composites/carousel.py +567 -0
  294. bootstack/widgets/_impl/composites/chart.py +882 -0
  295. bootstack/widgets/_impl/composites/compositeframe.py +298 -0
  296. bootstack/widgets/_impl/composites/contextmenu.py +1951 -0
  297. bootstack/widgets/_impl/composites/dateentry.py +404 -0
  298. bootstack/widgets/_impl/composites/dropdownbutton.py +325 -0
  299. bootstack/widgets/_impl/composites/expander.py +515 -0
  300. bootstack/widgets/_impl/composites/field.py +670 -0
  301. bootstack/widgets/_impl/composites/form.py +1066 -0
  302. bootstack/widgets/_impl/composites/gallery.py +551 -0
  303. bootstack/widgets/_impl/composites/list/__init__.py +15 -0
  304. bootstack/widgets/_impl/composites/list/listitem.py +802 -0
  305. bootstack/widgets/_impl/composites/list/listview.py +1433 -0
  306. bootstack/widgets/_impl/composites/menu/__init__.py +18 -0
  307. bootstack/widgets/_impl/composites/menu/model.py +358 -0
  308. bootstack/widgets/_impl/composites/menu/render_native.py +134 -0
  309. bootstack/widgets/_impl/composites/menu/render_themed.py +134 -0
  310. bootstack/widgets/_impl/composites/meter.py +860 -0
  311. bootstack/widgets/_impl/composites/numericentry.py +201 -0
  312. bootstack/widgets/_impl/composites/pagestack.py +395 -0
  313. bootstack/widgets/_impl/composites/passwordentry.py +142 -0
  314. bootstack/widgets/_impl/composites/pathentry.py +168 -0
  315. bootstack/widgets/_impl/composites/picture.py +289 -0
  316. bootstack/widgets/_impl/composites/radiogroup.py +511 -0
  317. bootstack/widgets/_impl/composites/scrolledtext.py +375 -0
  318. bootstack/widgets/_impl/composites/scrolledtext.pyi +186 -0
  319. bootstack/widgets/_impl/composites/scrollview.py +764 -0
  320. bootstack/widgets/_impl/composites/selectbox.py +1026 -0
  321. bootstack/widgets/_impl/composites/shell/__init__.py +40 -0
  322. bootstack/widgets/_impl/composites/shell/content_host.py +52 -0
  323. bootstack/widgets/_impl/composites/shell/layout.py +335 -0
  324. bootstack/widgets/_impl/composites/shell/nav_panel.py +345 -0
  325. bootstack/widgets/_impl/composites/shell/providers.py +558 -0
  326. bootstack/widgets/_impl/composites/shell/rail.py +117 -0
  327. bootstack/widgets/_impl/composites/shell/shell.py +581 -0
  328. bootstack/widgets/_impl/composites/shell/workspace.py +273 -0
  329. bootstack/widgets/_impl/composites/sidenav/__init__.py +16 -0
  330. bootstack/widgets/_impl/composites/sidenav/header.py +81 -0
  331. bootstack/widgets/_impl/composites/sidenav/separator.py +44 -0
  332. bootstack/widgets/_impl/composites/slider/__init__.py +7 -0
  333. bootstack/widgets/_impl/composites/slider/_shared.py +195 -0
  334. bootstack/widgets/_impl/composites/slider/rangeslider.py +982 -0
  335. bootstack/widgets/_impl/composites/slider/slider.py +851 -0
  336. bootstack/widgets/_impl/composites/spinnerentry.py +185 -0
  337. bootstack/widgets/_impl/composites/tableview/__init__.py +5 -0
  338. bootstack/widgets/_impl/composites/tableview/tableview.py +3388 -0
  339. bootstack/widgets/_impl/composites/tableview/types.py +169 -0
  340. bootstack/widgets/_impl/composites/tabs/__init__.py +23 -0
  341. bootstack/widgets/_impl/composites/tabs/tabitem.py +389 -0
  342. bootstack/widgets/_impl/composites/tabs/tabs.py +974 -0
  343. bootstack/widgets/_impl/composites/tabs/tabview.py +650 -0
  344. bootstack/widgets/_impl/composites/textarea/__init__.py +26 -0
  345. bootstack/widgets/_impl/composites/textarea/change.py +46 -0
  346. bootstack/widgets/_impl/composites/textarea/codeeditor.py +526 -0
  347. bootstack/widgets/_impl/composites/textarea/core.py +495 -0
  348. bootstack/widgets/_impl/composites/textarea/decoration.py +42 -0
  349. bootstack/widgets/_impl/composites/textarea/diff.py +127 -0
  350. bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +1 -0
  351. bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +138 -0
  352. bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +158 -0
  353. bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +138 -0
  354. bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +312 -0
  355. bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +177 -0
  356. bootstack/widgets/_impl/composites/textarea/filter.py +171 -0
  357. bootstack/widgets/_impl/composites/textarea/search_overlay.py +459 -0
  358. bootstack/widgets/_impl/composites/textarea/sidebar.py +88 -0
  359. bootstack/widgets/_impl/composites/textarea/style_registry.py +178 -0
  360. bootstack/widgets/_impl/composites/textarea/textarea.py +606 -0
  361. bootstack/widgets/_impl/composites/textarea/undo.py +217 -0
  362. bootstack/widgets/_impl/composites/textentry.py +57 -0
  363. bootstack/widgets/_impl/composites/timeentry.py +176 -0
  364. bootstack/widgets/_impl/composites/toast.py +390 -0
  365. bootstack/widgets/_impl/composites/toast_stack.py +156 -0
  366. bootstack/widgets/_impl/composites/togglegroup.py +418 -0
  367. bootstack/widgets/_impl/composites/toolbar.py +608 -0
  368. bootstack/widgets/_impl/composites/tooltip.py +491 -0
  369. bootstack/widgets/_impl/composites/tree/__init__.py +7 -0
  370. bootstack/widgets/_impl/composites/tree/source_binding.py +136 -0
  371. bootstack/widgets/_impl/composites/tree/treeitem.py +393 -0
  372. bootstack/widgets/_impl/composites/tree/treenode.py +174 -0
  373. bootstack/widgets/_impl/composites/tree/treeview.py +841 -0
  374. bootstack/widgets/_impl/mixins/__init__.py +24 -0
  375. bootstack/widgets/_impl/mixins/configure_mixin.py +216 -0
  376. bootstack/widgets/_impl/mixins/entry_mixin.py +134 -0
  377. bootstack/widgets/_impl/mixins/font_mixin.py +368 -0
  378. bootstack/widgets/_impl/mixins/icon_mixin.py +61 -0
  379. bootstack/widgets/_impl/mixins/localization_mixin.py +253 -0
  380. bootstack/widgets/_impl/mixins/signal_mixin.py +268 -0
  381. bootstack/widgets/_impl/mixins/validation_mixin.py +226 -0
  382. bootstack/widgets/_impl/primitives/__init__.py +49 -0
  383. bootstack/widgets/_impl/primitives/_menubutton.py +107 -0
  384. bootstack/widgets/_impl/primitives/badge.py +45 -0
  385. bootstack/widgets/_impl/primitives/button.py +76 -0
  386. bootstack/widgets/_impl/primitives/card.py +45 -0
  387. bootstack/widgets/_impl/primitives/checkbutton.py +124 -0
  388. bootstack/widgets/_impl/primitives/checktoggle.py +62 -0
  389. bootstack/widgets/_impl/primitives/combobox.py +156 -0
  390. bootstack/widgets/_impl/primitives/entry.py +87 -0
  391. bootstack/widgets/_impl/primitives/flexframe.py +448 -0
  392. bootstack/widgets/_impl/primitives/frame.py +185 -0
  393. bootstack/widgets/_impl/primitives/gridframe.py +546 -0
  394. bootstack/widgets/_impl/primitives/label.py +84 -0
  395. bootstack/widgets/_impl/primitives/labelframe.py +54 -0
  396. bootstack/widgets/_impl/primitives/optionmenu.py +387 -0
  397. bootstack/widgets/_impl/primitives/packframe.py +227 -0
  398. bootstack/widgets/_impl/primitives/panedwindow.py +45 -0
  399. bootstack/widgets/_impl/primitives/progressbar.py +83 -0
  400. bootstack/widgets/_impl/primitives/radiobutton.py +115 -0
  401. bootstack/widgets/_impl/primitives/radiotoggle.py +54 -0
  402. bootstack/widgets/_impl/primitives/scrollbar.py +42 -0
  403. bootstack/widgets/_impl/primitives/separator.py +43 -0
  404. bootstack/widgets/_impl/primitives/sizegrip.py +33 -0
  405. bootstack/widgets/_impl/primitives/spinbox.py +95 -0
  406. bootstack/widgets/_impl/primitives/switch.py +44 -0
  407. bootstack/widgets/_impl/primitives/treeview.py +69 -0
  408. bootstack/widgets/app.py +371 -0
  409. bootstack/widgets/appshell.py +1179 -0
  410. bootstack/widgets/avatar.py +140 -0
  411. bootstack/widgets/boolean_controls.py +455 -0
  412. bootstack/widgets/button.py +224 -0
  413. bootstack/widgets/buttongroup.py +239 -0
  414. bootstack/widgets/calendar.py +195 -0
  415. bootstack/widgets/card.py +159 -0
  416. bootstack/widgets/carousel.py +241 -0
  417. bootstack/widgets/chart.py +302 -0
  418. bootstack/widgets/codeeditor.py +675 -0
  419. bootstack/widgets/contextmenu.py +371 -0
  420. bootstack/widgets/datatable.py +688 -0
  421. bootstack/widgets/datefield.py +395 -0
  422. bootstack/widgets/divider.py +60 -0
  423. bootstack/widgets/expander.py +579 -0
  424. bootstack/widgets/form.py +200 -0
  425. bootstack/widgets/gallery.py +250 -0
  426. bootstack/widgets/gauge.py +168 -0
  427. bootstack/widgets/grid.py +121 -0
  428. bootstack/widgets/groupbox.py +162 -0
  429. bootstack/widgets/label.py +229 -0
  430. bootstack/widgets/listview.py +354 -0
  431. bootstack/widgets/menubutton.py +419 -0
  432. bootstack/widgets/numberfield.py +431 -0
  433. bootstack/widgets/pagestack.py +345 -0
  434. bootstack/widgets/passwordfield.py +383 -0
  435. bootstack/widgets/pathfield.py +454 -0
  436. bootstack/widgets/picture.py +251 -0
  437. bootstack/widgets/progressbar.py +106 -0
  438. bootstack/widgets/radio_variants.py +271 -0
  439. bootstack/widgets/radiogroup.py +228 -0
  440. bootstack/widgets/scrollbar.py +64 -0
  441. bootstack/widgets/scrollview.py +186 -0
  442. bootstack/widgets/select.py +302 -0
  443. bootstack/widgets/selectbutton.py +182 -0
  444. bootstack/widgets/sidebar_toggle.py +130 -0
  445. bootstack/widgets/sizegrip.py +42 -0
  446. bootstack/widgets/slider.py +408 -0
  447. bootstack/widgets/spinbox.py +147 -0
  448. bootstack/widgets/spinnerfield.py +413 -0
  449. bootstack/widgets/splash.py +367 -0
  450. bootstack/widgets/splitview.py +482 -0
  451. bootstack/widgets/stacks.py +249 -0
  452. bootstack/widgets/statusbar.py +189 -0
  453. bootstack/widgets/tabs.py +425 -0
  454. bootstack/widgets/textarea.py +459 -0
  455. bootstack/widgets/textfield.py +410 -0
  456. bootstack/widgets/theme_toggle.py +92 -0
  457. bootstack/widgets/timefield.py +340 -0
  458. bootstack/widgets/toast.py +314 -0
  459. bootstack/widgets/togglegroup.py +231 -0
  460. bootstack/widgets/toolbar.py +347 -0
  461. bootstack/widgets/tooltip.py +94 -0
  462. bootstack/widgets/tree.py +648 -0
  463. bootstack/widgets/types.py +305 -0
  464. bootstack/widgets/window.py +307 -0
  465. bootstack-0.1.0.dist-info/METADATA +301 -0
  466. bootstack-0.1.0.dist-info/RECORD +471 -0
  467. bootstack-0.1.0.dist-info/WHEEL +5 -0
  468. bootstack-0.1.0.dist-info/entry_points.txt +2 -0
  469. bootstack-0.1.0.dist-info/licenses/LICENSE +21 -0
  470. bootstack-0.1.0.dist-info/licenses/NOTICE +10 -0
  471. bootstack-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,268 @@
1
+ """PyInstaller spec file generation for bootstack projects."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from bootstack.cli.config import TtkbConfig
10
+
11
+
12
+ # PyInstaller spec template that reads from bootstack.toml
13
+ SPEC_TEMPLATE = '''\
14
+ # -*- mode: python ; coding: utf-8 -*-
15
+ """
16
+ PyInstaller spec file for {app_name}.
17
+
18
+ This file is auto-generated by bootstack CLI.
19
+ Edit bootstack.toml to change build settings, then regenerate with:
20
+ bootstack promote --pyinstaller --force
21
+ """
22
+
23
+ import sys
24
+ from pathlib import Path
25
+
26
+ # PyInstaller exec's this spec with SPECPATH (directory of the .spec file)
27
+ # in the namespace. __file__ is not defined inside a spec.
28
+ PROJECT_ROOT = Path(SPECPATH).parent.parent
29
+ CONFIG_PATH = PROJECT_ROOT / "bootstack.toml"
30
+
31
+ # Ensure we can import bootstack CLI tools
32
+ try:
33
+ from bootstack.cli.config import TtkbConfig
34
+ except ImportError:
35
+ sys.path.insert(0, str(PROJECT_ROOT / "src"))
36
+ from bootstack.cli.config import TtkbConfig
37
+
38
+ if not CONFIG_PATH.exists():
39
+ raise FileNotFoundError(f"bootstack.toml not found at {{CONFIG_PATH}}")
40
+
41
+ config = TtkbConfig.load(CONFIG_PATH)
42
+
43
+ # Extract settings
44
+ APP_NAME = config.app.name
45
+ ENTRY_POINT = PROJECT_ROOT / config.app.entry
46
+ WINDOWED = config.build.windowed if config.build else True
47
+ ONEFILE = config.build.onefile if config.build else False
48
+
49
+ # Icon handling — resolve the icon for the platform this build runs on
50
+ # (PyInstaller can't cross-compile, so we only need the host's format).
51
+ _HOST_KEY = {{"win32": "windows", "darwin": "macos", "linux": "linux"}}.get(sys.platform)
52
+ _HOST_EXT = {{"win32": ".ico", "darwin": ".icns"}}.get(sys.platform, ".png")
53
+
54
+ ICON_PATH = None
55
+ _icon_cfg = config.build.icon if config.build else None
56
+
57
+ # A host-specific path (windows/macos/linux) wins over the generic path.
58
+ _icon_path = None
59
+ if _icon_cfg:
60
+ _host_specific = getattr(_icon_cfg, _HOST_KEY, None) if _HOST_KEY else None
61
+ _icon_path = _host_specific or _icon_cfg.path
62
+
63
+ if _icon_path:
64
+ _candidate = PROJECT_ROOT / _icon_path
65
+ if _candidate.exists():
66
+ ICON_PATH = str(_candidate)
67
+ elif _icon_cfg and _icon_cfg.glyph:
68
+ # Render the glyph into the host platform's icon format. No app runs during a
69
+ # build, so colors must be hex — a theme token cannot be resolved.
70
+ if not (_icon_cfg.background.startswith("#") and _icon_cfg.foreground.startswith("#")):
71
+ print(
72
+ "[build.icon] background/foreground must be hex colors (e.g. "
73
+ "'#0d6efd'); theme tokens need a running app. Using the default icon."
74
+ )
75
+ else:
76
+ try:
77
+ from bootstack.images import AppIcon
78
+ out = PROJECT_ROOT / "build" / ("app-icon" + _HOST_EXT)
79
+ out.parent.mkdir(parents=True, exist_ok=True)
80
+ AppIcon(
81
+ _icon_cfg.glyph,
82
+ background=_icon_cfg.background,
83
+ foreground=_icon_cfg.foreground,
84
+ radius=_icon_cfg.radius,
85
+ shape=_icon_cfg.shape,
86
+ ).save(str(out))
87
+ ICON_PATH = str(out)
88
+ except Exception as exc:
89
+ print(f"Could not generate app icon from glyph {{_icon_cfg.glyph!r}}: {{exc}}")
90
+
91
+ if ICON_PATH is None:
92
+ # Fall back to the bundled bootstack launch icon. Override with [build.icon].
93
+ try:
94
+ import bootstack
95
+ default_icon = Path(bootstack.__file__).parent / "assets" / "bootstack.ico"
96
+ if default_icon.exists():
97
+ ICON_PATH = str(default_icon)
98
+ except Exception:
99
+ pass
100
+
101
+
102
+ # =============================================================================
103
+ # Collect data files
104
+ # =============================================================================
105
+
106
+ from glob import glob
107
+
108
+ datas = []
109
+
110
+ # Always include bootstack.toml
111
+ datas.append((str(CONFIG_PATH), "."))
112
+
113
+ # Include patterns from config
114
+ if config.build and config.build.datas.include:
115
+ for pattern in config.build.datas.include:
116
+ if pattern == "bootstack.toml":
117
+ continue # Already added
118
+ matches = glob(str(PROJECT_ROOT / pattern), recursive=True)
119
+ for match in matches:
120
+ match_path = Path(match)
121
+ if match_path.is_file():
122
+ rel_path = match_path.relative_to(PROJECT_ROOT)
123
+ dest_dir = str(rel_path.parent) if rel_path.parent != Path(".") else "."
124
+ datas.append((str(match_path), dest_dir))
125
+
126
+
127
+ # =============================================================================
128
+ # Collect package data (themes, fonts, icon glyphmaps, locales, etc.)
129
+ # =============================================================================
130
+
131
+ from PyInstaller.utils.hooks import collect_data_files, copy_metadata
132
+
133
+ for _pkg in ("bootstack",):
134
+ try:
135
+ datas += collect_data_files(_pkg)
136
+ datas += copy_metadata(_pkg)
137
+ except Exception as _e: # pragma: no cover
138
+ print(f"Warning: Could not collect data files from {{_pkg}}: {{_e}}")
139
+
140
+
141
+ # =============================================================================
142
+ # Hidden imports for bootstack
143
+ # =============================================================================
144
+
145
+ hiddenimports = [
146
+ "bootstack",
147
+ "bootstack.style",
148
+ "bootstack.widgets",
149
+ "PIL",
150
+ "PIL._tkinter_finder",
151
+ "babel.numbers",
152
+ "babel.dates",
153
+ "dateutil",
154
+ "dateparser",
155
+ ]
156
+ # Extra hidden imports from [build] hidden_imports
157
+ if config.build and config.build.hidden_imports:
158
+ hiddenimports += list(config.build.hidden_imports)
159
+
160
+ # babel.bin is a CLI script package whose __init__.py calls exit(1) when
161
+ # imported without args; PyInstaller's binary-dependency analyzer
162
+ # imports it during package walking and crashes. Excluding it is safe
163
+ # because nothing in a bootstack app uses babel's command-line tools.
164
+ excludes = [
165
+ "babel.bin",
166
+ ]
167
+ # Extra excludes from [build] excludes
168
+ if config.build and config.build.excludes:
169
+ excludes += list(config.build.excludes)
170
+
171
+
172
+ # =============================================================================
173
+ # Analysis
174
+ # =============================================================================
175
+
176
+ a = Analysis(
177
+ [str(ENTRY_POINT)],
178
+ pathex=[str(PROJECT_ROOT / "src")],
179
+ binaries=[],
180
+ datas=datas,
181
+ hiddenimports=hiddenimports,
182
+ hookspath=[],
183
+ hooksconfig={{}},
184
+ runtime_hooks=[],
185
+ excludes=excludes,
186
+ noarchive=False,
187
+ optimize=0,
188
+ )
189
+
190
+
191
+ # =============================================================================
192
+ # Build
193
+ # =============================================================================
194
+
195
+ pyz = PYZ(a.pure)
196
+
197
+ if ONEFILE:
198
+ exe = EXE(
199
+ pyz,
200
+ a.scripts,
201
+ a.binaries,
202
+ a.datas,
203
+ [],
204
+ name=APP_NAME,
205
+ debug=False,
206
+ bootloader_ignore_signals=False,
207
+ strip=False,
208
+ upx=True,
209
+ upx_exclude=[],
210
+ runtime_tmpdir=None,
211
+ console=not WINDOWED,
212
+ disable_windowed_traceback=False,
213
+ argv_emulation=False,
214
+ target_arch=None,
215
+ codesign_identity=None,
216
+ entitlements_file=None,
217
+ icon=ICON_PATH,
218
+ )
219
+ else:
220
+ exe = EXE(
221
+ pyz,
222
+ a.scripts,
223
+ [],
224
+ exclude_binaries=True,
225
+ name=APP_NAME,
226
+ debug=False,
227
+ bootloader_ignore_signals=False,
228
+ strip=False,
229
+ upx=True,
230
+ console=not WINDOWED,
231
+ disable_windowed_traceback=False,
232
+ argv_emulation=False,
233
+ target_arch=None,
234
+ codesign_identity=None,
235
+ entitlements_file=None,
236
+ icon=ICON_PATH,
237
+ )
238
+
239
+ coll = COLLECT(
240
+ exe,
241
+ a.binaries,
242
+ a.datas,
243
+ strip=False,
244
+ upx=True,
245
+ upx_exclude=[],
246
+ name=APP_NAME,
247
+ )
248
+ '''
249
+
250
+
251
+ def generate_spec(
252
+ config: TtkbConfig,
253
+ project_root: Path,
254
+ output_path: Path,
255
+ ) -> None:
256
+ """Generate a PyInstaller spec file.
257
+
258
+ Args:
259
+ config: The project configuration.
260
+ project_root: Root directory of the project.
261
+ output_path: Path to write the spec file.
262
+ """
263
+ content = SPEC_TEMPLATE.format(
264
+ app_name=config.app.name,
265
+ )
266
+
267
+ output_path.parent.mkdir(parents=True, exist_ok=True)
268
+ output_path.write_text(content, encoding="utf-8")
bootstack/cli/run.py ADDED
@@ -0,0 +1,95 @@
1
+ """bootstack run command - Run a bootstack application."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import subprocess
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ from bootstack.cli.config import TtkbConfig, find_config
11
+
12
+
13
+ def add_parser(subparsers: argparse._SubParsersAction) -> None:
14
+ """Add the 'run' subcommand parser."""
15
+ parser = subparsers.add_parser(
16
+ "run",
17
+ help="Run the bootstack application",
18
+ description="Run the application using the entry point defined in bootstack.toml.",
19
+ )
20
+ parser.add_argument(
21
+ "path",
22
+ nargs="?",
23
+ default=None,
24
+ help="Path to entry point or directory containing bootstack.toml (default: current directory)",
25
+ )
26
+ parser.set_defaults(func=run_run)
27
+
28
+
29
+ def run_run(args: argparse.Namespace) -> None:
30
+ """Execute the run command."""
31
+ path = args.path
32
+ config = None
33
+
34
+ # Determine what to run
35
+ if path:
36
+ path = Path(path)
37
+ if path.is_file() and path.suffix == ".py":
38
+ # Direct path to a Python file
39
+ entry_point = path
40
+ project_root = path.parent
41
+ elif path.is_dir():
42
+ # Directory - look for bootstack.toml
43
+ config_path = path / "bootstack.toml"
44
+ if not config_path.exists():
45
+ print(f"Error: No bootstack.toml found in '{path}'")
46
+ return
47
+ config = TtkbConfig.load(config_path)
48
+ entry_point = path / config.app.entry
49
+ project_root = path
50
+ else:
51
+ print(f"Error: '{path}' is not a valid file or directory")
52
+ return
53
+ else:
54
+ # No path specified - find bootstack.toml
55
+ config_path = find_config()
56
+ if config_path is None:
57
+ print("Error: No bootstack.toml found in current directory or parents.")
58
+ print("Run 'bootstack start <appname>' to create a new project.")
59
+ return
60
+
61
+ config = TtkbConfig.load(config_path)
62
+ project_root = config_path.parent
63
+ entry_point = project_root / config.app.entry
64
+
65
+ # Verify entry point exists
66
+ if not entry_point.exists():
67
+ print(f"Error: Entry point '{entry_point}' does not exist.")
68
+ return
69
+
70
+ print(f"Running {entry_point.relative_to(project_root)}...")
71
+ print()
72
+
73
+ # Run the application
74
+ # Add project src directory to PYTHONPATH
75
+ import os
76
+
77
+ src_dir = project_root / "src"
78
+ env = dict(os.environ)
79
+ python_path = env.get("PYTHONPATH", "")
80
+ if src_dir.exists():
81
+ if python_path:
82
+ env["PYTHONPATH"] = f"{src_dir}{os.pathsep}{python_path}"
83
+ else:
84
+ env["PYTHONPATH"] = str(src_dir)
85
+
86
+ try:
87
+ result = subprocess.run(
88
+ [sys.executable, str(entry_point.resolve())],
89
+ cwd=str(project_root),
90
+ env=env,
91
+ )
92
+ sys.exit(result.returncode)
93
+ except KeyboardInterrupt:
94
+ print("\nInterrupted.")
95
+ sys.exit(1)
bootstack/cli/start.py ADDED
@@ -0,0 +1,117 @@
1
+ """bootstack start command - Create a new bootstack project."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from pathlib import Path
7
+
8
+ from bootstack.cli.templates import create_project
9
+
10
+
11
+ def add_parser(subparsers: argparse._SubParsersAction) -> None:
12
+ """Add the 'start' subcommand parser."""
13
+ parser = subparsers.add_parser(
14
+ "start",
15
+ help="Create a new bootstack project",
16
+ description="Scaffold a new bootstack application with sensible defaults.",
17
+ )
18
+ parser.add_argument(
19
+ "name",
20
+ help="Application name (e.g., 'MyApp')",
21
+ )
22
+ parser.add_argument(
23
+ "--template",
24
+ choices=["basic", "appshell"],
25
+ default="basic",
26
+ help="Project template: 'basic' for single-view app, 'appshell' for sidebar navigation (default: basic)",
27
+ )
28
+ parser.add_argument(
29
+ "--nav",
30
+ choices=["single", "grouped", "master-detail", "workspaces"],
31
+ default="single",
32
+ help="Navigation style for the appshell template (default: single, "
33
+ "ignored for the basic template)",
34
+ )
35
+ parser.add_argument(
36
+ "--simple",
37
+ action="store_true",
38
+ help="Create minimal project without assets or build configuration",
39
+ )
40
+ parser.add_argument(
41
+ "--container",
42
+ choices=["grid", "pack"],
43
+ default="grid",
44
+ help="Default container type for views (default: grid, ignored for appshell template)",
45
+ )
46
+ parser.add_argument(
47
+ "--theme",
48
+ default="bootstrap-light",
49
+ help="Theme name for the application (default: bootstrap-light)",
50
+ )
51
+ parser.add_argument(
52
+ "--dir",
53
+ type=Path,
54
+ default=None,
55
+ help="Target directory (default: ./<appname>)",
56
+ )
57
+ parser.set_defaults(func=run_start)
58
+
59
+
60
+ def run_start(args: argparse.Namespace) -> None:
61
+ """Execute the start command."""
62
+ name = args.name
63
+ simple = args.simple
64
+ container = args.container
65
+ theme = args.theme
66
+ template = args.template
67
+ nav = args.nav
68
+
69
+ # Warn if --container is used with appshell (it's ignored)
70
+ if template == "appshell" and container != "grid":
71
+ print("Note: --container is ignored for appshell template.")
72
+ # Warn if --nav is used with the basic template (it's ignored)
73
+ if template == "basic" and nav != "single":
74
+ print("Note: --nav is ignored for the basic template.")
75
+
76
+ # Determine target directory
77
+ if args.dir:
78
+ target_dir = args.dir
79
+ else:
80
+ # Use lowercase name with underscores for directory
81
+ dir_name = name.lower().replace(" ", "_").replace("-", "_")
82
+ target_dir = Path.cwd() / dir_name
83
+
84
+ # Check if directory already exists and has content
85
+ if target_dir.exists() and any(target_dir.iterdir()):
86
+ print(f"Error: Directory '{target_dir}' already exists and is not empty.")
87
+ print("Choose a different name or remove the existing directory.")
88
+ return
89
+
90
+ # Create the project
91
+ print(f"Creating '{template}' project '{name}' in {target_dir}...")
92
+ create_project(
93
+ name=name,
94
+ target_dir=target_dir,
95
+ container=container,
96
+ theme=theme,
97
+ template=template,
98
+ simple=simple,
99
+ nav=nav,
100
+ )
101
+
102
+ # Print success message
103
+ print()
104
+ print(f"Project '{name}' created successfully!")
105
+ print()
106
+ print("Next steps:")
107
+ print(f" cd {target_dir.name}")
108
+ print(" bootstack run")
109
+ print()
110
+ if template == "appshell":
111
+ print("To add a new page:")
112
+ print(" bootstack add page DashboardPage")
113
+ print()
114
+ if not simple:
115
+ print("To build for distribution:")
116
+ print(" bootstack promote --pyinstaller")
117
+ print(" bootstack build")