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,931 @@
1
+ """Project templates for bootstack start command."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Literal
7
+
8
+ ContainerType = Literal["grid", "pack"]
9
+ TemplateType = Literal["basic", "appshell"]
10
+ NavStyle = Literal["single", "grouped", "master-detail", "workspaces"]
11
+
12
+
13
+ # =============================================================================
14
+ # Main entry point template
15
+ # =============================================================================
16
+
17
+ MAIN_PY_TEMPLATE = '''\
18
+ """
19
+ {app_name} - A bootstack application.
20
+
21
+ Run with: python -m {module_name}
22
+ """
23
+
24
+ import bootstack as bs
25
+
26
+ from {module_name}.views.main_view import build_main
27
+
28
+
29
+ def main() -> None:
30
+ """Application entry point."""
31
+ with bs.App(
32
+ title="{app_name}",
33
+ theme="{theme}",
34
+ size=(800, 600),
35
+ padding=20,
36
+ gap=10,
37
+ ) as app:
38
+ # `build_main` is a builder function: it paints its widgets into the
39
+ # active container (here, the App body). See the "Composing with
40
+ # Builders" guide.
41
+ build_main()
42
+
43
+ app.run()
44
+
45
+
46
+ if __name__ == "__main__":
47
+ main()
48
+ '''
49
+
50
+
51
+ # =============================================================================
52
+ # View templates (GridFrame vs PackFrame)
53
+ # =============================================================================
54
+
55
+ MAIN_VIEW_GRID_TEMPLATE = '''\
56
+ """Main application view."""
57
+
58
+ import bootstack as bs
59
+
60
+
61
+ def build_main() -> None:
62
+ """Build the main view (a two-column grid form)."""
63
+ # The App body is a column, so open a Grid for the label/field columns.
64
+ # `horizontal="stretch"` lets the Grid fill the width so the field column grows.
65
+ with bs.Grid(columns=["auto", 1], gap=10, horizontal="stretch"):
66
+ bs.Label("Welcome to {app_name}", font="heading-lg", columnspan=2)
67
+ bs.Label("Name:")
68
+ name_field = bs.TextField()
69
+ bs.Label("Email:")
70
+ email_field = bs.TextField()
71
+
72
+ def on_submit() -> None:
73
+ print(f"Name: {{name_field.value}}, Email: {{email_field.value}}")
74
+
75
+ bs.Button("Get Started", accent="primary", on_click=on_submit, columnspan=2)
76
+ '''
77
+
78
+
79
+ MAIN_VIEW_PACK_TEMPLATE = '''\
80
+ """Main application view."""
81
+
82
+ import bootstack as bs
83
+
84
+
85
+ def build_main() -> None:
86
+ """Build the main view (a vertical stack of fields)."""
87
+ # Paints directly into the active container (the padded App body).
88
+ bs.Label("Welcome to {app_name}", font="heading-lg")
89
+ name_field = bs.TextField(label="Name:", horizontal="stretch")
90
+ email_field = bs.TextField(label="Email:", horizontal="stretch")
91
+
92
+ def on_submit() -> None:
93
+ print(f"Name: {{name_field.value}}, Email: {{email_field.value}}")
94
+
95
+ bs.Button("Get Started", accent="primary", on_click=on_submit)
96
+ '''
97
+
98
+
99
+ # =============================================================================
100
+ # View/Dialog add templates
101
+ # =============================================================================
102
+
103
+ VIEW_GRID_TEMPLATE = '''\
104
+ """{title} view."""
105
+
106
+ import bootstack as bs
107
+
108
+
109
+ def {func_name}() -> None:
110
+ """Build the {title} view (a two-column grid)."""
111
+ with bs.Grid(columns=["auto", 1], gap=10, horizontal="stretch"):
112
+ bs.Label("{title}", font="heading-lg", columnspan=2)
113
+ # Add your widgets here
114
+ '''
115
+
116
+
117
+ VIEW_PACK_TEMPLATE = '''\
118
+ """{title} view."""
119
+
120
+ import bootstack as bs
121
+
122
+
123
+ def {func_name}() -> None:
124
+ """Build the {title} view."""
125
+ # Paints into the active container; the caller supplies padding/gap.
126
+ bs.Label("{title}", font="heading-lg")
127
+ # Add your widgets here
128
+ '''
129
+
130
+
131
+ DIALOG_TEMPLATE = '''\
132
+ """
133
+ {class_name} dialog.
134
+ """
135
+
136
+ import bootstack as bs
137
+ from bootstack.dialogs import Dialog, DialogButton
138
+
139
+
140
+ class {class_name}:
141
+ """A custom {class_name} dialog.
142
+
143
+ Usage::
144
+
145
+ result = {class_name}(parent=app).show()
146
+ if result:
147
+ ...
148
+ """
149
+
150
+ def __init__(self, parent=None):
151
+ self._dialog = Dialog(
152
+ title="{class_name}",
153
+ content_builder=self._build,
154
+ buttons=[
155
+ DialogButton("Cancel", role="cancel"),
156
+ DialogButton("OK", role="primary", result=True, default=True),
157
+ ],
158
+ min_size=(360, 200),
159
+ parent=parent,
160
+ )
161
+
162
+ def _build(self, frame) -> None:
163
+ """Fill the dialog body."""
164
+ with bs.Column(padding=20, gap=8, parent=frame):
165
+ bs.Label("Dialog content goes here.")
166
+
167
+ def show(self):
168
+ """Show the dialog modally and return its result (None if cancelled)."""
169
+ self._dialog.show()
170
+ return self._dialog.result
171
+ '''
172
+
173
+
174
+ # =============================================================================
175
+ # Package init template
176
+ # =============================================================================
177
+
178
+ INIT_PY_TEMPLATE = '''\
179
+ """{app_name} package."""
180
+
181
+ __version__ = "0.1.0"
182
+ '''
183
+
184
+
185
+ # =============================================================================
186
+ # README template
187
+ # =============================================================================
188
+
189
+ README_TEMPLATE = '''\
190
+ # {app_name}
191
+
192
+ A bootstack application.
193
+
194
+ ## Getting Started
195
+
196
+ ### Development
197
+
198
+ ```bash
199
+ # Run the application
200
+ python -m {module_name}
201
+
202
+ # Or use the CLI
203
+ bootstack run
204
+ ```
205
+
206
+ ### Building for Distribution
207
+
208
+ ```bash
209
+ # Promote to packaging-ready (adds PyInstaller support)
210
+ bootstack promote --pyinstaller
211
+
212
+ # Build the executable
213
+ bootstack build
214
+ ```
215
+
216
+ ## Project Structure
217
+
218
+ ```
219
+ {project_dir}/
220
+ ├── src/{module_name}/
221
+ │ ├── __init__.py
222
+ │ ├── main.py
223
+ │ └── views/
224
+ │ └── main_view.py
225
+ ├── assets/
226
+ ├── bootstack.toml
227
+ └── README.md
228
+ ```
229
+
230
+ ## Configuration
231
+
232
+ Application settings are defined in `bootstack.toml`:
233
+
234
+ - `[app]` - Application metadata
235
+ - `[layout]` - Default layout preferences
236
+ - `[build]` - Build/packaging configuration (after `bootstack promote`)
237
+ '''
238
+
239
+
240
+ # =============================================================================
241
+ # AppShell templates
242
+ # =============================================================================
243
+
244
+ def _appshell_chrome(app_name: str) -> str:
245
+ """The shared toolbar (menus + commands) and status bar baseline for AppShell
246
+ scaffolds. Delete what you do not need."""
247
+ return f'''\
248
+ # --- Toolbar (menus + commands) -------------------------------------
249
+ with shell.add_toolbar() as bar:
250
+ with bar.add_menu("File") as file_menu:
251
+ file_menu.add_action("Quit", shortcut="Mod+Q", on_click=shell.close)
252
+ with bar.add_menu("Help") as help_menu:
253
+ help_menu.add_action(
254
+ "About",
255
+ on_click=lambda: bs.alert("{app_name}", title="About"),
256
+ )
257
+ bar.add_spacer()
258
+ bar.add_theme_toggle()
259
+
260
+ # --- Status bar -----------------------------------------------------
261
+ shell.statusbar.add_text("Ready")
262
+ shell.statusbar.add_text("v0.1.0", side="right")
263
+ '''
264
+
265
+
266
+ APPSHELL_MAIN_SINGLE_TEMPLATE = '''\
267
+ """
268
+ {app_name} - A bootstack application.
269
+
270
+ Run with: python -m {module_name}
271
+ """
272
+
273
+ import bootstack as bs
274
+
275
+ from {module_name}.pages.home_page import build_home
276
+ from {module_name}.pages.settings_page import build_settings
277
+
278
+
279
+ def main() -> None:
280
+ """Application entry point."""
281
+ with bs.AppShell(
282
+ title="{app_name}",
283
+ theme="{theme}",
284
+ size=(1000, 650),
285
+ show_statusbar=True,
286
+ ) as shell:
287
+ {chrome}
288
+ # --- Navigation: a flat sidebar of pages ----------------------------
289
+ # Each page IS a layout container, so configure padding/gap/alignment on
290
+ # add_page() and let the page's builder paint straight into it.
291
+ with shell.page_nav() as nav:
292
+ with nav.add_page("home", text="Home", icon="house",
293
+ padding=20, gap=12, horizontal_items="stretch"):
294
+ build_home()
295
+
296
+ with nav.add_page("settings", text="Settings", icon="gear",
297
+ pin_to_footer=True, padding=20, gap=12,
298
+ horizontal_items="stretch"):
299
+ build_settings()
300
+
301
+ shell.navigate("home")
302
+
303
+ shell.run()
304
+
305
+
306
+ if __name__ == "__main__":
307
+ main()
308
+ '''
309
+
310
+
311
+ APPSHELL_MAIN_GROUPED_TEMPLATE = '''\
312
+ """
313
+ {app_name} - A bootstack application.
314
+
315
+ Run with: python -m {module_name}
316
+ """
317
+
318
+ import bootstack as bs
319
+
320
+ from {module_name}.pages.home_page import build_home
321
+ from {module_name}.pages.reports_page import build_reports
322
+ from {module_name}.pages.profile_page import build_profile
323
+ from {module_name}.pages.settings_page import build_settings
324
+
325
+
326
+ def main() -> None:
327
+ """Application entry point."""
328
+ with bs.AppShell(
329
+ title="{app_name}",
330
+ theme="{theme}",
331
+ size=(1000, 650),
332
+ show_statusbar=True,
333
+ ) as shell:
334
+ {chrome}
335
+ # --- Navigation: pages grouped under section headers ----------------
336
+ # Each page IS a layout container, so configure padding/gap/alignment on
337
+ # add_page() and let the page's builder paint straight into it.
338
+ with shell.page_nav() as nav:
339
+ nav.add_header("Workspace")
340
+ with nav.add_page("home", text="Home", icon="house",
341
+ padding=20, gap=12, horizontal_items="stretch"):
342
+ build_home()
343
+ with nav.add_page("reports", text="Reports", icon="bar-chart",
344
+ padding=20, gap=12, horizontal_items="stretch"):
345
+ build_reports()
346
+
347
+ nav.add_header("Account")
348
+ with nav.add_page("profile", text="Profile", icon="person",
349
+ padding=20, gap=12, horizontal_items="stretch"):
350
+ build_profile()
351
+
352
+ with nav.add_page("settings", text="Settings", icon="gear",
353
+ pin_to_footer=True, padding=20, gap=12,
354
+ horizontal_items="stretch"):
355
+ build_settings()
356
+
357
+ shell.navigate("home")
358
+
359
+ shell.run()
360
+
361
+
362
+ if __name__ == "__main__":
363
+ main()
364
+ '''
365
+
366
+
367
+ APPSHELL_MAIN_MASTER_DETAIL_TEMPLATE = '''\
368
+ """
369
+ {app_name} - A bootstack application.
370
+
371
+ Run with: python -m {module_name}
372
+ """
373
+
374
+ import bootstack as bs
375
+ from bootstack.data import MemoryDataSource
376
+
377
+ from {module_name}.pages.detail_view import build_detail
378
+
379
+ # Sample records drive the sidebar list. Swap in your own data source.
380
+ RECORDS = MemoryDataSource().load([
381
+ {{"id": 1, "title": "Dana Reyes", "text": "Q3 roadmap review", "icon": "person"}},
382
+ {{"id": 2, "title": "Sam Okonkwo", "text": "Budget approval", "icon": "person"}},
383
+ {{"id": 3, "title": "Priya Nair", "text": "New hire onboarding", "icon": "person"}},
384
+ ])
385
+
386
+
387
+ def main() -> None:
388
+ """Application entry point."""
389
+ with bs.AppShell(
390
+ title="{app_name}",
391
+ theme="{theme}",
392
+ size=(1000, 650),
393
+ show_statusbar=True,
394
+ ) as shell:
395
+ {chrome}
396
+ # --- Navigation: a record list drives a detail view -----------------
397
+ # `build_detail` is a builder: it paints the selected record into the
398
+ # detail region each time the selection changes.
399
+ shell.list_nav(RECORDS)
400
+
401
+ @shell.detail
402
+ def show(record):
403
+ build_detail(record)
404
+
405
+ shell.run()
406
+
407
+
408
+ if __name__ == "__main__":
409
+ main()
410
+ '''
411
+
412
+
413
+ APPSHELL_MAIN_WORKSPACES_TEMPLATE = '''\
414
+ """
415
+ {app_name} - A bootstack application.
416
+
417
+ Run with: python -m {module_name}
418
+ """
419
+
420
+ import bootstack as bs
421
+
422
+ from {module_name}.pages.home_page import build_home
423
+ from {module_name}.pages.reports_page import build_reports
424
+ from {module_name}.pages.settings_page import build_settings
425
+
426
+
427
+ def main() -> None:
428
+ """Application entry point."""
429
+ with bs.Workbench(
430
+ title="{app_name}",
431
+ theme="{theme}",
432
+ size=(1000, 650),
433
+ show_statusbar=True,
434
+ rail_labels=True,
435
+ ) as shell:
436
+ {chrome}
437
+ # --- Navigation: a labeled rail of workspaces -----------------------
438
+ # Each page IS a layout container, so configure padding/gap/alignment on
439
+ # add_page() and let the page's builder paint straight into it.
440
+ with shell.add_workspace("home", text="Home", icon="house") as ws:
441
+ with ws.page_nav() as nav:
442
+ with nav.add_page("overview", text="Overview", icon="speedometer2",
443
+ padding=20, gap=12, horizontal_items="stretch"):
444
+ build_home()
445
+
446
+ with shell.add_workspace("reports", text="Reports", icon="bar-chart") as ws:
447
+ with ws.page_nav() as nav:
448
+ with nav.add_page("monthly", text="Monthly", icon="calendar3",
449
+ padding=20, gap=12, horizontal_items="stretch"):
450
+ build_reports()
451
+
452
+ with shell.add_workspace("settings", text="Settings", icon="gear", pin_to_footer=True) as ws:
453
+ with ws.page_nav() as nav:
454
+ with nav.add_page("general", text="General", icon="sliders",
455
+ padding=20, gap=12, horizontal_items="stretch"):
456
+ build_settings()
457
+
458
+ shell.run()
459
+
460
+
461
+ if __name__ == "__main__":
462
+ main()
463
+ '''
464
+
465
+
466
+ APPSHELL_HOME_PAGE_TEMPLATE = '''\
467
+ """Home page."""
468
+
469
+ import bootstack as bs
470
+
471
+
472
+ def build_home():
473
+ """Build the Home page.
474
+
475
+ A builder function: it paints into the active container. The page region
476
+ (configured on `nav.add_page(...)` in main.py) supplies the padding and gap,
477
+ so there is no extra layout wrapper here.
478
+ """
479
+ bs.Label("Welcome to {app_name}", font="heading-xl")
480
+ bs.Label(
481
+ "This is your home page. Edit this file to get started.",
482
+ wrap_width=500,
483
+ )
484
+ with bs.GroupBox("Getting Started", grow=True, horizontal="stretch"):
485
+ bs.Label(
486
+ "Add your widgets here.\\n\\n"
487
+ "To add another page:\\n"
488
+ " 1. Run \\'bootstack add page <Name>\\' to generate the file.\\n"
489
+ " 2. In main.py, add a \\'with nav.add_page(...):\\' block inside\\n"
490
+ " your \\'shell.page_nav()\\' and call the page's build_* function in it."
491
+ )
492
+ '''
493
+
494
+
495
+ APPSHELL_SETTINGS_PAGE_TEMPLATE = '''\
496
+ """Settings page."""
497
+
498
+ import bootstack as bs
499
+
500
+
501
+ def build_settings():
502
+ """Build the Settings page."""
503
+ bs.Label("Settings", font="heading-xl")
504
+ bs.Label("Configure your application preferences.", wrap_width=500)
505
+ with bs.GroupBox("Preferences", grow=True, horizontal="stretch"):
506
+ with bs.Row(gap=8):
507
+ bs.Label("Theme:")
508
+ bs.ThemeToggle()
509
+ '''
510
+
511
+
512
+ APPSHELL_PAGE_TEMPLATE = '''\
513
+ """{page_title} page."""
514
+
515
+ import bootstack as bs
516
+
517
+
518
+ def {func_name}():
519
+ """Build the {page_title} page."""
520
+ bs.Label("{page_title}", font="heading-lg")
521
+ with bs.GroupBox("Content", grow=True, horizontal="stretch"):
522
+ pass # Add your widgets here
523
+ '''
524
+
525
+
526
+ APPSHELL_CONTENT_PAGE_TEMPLATE = '''\
527
+ """{page_title} page."""
528
+
529
+ import bootstack as bs
530
+
531
+
532
+ def {func_name}():
533
+ """Build the {page_title} page."""
534
+ bs.Label("{page_title}", font="heading-lg")
535
+ with bs.GroupBox("Content", grow=True, horizontal="stretch"):
536
+ bs.Label("Add your widgets here.")
537
+ '''
538
+
539
+
540
+ APPSHELL_DETAIL_VIEW_TEMPLATE = '''\
541
+ """Detail view for the record selected in the sidebar list."""
542
+
543
+ import bootstack as bs
544
+
545
+
546
+ def build_detail(record):
547
+ """Build the detail view for the selected record.
548
+
549
+ The detail region is a plain fill area, so this builder opens its own
550
+ padded `Column` to frame the content.
551
+ """
552
+ with bs.Column(padding=20, gap=12, horizontal_items="stretch"):
553
+ bs.Label(record["title"], font="heading-lg")
554
+ bs.Label(record.get("text", ""), accent="secondary")
555
+ bs.Divider(horizontal="stretch")
556
+ bs.Label("Detail content for this record goes here.")
557
+ '''
558
+
559
+
560
+ APPSHELL_README_TEMPLATE = '''\
561
+ # {app_name}
562
+
563
+ A bootstack application using AppShell navigation.
564
+
565
+ ## Getting Started
566
+
567
+ ### Development
568
+
569
+ ```bash
570
+ # Run the application
571
+ python -m {module_name}
572
+
573
+ # Or use the CLI
574
+ bootstack run
575
+ ```
576
+
577
+ ### Adding Pages
578
+
579
+ ```bash
580
+ # Scaffold a new page
581
+ bootstack add page DashboardPage
582
+
583
+ # Then wire it up in main.py, inside your shell.page_nav() block:
584
+ # from {module_name}.pages.dashboard_page import build_dashboard
585
+ # with nav.add_page("dashboard", text="Dashboard", icon="speedometer2",
586
+ # padding=20, gap=12, horizontal_items="stretch"):
587
+ # build_dashboard()
588
+ ```
589
+
590
+ ### Building for Distribution
591
+
592
+ ```bash
593
+ # Promote to packaging-ready (adds PyInstaller support)
594
+ bootstack promote --pyinstaller
595
+
596
+ # Build the executable
597
+ bootstack build
598
+ ```
599
+
600
+ ## Project Structure
601
+
602
+ ```
603
+ {project_dir}/
604
+ \u251c\u2500\u2500 src/{module_name}/
605
+ \u2502 \u251c\u2500\u2500 __init__.py
606
+ \u2502 \u251c\u2500\u2500 main.py
607
+ \u2502 \u2514\u2500\u2500 pages/
608
+ \u2502 \u251c\u2500\u2500 __init__.py
609
+ \u2502 \u251c\u2500\u2500 home_page.py
610
+ \u2502 \u2514\u2500\u2500 settings_page.py
611
+ \u251c\u2500\u2500 assets/
612
+ \u251c\u2500\u2500 bootstack.toml
613
+ \u2514\u2500\u2500 README.md
614
+ ```
615
+
616
+ ## Configuration
617
+
618
+ Application settings are defined in `bootstack.toml`:
619
+
620
+ - `[app]` - Application metadata
621
+ - `[layout]` - Default layout preferences
622
+ - `[build]` - Build/packaging configuration (after `bootstack promote`)
623
+ '''
624
+
625
+
626
+ def create_page(
627
+ class_name: str,
628
+ target_dir: Path,
629
+ scrollable: bool = False,
630
+ ) -> Path:
631
+ """Create a new AppShell page file.
632
+
633
+ Args:
634
+ class_name: Page class name (CamelCase).
635
+ target_dir: Directory to create the page in.
636
+ scrollable: If True, the page template notes that
637
+ `scrollable=True` should be passed to `add_page()`.
638
+
639
+ Returns:
640
+ Path to the created file.
641
+ """
642
+ file_name = _camel_to_snake(class_name) + ".py"
643
+
644
+ content = APPSHELL_PAGE_TEMPLATE.format(
645
+ func_name=_build_func_name(class_name),
646
+ page_title=_readable_title(class_name),
647
+ )
648
+
649
+ file_path = target_dir / file_name
650
+ file_path.write_text(content, encoding="utf-8")
651
+
652
+ return file_path
653
+
654
+
655
+ def create_project(
656
+ name: str,
657
+ target_dir: Path,
658
+ container: ContainerType = "grid",
659
+ theme: str = "bootstrap-light",
660
+ template: TemplateType = "basic",
661
+ simple: bool = False,
662
+ nav: NavStyle = "single",
663
+ ) -> None:
664
+ """Create a new bootstack project.
665
+
666
+ Args:
667
+ name: Application name.
668
+ target_dir: Target directory for the project.
669
+ container: Default container type ('grid' or 'pack').
670
+ theme: Theme name for the application.
671
+ template: Project template ('basic' or 'appshell').
672
+ simple: If True, create minimal project without build config.
673
+ nav: Navigation style for the appshell template ('single', 'grouped',
674
+ 'master-detail', or 'workspaces'). Ignored for the basic template.
675
+ """
676
+ if template == "appshell":
677
+ _create_appshell_project(name, target_dir, theme, simple, nav)
678
+ else:
679
+ _create_basic_project(name, target_dir, container, theme, simple)
680
+
681
+
682
+ def _create_basic_project(
683
+ name: str,
684
+ target_dir: Path,
685
+ container: ContainerType,
686
+ theme: str,
687
+ simple: bool,
688
+ ) -> None:
689
+ """Create a basic single-view project."""
690
+ from bootstack.cli.config import write_config
691
+
692
+ # Normalize names
693
+ name_lower = name.lower().replace(" ", "_").replace("-", "_")
694
+ module_name = name_lower
695
+
696
+ # Create directory structure
697
+ src_dir = target_dir / "src" / module_name
698
+ views_dir = src_dir / "views"
699
+ assets_dir = target_dir / "assets"
700
+
701
+ src_dir.mkdir(parents=True, exist_ok=True)
702
+ views_dir.mkdir(parents=True, exist_ok=True)
703
+ if not simple:
704
+ assets_dir.mkdir(parents=True, exist_ok=True)
705
+
706
+ # Write main.py
707
+ main_content = MAIN_PY_TEMPLATE.format(
708
+ app_name=name,
709
+ module_name=module_name,
710
+ theme=theme,
711
+ )
712
+ (src_dir / "main.py").write_text(main_content, encoding="utf-8")
713
+
714
+ # Write __init__.py
715
+ init_content = INIT_PY_TEMPLATE.format(app_name=name)
716
+ (src_dir / "__init__.py").write_text(init_content, encoding="utf-8")
717
+
718
+ # Write views/__init__.py
719
+ (views_dir / "__init__.py").write_text(
720
+ '"""Views package."""\n', encoding="utf-8"
721
+ )
722
+
723
+ # Write main_view.py
724
+ if container == "grid":
725
+ view_template = MAIN_VIEW_GRID_TEMPLATE
726
+ else:
727
+ view_template = MAIN_VIEW_PACK_TEMPLATE
728
+
729
+ view_content = view_template.format(app_name=name)
730
+ (views_dir / "main_view.py").write_text(view_content, encoding="utf-8")
731
+
732
+ # Write bootstack.toml
733
+ write_config(
734
+ path=target_dir / "bootstack.toml",
735
+ name=name,
736
+ entry=f"src/{module_name}/main.py",
737
+ theme=theme,
738
+ template="basic",
739
+ include_build=False,
740
+ )
741
+
742
+ # Write README.md
743
+ if not simple:
744
+ readme_content = README_TEMPLATE.format(
745
+ app_name=name,
746
+ module_name=module_name,
747
+ project_dir=target_dir.name,
748
+ )
749
+ (target_dir / "README.md").write_text(readme_content, encoding="utf-8")
750
+
751
+
752
+ _APPSHELL_MAIN_TEMPLATES = {
753
+ "single": APPSHELL_MAIN_SINGLE_TEMPLATE,
754
+ "grouped": APPSHELL_MAIN_GROUPED_TEMPLATE,
755
+ "master-detail": APPSHELL_MAIN_MASTER_DETAIL_TEMPLATE,
756
+ "workspaces": APPSHELL_MAIN_WORKSPACES_TEMPLATE,
757
+ }
758
+
759
+
760
+ def _create_appshell_project(
761
+ name: str,
762
+ target_dir: Path,
763
+ theme: str,
764
+ simple: bool,
765
+ nav: NavStyle = "single",
766
+ ) -> None:
767
+ """Create an AppShell project with the chosen navigation style."""
768
+ from bootstack.cli.config import write_config
769
+
770
+ # Normalize names
771
+ name_lower = name.lower().replace(" ", "_").replace("-", "_")
772
+ module_name = name_lower
773
+
774
+ # Create directory structure
775
+ src_dir = target_dir / "src" / module_name
776
+ pages_dir = src_dir / "pages"
777
+ assets_dir = target_dir / "assets"
778
+
779
+ src_dir.mkdir(parents=True, exist_ok=True)
780
+ pages_dir.mkdir(parents=True, exist_ok=True)
781
+ if not simple:
782
+ assets_dir.mkdir(parents=True, exist_ok=True)
783
+
784
+ # Write main.py for the chosen navigation style
785
+ main_template = _APPSHELL_MAIN_TEMPLATES.get(nav, APPSHELL_MAIN_SINGLE_TEMPLATE)
786
+ main_content = main_template.format(
787
+ app_name=name,
788
+ module_name=module_name,
789
+ theme=theme,
790
+ chrome=_appshell_chrome(name),
791
+ )
792
+ (src_dir / "main.py").write_text(main_content, encoding="utf-8")
793
+
794
+ # Write __init__.py
795
+ init_content = INIT_PY_TEMPLATE.format(app_name=name)
796
+ (src_dir / "__init__.py").write_text(init_content, encoding="utf-8")
797
+
798
+ # Write pages/__init__.py
799
+ (pages_dir / "__init__.py").write_text(
800
+ '"""Pages package."""\n', encoding="utf-8"
801
+ )
802
+
803
+ def _write_content_page(class_name: str, title: str) -> None:
804
+ content = APPSHELL_CONTENT_PAGE_TEMPLATE.format(
805
+ func_name=_build_func_name(class_name), page_title=title
806
+ )
807
+ (pages_dir / f"{_camel_to_snake(class_name)}.py").write_text(
808
+ content, encoding="utf-8"
809
+ )
810
+
811
+ # Write the page files the chosen nav style imports
812
+ if nav == "master-detail":
813
+ (pages_dir / "detail_view.py").write_text(
814
+ APPSHELL_DETAIL_VIEW_TEMPLATE, encoding="utf-8"
815
+ )
816
+ else:
817
+ (pages_dir / "home_page.py").write_text(
818
+ APPSHELL_HOME_PAGE_TEMPLATE.format(app_name=name), encoding="utf-8"
819
+ )
820
+ (pages_dir / "settings_page.py").write_text(
821
+ APPSHELL_SETTINGS_PAGE_TEMPLATE, encoding="utf-8"
822
+ )
823
+ if nav in ("grouped", "workspaces"):
824
+ _write_content_page("ReportsPage", "Reports")
825
+ if nav == "grouped":
826
+ _write_content_page("ProfilePage", "Profile")
827
+
828
+ # Write bootstack.toml
829
+ write_config(
830
+ path=target_dir / "bootstack.toml",
831
+ name=name,
832
+ entry=f"src/{module_name}/main.py",
833
+ theme=theme,
834
+ template="appshell",
835
+ include_build=False,
836
+ )
837
+
838
+ # Write README.md
839
+ if not simple:
840
+ readme_content = APPSHELL_README_TEMPLATE.format(
841
+ app_name=name,
842
+ module_name=module_name,
843
+ project_dir=target_dir.name,
844
+ )
845
+ (target_dir / "README.md").write_text(readme_content, encoding="utf-8")
846
+
847
+
848
+ def create_view(
849
+ class_name: str,
850
+ target_dir: Path,
851
+ container: ContainerType = "grid",
852
+ ) -> Path:
853
+ """Create a new view file.
854
+
855
+ Args:
856
+ class_name: View class name (CamelCase).
857
+ target_dir: Directory to create the view in.
858
+ container: Container type ('grid' or 'pack').
859
+
860
+ Returns:
861
+ Path to the created file.
862
+ """
863
+ # Convert CamelCase to snake_case
864
+ file_name = _camel_to_snake(class_name) + ".py"
865
+
866
+ if container == "grid":
867
+ template = VIEW_GRID_TEMPLATE
868
+ else:
869
+ template = VIEW_PACK_TEMPLATE
870
+
871
+ content = template.format(
872
+ func_name=_build_func_name(class_name),
873
+ title=_readable_title(class_name),
874
+ )
875
+
876
+ file_path = target_dir / file_name
877
+ file_path.write_text(content, encoding="utf-8")
878
+
879
+ return file_path
880
+
881
+
882
+ def create_dialog(
883
+ class_name: str,
884
+ target_dir: Path,
885
+ ) -> Path:
886
+ """Create a new dialog file.
887
+
888
+ Args:
889
+ class_name: Dialog class name (CamelCase).
890
+ target_dir: Directory to create the dialog in.
891
+
892
+ Returns:
893
+ Path to the created file.
894
+ """
895
+ file_name = _camel_to_snake(class_name) + ".py"
896
+ content = DIALOG_TEMPLATE.format(class_name=class_name)
897
+
898
+ file_path = target_dir / file_name
899
+ file_path.write_text(content, encoding="utf-8")
900
+
901
+ return file_path
902
+
903
+
904
+ def _camel_to_snake(name: str) -> str:
905
+ """Convert CamelCase to snake_case."""
906
+ import re
907
+
908
+ # Insert underscore before uppercase letters and lowercase
909
+ s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)
910
+ return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
911
+
912
+
913
+ def _strip_kind_suffix(name: str) -> str:
914
+ """Drop a trailing `Page`/`View` from a CamelCase name (keep if it's all there is)."""
915
+ for suffix in ("Page", "View"):
916
+ if name.endswith(suffix) and len(name) > len(suffix):
917
+ return name[: -len(suffix)]
918
+ return name
919
+
920
+
921
+ def _build_func_name(class_name: str) -> str:
922
+ """Derive a builder function name from a name (e.g. `DashboardPage` -> `build_dashboard`)."""
923
+ return "build_" + _camel_to_snake(_strip_kind_suffix(class_name))
924
+
925
+
926
+ def _readable_title(class_name: str) -> str:
927
+ """Derive a readable title from a name (e.g. `DashboardPage` -> `Dashboard`)."""
928
+ import re
929
+
930
+ base = _strip_kind_suffix(class_name)
931
+ return re.sub(r"([a-z])([A-Z])", r"\1 \2", base)