bootstack 0.1.0a1__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 (419) hide show
  1. bootstack/__init__.py +249 -0
  2. bootstack/__main__.py +5 -0
  3. bootstack/api/__init__.py +127 -0
  4. bootstack/api/app.py +30 -0
  5. bootstack/api/constants.py +3 -0
  6. bootstack/api/data.py +23 -0
  7. bootstack/api/dialogs.py +44 -0
  8. bootstack/api/i18n.py +17 -0
  9. bootstack/api/localization.py +16 -0
  10. bootstack/api/menu.py +7 -0
  11. bootstack/api/style.py +23 -0
  12. bootstack/api/utils.py +24 -0
  13. bootstack/api/widgets.py +137 -0
  14. bootstack/assets/__init__.py +24 -0
  15. bootstack/assets/bootstack-transparent.png +0 -0
  16. bootstack/assets/bootstack.ico +0 -0
  17. bootstack/assets/bootstack.png +0 -0
  18. bootstack/assets/elements/__init__.py +0 -0
  19. bootstack/assets/elements/badge-pill.png +0 -0
  20. bootstack/assets/elements/badge-square.png +0 -0
  21. bootstack/assets/elements/border.png +0 -0
  22. bootstack/assets/elements/button-compact.png +0 -0
  23. bootstack/assets/elements/button-default.png +0 -0
  24. bootstack/assets/elements/button-group-horizontal-after-compact.png +0 -0
  25. bootstack/assets/elements/button-group-horizontal-after-default.png +0 -0
  26. bootstack/assets/elements/button-group-horizontal-before-compact.png +0 -0
  27. bootstack/assets/elements/button-group-horizontal-before-default.png +0 -0
  28. bootstack/assets/elements/button-group-horizontal-center-compact.png +0 -0
  29. bootstack/assets/elements/button-group-horizontal-center-default.png +0 -0
  30. bootstack/assets/elements/button-group-vertical-after-compact.png +0 -0
  31. bootstack/assets/elements/button-group-vertical-after-default.png +0 -0
  32. bootstack/assets/elements/button-group-vertical-before-compact.png +0 -0
  33. bootstack/assets/elements/button-group-vertical-before-default.png +0 -0
  34. bootstack/assets/elements/button-group-vertical-center-compact.png +0 -0
  35. bootstack/assets/elements/button-group-vertical-center-default.png +0 -0
  36. bootstack/assets/elements/checkbox-checked.png +0 -0
  37. bootstack/assets/elements/checkbox-indeterminate.png +0 -0
  38. bootstack/assets/elements/checkbox-unchecked.png +0 -0
  39. bootstack/assets/elements/field.png +0 -0
  40. bootstack/assets/elements/input-after-compact.png +0 -0
  41. bootstack/assets/elements/input-after-default.png +0 -0
  42. bootstack/assets/elements/input-before-compact.png +0 -0
  43. bootstack/assets/elements/input-before-default.png +0 -0
  44. bootstack/assets/elements/input-compact.png +0 -0
  45. bootstack/assets/elements/input-default.png +0 -0
  46. bootstack/assets/elements/list-item-separated.png +0 -0
  47. bootstack/assets/elements/list-item.png +0 -0
  48. bootstack/assets/elements/manifest.toml +480 -0
  49. bootstack/assets/elements/menu-item.png +0 -0
  50. bootstack/assets/elements/nav-button-compact.png +0 -0
  51. bootstack/assets/elements/nav-button-default.png +0 -0
  52. bootstack/assets/elements/nav-icon-button-compact.png +0 -0
  53. bootstack/assets/elements/nav-icon-button-default.png +0 -0
  54. bootstack/assets/elements/notebook-client-border.png +0 -0
  55. bootstack/assets/elements/notebook-tab-active.png +0 -0
  56. bootstack/assets/elements/notebook-tab-bar.png +0 -0
  57. bootstack/assets/elements/notebook-tab-normal.png +0 -0
  58. bootstack/assets/elements/notebook-tab-pill.png +0 -0
  59. bootstack/assets/elements/progress-bar-horizontal-striped.png +0 -0
  60. bootstack/assets/elements/progress-bar-solid.png +0 -0
  61. bootstack/assets/elements/progress-bar-thin.png +0 -0
  62. bootstack/assets/elements/progress-bar-vertical-striped.png +0 -0
  63. bootstack/assets/elements/radio-selected.png +0 -0
  64. bootstack/assets/elements/radio-unselected.png +0 -0
  65. bootstack/assets/elements/scrollbar-horizontal.png +0 -0
  66. bootstack/assets/elements/scrollbar-vertical.png +0 -0
  67. bootstack/assets/elements/slider-handle-focus.png +0 -0
  68. bootstack/assets/elements/slider-handle.png +0 -0
  69. bootstack/assets/elements/slider-track-horizontal.png +0 -0
  70. bootstack/assets/elements/slider-track-vertical.png +0 -0
  71. bootstack/assets/elements/switch-off.png +0 -0
  72. bootstack/assets/elements/switch-on.png +0 -0
  73. bootstack/assets/elements/tabs-bar-horizontal.png +0 -0
  74. bootstack/assets/elements/tabs-bar-vertical.png +0 -0
  75. bootstack/assets/elements/tabs-pill.png +0 -0
  76. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
  77. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +853 -0
  78. bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +875 -0
  79. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
  80. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +853 -0
  81. bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
  82. bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +853 -0
  83. bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
  84. bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +853 -0
  85. bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
  86. bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +875 -0
  87. bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
  88. bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +853 -0
  89. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
  90. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +853 -0
  91. bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
  92. bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +851 -0
  93. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
  94. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +842 -0
  95. bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
  96. bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +841 -0
  97. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
  98. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +914 -0
  99. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
  100. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +842 -0
  101. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
  102. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +841 -0
  103. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
  104. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +841 -0
  105. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
  106. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +842 -0
  107. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
  108. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +842 -0
  109. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
  110. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +842 -0
  111. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
  112. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +842 -0
  113. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
  114. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +842 -0
  115. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
  116. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +842 -0
  117. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
  118. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +842 -0
  119. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
  120. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +842 -0
  121. bootstack/assets/themes/__init__.py +0 -0
  122. bootstack/assets/themes/amber-dark.json +32 -0
  123. bootstack/assets/themes/amber-light.json +32 -0
  124. bootstack/assets/themes/aurora-dark.json +32 -0
  125. bootstack/assets/themes/aurora-light.json +32 -0
  126. bootstack/assets/themes/bootstrap-dark.json +32 -0
  127. bootstack/assets/themes/bootstrap-light.json +32 -0
  128. bootstack/assets/themes/classic-dark.json +32 -0
  129. bootstack/assets/themes/classic-light.json +32 -0
  130. bootstack/assets/themes/docs-dark.json +32 -0
  131. bootstack/assets/themes/docs-light.json +32 -0
  132. bootstack/assets/themes/forest-dark.json +32 -0
  133. bootstack/assets/themes/forest-light.json +32 -0
  134. bootstack/assets/themes/ocean-dark.json +32 -0
  135. bootstack/assets/themes/ocean-light.json +32 -0
  136. bootstack/assets/themes/rose-dark.json +32 -0
  137. bootstack/assets/themes/rose-light.json +32 -0
  138. bootstack/assets/widgets/__init__.py +0 -0
  139. bootstack/assets/widgets/badge-default.png +0 -0
  140. bootstack/assets/widgets/badge-pill.png +0 -0
  141. bootstack/assets/widgets/border.png +0 -0
  142. bootstack/assets/widgets/button-group-horizontal-after.png +0 -0
  143. bootstack/assets/widgets/button-group-horizontal-before.png +0 -0
  144. bootstack/assets/widgets/button-group-horizontal-center.png +0 -0
  145. bootstack/assets/widgets/button-group-vertical-after.png +0 -0
  146. bootstack/assets/widgets/button-group-vertical-before.png +0 -0
  147. bootstack/assets/widgets/button-group-vertical-center.png +0 -0
  148. bootstack/assets/widgets/button.png +0 -0
  149. bootstack/assets/widgets/checkbox-checked.png +0 -0
  150. bootstack/assets/widgets/checkbox-indeterminate.png +0 -0
  151. bootstack/assets/widgets/checkbox-unchecked.png +0 -0
  152. bootstack/assets/widgets/field.png +0 -0
  153. bootstack/assets/widgets/icon-button.png +0 -0
  154. bootstack/assets/widgets/input-inner.png +0 -0
  155. bootstack/assets/widgets/input-prefix.png +0 -0
  156. bootstack/assets/widgets/input-suffix.png +0 -0
  157. bootstack/assets/widgets/input.png +0 -0
  158. bootstack/assets/widgets/list-item-focus.png +0 -0
  159. bootstack/assets/widgets/list-item-separated.png +0 -0
  160. bootstack/assets/widgets/menu-item-separated.png +0 -0
  161. bootstack/assets/widgets/notebook-client-border.png +0 -0
  162. bootstack/assets/widgets/notebook-pill-active.png +0 -0
  163. bootstack/assets/widgets/notebook-pill-inactive.png +0 -0
  164. bootstack/assets/widgets/notebook-tab-active.png +0 -0
  165. bootstack/assets/widgets/notebook-tab-border.png +0 -0
  166. bootstack/assets/widgets/notebook-tab-normal.png +0 -0
  167. bootstack/assets/widgets/notebook-underline.png +0 -0
  168. bootstack/assets/widgets/progress-bar-horizontal-default.png +0 -0
  169. bootstack/assets/widgets/progress-bar-horizontal-striped.png +0 -0
  170. bootstack/assets/widgets/progress-bar-vertical-default.png +0 -0
  171. bootstack/assets/widgets/progress-bar-vertical-striped.png +0 -0
  172. bootstack/assets/widgets/progress-trough-horizontal.png +0 -0
  173. bootstack/assets/widgets/progress-trough-vertical.png +0 -0
  174. bootstack/assets/widgets/radio-selected.png +0 -0
  175. bootstack/assets/widgets/radio-unselected.png +0 -0
  176. bootstack/assets/widgets/scrollbar-horizontal-rounded.png +0 -0
  177. bootstack/assets/widgets/scrollbar-vertical-rounded.png +0 -0
  178. bootstack/assets/widgets/separator-horizontal.png +0 -0
  179. bootstack/assets/widgets/separator-vertical.png +0 -0
  180. bootstack/assets/widgets/slider-handle-focus.png +0 -0
  181. bootstack/assets/widgets/slider-handle.png +0 -0
  182. bootstack/assets/widgets/slider-track-horizontal.png +0 -0
  183. bootstack/assets/widgets/slider-track-vertical.png +0 -0
  184. bootstack/assets/widgets/switch-off.png +0 -0
  185. bootstack/assets/widgets/switch-on.png +0 -0
  186. bootstack/assets/widgets/tabs-bar-horizontal.png +0 -0
  187. bootstack/assets/widgets/tabs-bar-vertical.png +0 -0
  188. bootstack/assets/widgets/tabs-pill.png +0 -0
  189. bootstack/cli/__init__.py +124 -0
  190. bootstack/cli/__main__.py +6 -0
  191. bootstack/cli/add.py +439 -0
  192. bootstack/cli/build.py +115 -0
  193. bootstack/cli/config.py +287 -0
  194. bootstack/cli/demo.py +1267 -0
  195. bootstack/cli/doctor.py +195 -0
  196. bootstack/cli/list_cmd.py +71 -0
  197. bootstack/cli/promote.py +120 -0
  198. bootstack/cli/pyinstaller.py +246 -0
  199. bootstack/cli/run.py +99 -0
  200. bootstack/cli/start.py +105 -0
  201. bootstack/cli/templates/__init__.py +861 -0
  202. bootstack/constants.py +325 -0
  203. bootstack/core/__init__.py +34 -0
  204. bootstack/core/capabilities/__init__.py +45 -0
  205. bootstack/core/capabilities/after.py +103 -0
  206. bootstack/core/capabilities/bind.py +154 -0
  207. bootstack/core/capabilities/bindtags.py +112 -0
  208. bootstack/core/capabilities/busy.py +61 -0
  209. bootstack/core/capabilities/clipboard.py +88 -0
  210. bootstack/core/capabilities/focus.py +118 -0
  211. bootstack/core/capabilities/grab.py +65 -0
  212. bootstack/core/capabilities/grid.py +188 -0
  213. bootstack/core/capabilities/localization.py +231 -0
  214. bootstack/core/capabilities/pack.py +119 -0
  215. bootstack/core/capabilities/place.py +92 -0
  216. bootstack/core/capabilities/selection.py +136 -0
  217. bootstack/core/capabilities/signals.py +242 -0
  218. bootstack/core/capabilities/winfo.py +315 -0
  219. bootstack/core/colorutils.py +234 -0
  220. bootstack/core/exceptions.py +95 -0
  221. bootstack/core/images.py +283 -0
  222. bootstack/core/localization/README.md +90 -0
  223. bootstack/core/localization/__init__.py +13 -0
  224. bootstack/core/localization/intl_format.py +580 -0
  225. bootstack/core/localization/msgcat.py +425 -0
  226. bootstack/core/localization/specs.py +143 -0
  227. bootstack/core/mixins/__init__.py +1 -0
  228. bootstack/core/mixins/ttk_state.py +35 -0
  229. bootstack/core/mixins/widget.py +132 -0
  230. bootstack/core/publisher.py +147 -0
  231. bootstack/core/signals/README.md +112 -0
  232. bootstack/core/signals/__init__.py +8 -0
  233. bootstack/core/signals/integration.py +100 -0
  234. bootstack/core/signals/signal.py +317 -0
  235. bootstack/core/signals/types.py +4 -0
  236. bootstack/core/validation/__init__.py +5 -0
  237. bootstack/core/validation/types.py +13 -0
  238. bootstack/core/validation/validation_result.py +17 -0
  239. bootstack/core/validation/validation_rules.py +112 -0
  240. bootstack/core/variables.py +62 -0
  241. bootstack/datasource/README.md +607 -0
  242. bootstack/datasource/__init__.py +51 -0
  243. bootstack/datasource/base.py +474 -0
  244. bootstack/datasource/file_source.py +541 -0
  245. bootstack/datasource/memory_source.py +482 -0
  246. bootstack/datasource/sqlite_source.py +453 -0
  247. bootstack/datasource/types.py +259 -0
  248. bootstack/dialogs/__init__.py +56 -0
  249. bootstack/dialogs/colorchooser.py +674 -0
  250. bootstack/dialogs/colordropper.py +257 -0
  251. bootstack/dialogs/datedialog.py +404 -0
  252. bootstack/dialogs/dialog.py +514 -0
  253. bootstack/dialogs/filterdialog.py +358 -0
  254. bootstack/dialogs/fontdialog.py +339 -0
  255. bootstack/dialogs/formdialog.py +541 -0
  256. bootstack/dialogs/message.py +489 -0
  257. bootstack/dialogs/query.py +561 -0
  258. bootstack/py.typed +1 -0
  259. bootstack/runtime/__init__.py +3 -0
  260. bootstack/runtime/app.py +879 -0
  261. bootstack/runtime/base_window.py +786 -0
  262. bootstack/runtime/events.py +399 -0
  263. bootstack/runtime/menu.py +510 -0
  264. bootstack/runtime/shortcuts.py +423 -0
  265. bootstack/runtime/tk_patch.py +31 -0
  266. bootstack/runtime/toplevel.py +131 -0
  267. bootstack/runtime/utility.py +371 -0
  268. bootstack/runtime/visual_focus.py +228 -0
  269. bootstack/runtime/window_utilities.py +1043 -0
  270. bootstack/style/__init__.py +5498 -0
  271. bootstack/style/bootstyle.py +507 -0
  272. bootstack/style/bootstyle_builder_base.py +752 -0
  273. bootstack/style/bootstyle_builder_mixed.py +93 -0
  274. bootstack/style/bootstyle_builder_tk.py +109 -0
  275. bootstack/style/bootstyle_builder_ttk.py +354 -0
  276. bootstack/style/builders/__init__.py +51 -0
  277. bootstack/style/builders/badge.py +44 -0
  278. bootstack/style/builders/button.py +453 -0
  279. bootstack/style/builders/buttongroup.py +344 -0
  280. bootstack/style/builders/calendar.py +271 -0
  281. bootstack/style/builders/checkbutton.py +95 -0
  282. bootstack/style/builders/combobox.py +112 -0
  283. bootstack/style/builders/contextmenu.py +268 -0
  284. bootstack/style/builders/entry.py +83 -0
  285. bootstack/style/builders/expander.py +171 -0
  286. bootstack/style/builders/field.py +312 -0
  287. bootstack/style/builders/frame.py +27 -0
  288. bootstack/style/builders/label.py +28 -0
  289. bootstack/style/builders/labelframe.py +41 -0
  290. bootstack/style/builders/listview.py +267 -0
  291. bootstack/style/builders/menubar.py +74 -0
  292. bootstack/style/builders/menubutton.py +408 -0
  293. bootstack/style/builders/notebook.py +316 -0
  294. bootstack/style/builders/panedwindow.py +25 -0
  295. bootstack/style/builders/progressbar.py +71 -0
  296. bootstack/style/builders/radiobutton.py +68 -0
  297. bootstack/style/builders/scale.py +66 -0
  298. bootstack/style/builders/scrollbar.py +360 -0
  299. bootstack/style/builders/separator.py +45 -0
  300. bootstack/style/builders/sidenav.py +313 -0
  301. bootstack/style/builders/sizegrip.py +15 -0
  302. bootstack/style/builders/spinbox.py +119 -0
  303. bootstack/style/builders/switch.py +67 -0
  304. bootstack/style/builders/tabitem.py +205 -0
  305. bootstack/style/builders/toolbutton.py +260 -0
  306. bootstack/style/builders/tooltip.py +26 -0
  307. bootstack/style/builders/treeview.py +269 -0
  308. bootstack/style/builders/utils.py +404 -0
  309. bootstack/style/builders_tk/__init__.py +16 -0
  310. bootstack/style/builders_tk/defaults.py +229 -0
  311. bootstack/style/element.py +173 -0
  312. bootstack/style/style.py +499 -0
  313. bootstack/style/theme_provider.py +449 -0
  314. bootstack/style/tk_patch.py +5 -0
  315. bootstack/style/token_maps.py +42 -0
  316. bootstack/style/types.py +32 -0
  317. bootstack/style/typography.py +527 -0
  318. bootstack/style/utility.py +696 -0
  319. bootstack/themes/__init__.py +12 -0
  320. bootstack/themes/standard.py +415 -0
  321. bootstack/themes/user.py +45 -0
  322. bootstack/widgets/__init__.py +53 -0
  323. bootstack/widgets/composites/__init__.py +38 -0
  324. bootstack/widgets/composites/accordion.py +385 -0
  325. bootstack/widgets/composites/appshell.py +445 -0
  326. bootstack/widgets/composites/buttongroup.py +391 -0
  327. bootstack/widgets/composites/calendar.py +914 -0
  328. bootstack/widgets/composites/compositeframe.py +282 -0
  329. bootstack/widgets/composites/contextmenu.py +1754 -0
  330. bootstack/widgets/composites/dateentry.py +261 -0
  331. bootstack/widgets/composites/dropdownbutton.py +190 -0
  332. bootstack/widgets/composites/expander.py +508 -0
  333. bootstack/widgets/composites/field.py +448 -0
  334. bootstack/widgets/composites/floodgauge.py +434 -0
  335. bootstack/widgets/composites/form.py +983 -0
  336. bootstack/widgets/composites/labeledscale.py +209 -0
  337. bootstack/widgets/composites/list/__init__.py +15 -0
  338. bootstack/widgets/composites/list/listitem.py +733 -0
  339. bootstack/widgets/composites/list/listview.py +1507 -0
  340. bootstack/widgets/composites/menubar.py +303 -0
  341. bootstack/widgets/composites/meter.py +882 -0
  342. bootstack/widgets/composites/numericentry.py +183 -0
  343. bootstack/widgets/composites/pagestack.py +330 -0
  344. bootstack/widgets/composites/passwordentry.py +149 -0
  345. bootstack/widgets/composites/pathentry.py +223 -0
  346. bootstack/widgets/composites/radiogroup.py +466 -0
  347. bootstack/widgets/composites/scrolledtext.py +388 -0
  348. bootstack/widgets/composites/scrolledtext.pyi +186 -0
  349. bootstack/widgets/composites/scrollview.py +675 -0
  350. bootstack/widgets/composites/selectbox.py +544 -0
  351. bootstack/widgets/composites/sidenav/__init__.py +24 -0
  352. bootstack/widgets/composites/sidenav/group.py +485 -0
  353. bootstack/widgets/composites/sidenav/header.py +83 -0
  354. bootstack/widgets/composites/sidenav/item.py +413 -0
  355. bootstack/widgets/composites/sidenav/separator.py +51 -0
  356. bootstack/widgets/composites/sidenav/view.py +919 -0
  357. bootstack/widgets/composites/spinnerentry.py +232 -0
  358. bootstack/widgets/composites/tableview/__init__.py +5 -0
  359. bootstack/widgets/composites/tableview/tableview.py +2254 -0
  360. bootstack/widgets/composites/tableview/types.py +169 -0
  361. bootstack/widgets/composites/tabs/__init__.py +6 -0
  362. bootstack/widgets/composites/tabs/tabitem.py +372 -0
  363. bootstack/widgets/composites/tabs/tabs.py +478 -0
  364. bootstack/widgets/composites/tabs/tabview.py +352 -0
  365. bootstack/widgets/composites/textentry.py +90 -0
  366. bootstack/widgets/composites/timeentry.py +189 -0
  367. bootstack/widgets/composites/toast.py +364 -0
  368. bootstack/widgets/composites/togglegroup.py +382 -0
  369. bootstack/widgets/composites/toolbar.py +393 -0
  370. bootstack/widgets/composites/tooltip.py +404 -0
  371. bootstack/widgets/internal/__init__.py +0 -0
  372. bootstack/widgets/internal/wrapper_base.py +304 -0
  373. bootstack/widgets/mixins/__init__.py +25 -0
  374. bootstack/widgets/mixins/configure_mixin.py +186 -0
  375. bootstack/widgets/mixins/entry_mixin.py +70 -0
  376. bootstack/widgets/mixins/font_mixin.py +346 -0
  377. bootstack/widgets/mixins/icon_mixin.py +38 -0
  378. bootstack/widgets/mixins/localization_mixin.py +255 -0
  379. bootstack/widgets/mixins/signal_mixin.py +272 -0
  380. bootstack/widgets/mixins/validation_mixin.py +204 -0
  381. bootstack/widgets/parts/__init__.py +11 -0
  382. bootstack/widgets/parts/numberentry_part.py +345 -0
  383. bootstack/widgets/parts/spinnerentry_part.py +394 -0
  384. bootstack/widgets/parts/textentry_part.py +344 -0
  385. bootstack/widgets/primitives/__init__.py +55 -0
  386. bootstack/widgets/primitives/badge.py +44 -0
  387. bootstack/widgets/primitives/button.py +89 -0
  388. bootstack/widgets/primitives/card.py +66 -0
  389. bootstack/widgets/primitives/checkbutton.py +124 -0
  390. bootstack/widgets/primitives/checktoggle.py +53 -0
  391. bootstack/widgets/primitives/combobox.py +165 -0
  392. bootstack/widgets/primitives/entry.py +98 -0
  393. bootstack/widgets/primitives/frame.py +206 -0
  394. bootstack/widgets/primitives/gridframe.py +479 -0
  395. bootstack/widgets/primitives/label.py +95 -0
  396. bootstack/widgets/primitives/labelframe.py +63 -0
  397. bootstack/widgets/primitives/menubutton.py +118 -0
  398. bootstack/widgets/primitives/notebook.py +551 -0
  399. bootstack/widgets/primitives/optionmenu.py +248 -0
  400. bootstack/widgets/primitives/packframe.py +228 -0
  401. bootstack/widgets/primitives/panedwindow.py +58 -0
  402. bootstack/widgets/primitives/progressbar.py +95 -0
  403. bootstack/widgets/primitives/radiobutton.py +115 -0
  404. bootstack/widgets/primitives/radiotoggle.py +50 -0
  405. bootstack/widgets/primitives/scale.py +85 -0
  406. bootstack/widgets/primitives/scrollbar.py +56 -0
  407. bootstack/widgets/primitives/separator.py +56 -0
  408. bootstack/widgets/primitives/sizegrip.py +47 -0
  409. bootstack/widgets/primitives/spinbox.py +91 -0
  410. bootstack/widgets/primitives/switch.py +41 -0
  411. bootstack/widgets/primitives/treeview.py +77 -0
  412. bootstack/widgets/types.py +20 -0
  413. bootstack-0.1.0a1.dist-info/METADATA +196 -0
  414. bootstack-0.1.0a1.dist-info/RECORD +419 -0
  415. bootstack-0.1.0a1.dist-info/WHEEL +5 -0
  416. bootstack-0.1.0a1.dist-info/entry_points.txt +2 -0
  417. bootstack-0.1.0a1.dist-info/licenses/LICENSE +22 -0
  418. bootstack-0.1.0a1.dist-info/licenses/NOTICE +10 -0
  419. bootstack-0.1.0a1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,449 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ # import tomllib
5
+ from importlib import resources
6
+
7
+ from bootstack.runtime.app import get_app_settings
8
+ from bootstack.core.exceptions import ThemeError
9
+ from bootstack.style.utility import (
10
+ shade_color, tint_color, color_to_hsl, hsl_to_hex, best_foreground
11
+ )
12
+
13
+ _registered_themes = {}
14
+ _current_theme = None
15
+
16
+ # Weights for generating color spectrum tints (toward white) and shades (toward black)
17
+ # Each weight represents how much of the original color to retain when mixing
18
+ # Full 50-step increments from 50-450 (tints) and 550-950 (shades)
19
+ TINT_WEIGHTS = {
20
+ 50: 0.90,
21
+ 100: 0.80,
22
+ 150: 0.70,
23
+ 200: 0.60,
24
+ 250: 0.50,
25
+ 300: 0.40,
26
+ 350: 0.325,
27
+ 400: 0.25,
28
+ 450: 0.125,
29
+ }
30
+ SHADE_WEIGHTS = {
31
+ 550: 0.125,
32
+ 600: 0.25,
33
+ 650: 0.325,
34
+ 700: 0.40,
35
+ 750: 0.50,
36
+ 800: 0.60,
37
+ 850: 0.725,
38
+ 900: 0.85,
39
+ 950: 0.95,
40
+ }
41
+
42
+
43
+ def register_user_theme(name, path):
44
+ data = load_user_defined_theme(path)
45
+ _registered_themes[name] = data
46
+
47
+
48
+ def get_theme(name):
49
+ """Return a registered theme by name.
50
+
51
+ If the theme is not currently registered this will re-run
52
+ `load_system_themes` once to pick up any newly added themes before
53
+ failing.
54
+ """
55
+ if name in _registered_themes:
56
+ return _registered_themes[name]
57
+
58
+ # Lazy fallback: configuration (e.g., load_all_themes/include_legacy_themes)
59
+ # may have changed after the provider singleton was first initialized.
60
+ # Re-run the loader once to pick up any additional themes.
61
+ try:
62
+ load_system_themes()
63
+ if name in _registered_themes:
64
+ return _registered_themes[name]
65
+ except Exception:
66
+ # If anything goes wrong here, fall through to the ThemeError.
67
+ pass
68
+
69
+ # Build a helpful error message listing available themes
70
+ available = sorted(
71
+ {
72
+ data.get("name")
73
+ for data in _registered_themes.values()
74
+ if isinstance(data, dict) and data.get("name")
75
+ }
76
+ )
77
+ available_str = ", ".join(available) if available else "<none>"
78
+ raise ThemeError(
79
+ f"Theme '{name}' is not registered. "
80
+ f"Registered themes: {available_str}"
81
+ )
82
+
83
+
84
+ def load_system_themes():
85
+ """Load system themes from the package.
86
+
87
+ Loads all v2 themes from `bootstack.assets.themes`. Themes matching
88
+ app settings dark_theme or light_theme are also registered with
89
+ 'dark' and 'light' aliases for convenience.
90
+ """
91
+ from importlib import resources
92
+
93
+ global _registered_themes
94
+
95
+ base_package = 'bootstack.assets.themes'
96
+
97
+ # Get configured theme names for dark/light aliases
98
+ app_settings = get_app_settings()
99
+ dark_theme_name = app_settings.dark_theme
100
+ light_theme_name = app_settings.light_theme
101
+
102
+ try:
103
+ base_dir = resources.files(base_package)
104
+ except ModuleNotFoundError:
105
+ base_dir = None
106
+ if base_dir is not None:
107
+ for theme_file in base_dir.iterdir():
108
+ if not theme_file.name.endswith(".json"):
109
+ continue
110
+ data = load_package_theme(theme_file.name, base_package)
111
+ name = data.get("name")
112
+ if not name:
113
+ continue
114
+ _registered_themes[name] = data
115
+
116
+ # Register aliases for dark/light themes
117
+ if name == dark_theme_name:
118
+ _registered_themes['dark'] = data
119
+ elif name == light_theme_name:
120
+ _registered_themes['light'] = data
121
+
122
+
123
+ def load_user_defined_theme(path):
124
+ """Load a user-defined theme from a JSON file on disk.
125
+
126
+ The file must follow the v2 theme schema (top-level `name`,
127
+ `display_name`, `mode`, `foreground`, `background`, plus
128
+ `shades` and `semantic` mappings).
129
+ """
130
+ with open(path, "r", encoding="utf-8") as f:
131
+ return json.load(f)
132
+
133
+
134
+ def load_package_theme(filename: str, package="bootstack.assets.themes"):
135
+ with resources.files(package).joinpath(filename).open("r", encoding="utf-8") as f:
136
+ return json.load(f)
137
+
138
+
139
+ def color_spectrum(token, value):
140
+ """Generate a color spectrum with 50-step increments from 50-950.
141
+
142
+ Creates tints (lighter) and shades (darker) of the base color:
143
+ - 50-450: Tints toward white (50 is lightest)
144
+ - 500: Base color
145
+ - 550-950: Shades toward black (950 is darkest)
146
+
147
+ Args:
148
+ token: The color token name (e.g., 'gray', 'blue')
149
+ value: The base hex color value
150
+
151
+ Returns:
152
+ Dict mapping spectrum names to hex colors
153
+ """
154
+ result = {}
155
+
156
+ # Generate tints (50-450)
157
+ for stop, weight in TINT_WEIGHTS.items():
158
+ result[f'{token}[{stop}]'] = tint_color(value, weight)
159
+
160
+ # Base color (500)
161
+ result[f'{token}[500]'] = value
162
+
163
+ # Generate shades (550-950)
164
+ for stop, weight in SHADE_WEIGHTS.items():
165
+ result[f'{token}[{stop}]'] = shade_color(value, weight)
166
+
167
+ return result
168
+
169
+
170
+ class ThemeProvider:
171
+ """Theme data provider with singleton access and helpers.
172
+
173
+ Mirrors the pattern used by Style/use_style():
174
+ - `ThemeProvider()` returns the global instance (singleton)
175
+ - `use_theme(name)` returns/initializes the singleton with optional theme
176
+ - `ThemeProvider.instance(name)` remains for backward compatibility
177
+ """
178
+
179
+ # Class-level global singleton instance
180
+ _instance: ThemeProvider | None = None
181
+
182
+ def __new__(cls, *args, **kwargs):
183
+ """Ensure ThemeProvider() always returns the global singleton instance."""
184
+ if cls._instance is None:
185
+ cls._instance = super().__new__(cls)
186
+ return cls._instance
187
+
188
+ def __init__(self, name: str = "dark"):
189
+ # Prevent reinitialization on subsequent ThemeProvider() calls
190
+ if getattr(self, "_initialized", False):
191
+ if name and name != self.name:
192
+ self.use(name)
193
+ return
194
+
195
+ self._theme = {}
196
+ self._colors = {}
197
+ load_system_themes()
198
+
199
+ from bootstack.style.typography import Typography
200
+ Typography.initialize()
201
+
202
+ self.use(name)
203
+ self._initialized = True
204
+
205
+ # ----- Theme metadata helpers -------------------------------------------------
206
+
207
+ def list_themes(self) -> list[dict[str, str]]:
208
+ """Return a list of available themes with names and display names.
209
+
210
+ The result is a list of dictionaries in the form:
211
+
212
+ ```python
213
+ {"name": "bootstrap-light", "display_name": "Bootstrap Light"}
214
+ ```
215
+
216
+ Aliases such as `\"light\"` and `\"dark\"` are not included; only the
217
+ canonical theme entries loaded into the provider are returned.
218
+ """
219
+ themes: list[dict[str, str]] = []
220
+ seen: set[str] = set()
221
+
222
+ for key, data in _registered_themes.items():
223
+ # Skip alias keys that point at an existing theme object
224
+ name = data.get("name")
225
+ if not name:
226
+ continue
227
+ if key != name and name in _registered_themes:
228
+ # This is an alias like 'light' or 'dark'
229
+ continue
230
+ if name in seen:
231
+ continue
232
+ seen.add(name)
233
+ themes.append(
234
+ {
235
+ "name": name,
236
+ "display_name": data.get("display_name", name),
237
+ }
238
+ )
239
+
240
+ # If the application has declared a specific set/order of themes
241
+ # to expose, filter and order by that list.
242
+ select_themes = get_app_settings().available_themes
243
+ if select_themes:
244
+ by_name = {t["name"]: t for t in themes}
245
+ ordered: list[dict[str, str]] = []
246
+ for name in select_themes:
247
+ t = by_name.get(name)
248
+ if t is not None:
249
+ ordered.append(t)
250
+ return ordered
251
+
252
+ # Otherwise, sort for stable UI ordering (by display name, then name)
253
+ themes.sort(key=lambda t: (t["display_name"].lower(), t["name"].lower()))
254
+ return themes
255
+
256
+ def use(self, name):
257
+ self._theme = get_theme(name)
258
+ self.build_theme_colors()
259
+
260
+ @property
261
+ def raw(self):
262
+ """Return the raw source dictionary"""
263
+ return self._theme
264
+
265
+ def build_theme_colors(self):
266
+ colors = {}
267
+ colors.update(
268
+ foreground=self.raw.get('foreground'),
269
+ background=self.raw.get('background'),
270
+ white=self.raw.get('white'),
271
+ black=self.raw.get('black'),
272
+ **self._shades,
273
+ )
274
+ # add shaded spectrum
275
+ for color, value in self._shades.items():
276
+ colors.update(**color_spectrum(color, value))
277
+
278
+ # semantic tokens
279
+ neutral_tokens = {
280
+ "foreground",
281
+ "muted",
282
+ "muted_alt",
283
+ "subtle",
284
+ "border",
285
+ "border_subtle",
286
+ }
287
+ for token, value in self._semantic.items():
288
+ # Neutral roles are derived elsewhere from the top-level
289
+ # foreground/background and should not be overridden here.
290
+ if token in neutral_tokens:
291
+ continue
292
+ colors[token] = colors[value]
293
+
294
+ # Surface tokens - semantic ramps for container backgrounds
295
+ self._build_surface_tokens(colors)
296
+
297
+ self._colors.clear()
298
+ self._colors.update(**colors)
299
+
300
+ def _build_surface_tokens(self, colors: dict):
301
+ """Build semantic surface tokens for container backgrounds.
302
+
303
+ Surface tokens provide deterministic, theme-defined backgrounds
304
+ that don't rely on "background +1 math" for elevation. The hue
305
+ and saturation are derived from the theme's background color to
306
+ ensure consistent tinting across all surfaces.
307
+
308
+ Token families:
309
+ - chrome: UI shell (sidebars, toolbars, navigation)
310
+ - content: Main content area background (= theme background)
311
+ - card: Elevated content (cards, panels)
312
+ - overlay: Floating elements (menus, dialogs, tooltips)
313
+ - input: Form control backgrounds
314
+ """
315
+ is_dark = self.mode == 'dark'
316
+ bg = colors['background']
317
+ fg = colors['foreground']
318
+
319
+ # Extract hue and saturation from background for tinting
320
+ hue, sat, bg_lightness = color_to_hsl(bg, model='hex')
321
+
322
+ # Cap saturation for subtle tinting (avoid garish surfaces)
323
+ # Use 0 saturation for near-neutral backgrounds
324
+ tint_sat = min(sat, 25) if sat >= 5 else 0
325
+
326
+ def tinted_surface(lightness: float) -> str:
327
+ """Generate a surface color with the theme's tint at given lightness."""
328
+ if tint_sat == 0:
329
+ # No tint - pure neutral grey
330
+ return hsl_to_hex(0, 0, lightness)
331
+ return hsl_to_hex(hue, tint_sat, lightness)
332
+
333
+ # Define surface lightness levels for each mode
334
+ # content = theme background (as defined in JSON)
335
+ # Other surfaces are relative to it
336
+ if is_dark:
337
+ # Dark mode: lower lightness = darker/recessed
338
+ surfaces = {
339
+ 'chrome': tinted_surface(max(bg_lightness - 3, 3)), # Darker than content
340
+ 'content': bg, # Theme background
341
+ 'card': tinted_surface(min(bg_lightness + 4, 20)), # Elevated cards
342
+ 'overlay': tinted_surface(min(bg_lightness + 7, 25)), # Menus, dialogs
343
+ 'input': tinted_surface(max(bg_lightness - 5, 2)), # Recessed inputs
344
+ }
345
+ # Stroke colors for borders
346
+ colors['stroke'] = tinted_surface(min(bg_lightness + 12, 30))
347
+ colors['stroke_subtle'] = tinted_surface(min(bg_lightness + 6, 22))
348
+ else:
349
+ # Light mode: use subtle darkening for elevation (since bg is often near-white)
350
+ surfaces = {
351
+ 'chrome': tinted_surface(max(bg_lightness - 8, 88)), # Noticeably darker
352
+ 'content': bg, # Theme background
353
+ 'card': tinted_surface(max(bg_lightness - 4, 92)), # Slightly darker for contrast
354
+ 'overlay': tinted_surface(max(bg_lightness - 2, 96)), # Subtle for popups
355
+ 'input': tinted_surface(max(bg_lightness - 5, 90)), # Recessed inputs
356
+ }
357
+ # Stroke colors for borders
358
+ colors['stroke'] = tinted_surface(max(bg_lightness - 20, 70))
359
+ colors['stroke_subtle'] = tinted_surface(max(bg_lightness - 10, 80))
360
+
361
+ # Add surfaces to colors
362
+ for name, value in surfaces.items():
363
+ colors[name] = value
364
+
365
+ # Pre-compute foreground colors for each surface
366
+ candidates = [fg, '#ffffff', '#000000']
367
+ for name, value in surfaces.items():
368
+ colors[f'on_{name}'] = best_foreground(value, candidates)
369
+
370
+ # Secondary/muted foreground for each surface (reduced contrast)
371
+ for name, value in surfaces.items():
372
+ # Generate a muted foreground by mixing fg with the surface
373
+ on_color = colors[f'on_{name}']
374
+ if on_color in ('#ffffff', '#FFFFFF'):
375
+ colors[f'on_{name}_secondary'] = tinted_surface(65) if is_dark else tinted_surface(45)
376
+ else:
377
+ colors[f'on_{name}_secondary'] = tinted_surface(35) if is_dark else tinted_surface(55)
378
+
379
+ # Hover states for each surface (subtle highlight)
380
+ for name in surfaces.keys():
381
+ if name == 'content':
382
+ # Content hover: slightly elevated
383
+ hover_l = min(bg_lightness + 5, 25) if is_dark else max(bg_lightness - 5, 90)
384
+ else:
385
+ # Other surfaces: shift toward content
386
+ surface_h, surface_s, surface_l = color_to_hsl(colors[name], model='hex')
387
+ hover_l = min(surface_l + 5, 30) if is_dark else max(surface_l - 5, 85)
388
+ colors[f'{name}_hover'] = tinted_surface(hover_l)
389
+
390
+ @property
391
+ def name(self):
392
+ """The name of the theme"""
393
+ return self.raw.get('name')
394
+
395
+ @property
396
+ def display_name(self):
397
+ """The display name of the theme"""
398
+ return self.raw.get('display_name')
399
+
400
+ @property
401
+ def mode(self):
402
+ """Returns the color mode 'light' or 'dark'"""
403
+ return self.raw.get('mode')
404
+
405
+ @property
406
+ def colors(self):
407
+ return self._colors
408
+
409
+ @property
410
+ def typography(self):
411
+ """Returns the current typography configuration as FontTokens"""
412
+ from bootstack.style.typography import Typography
413
+ return Typography.all()
414
+
415
+ @property
416
+ def _shades(self):
417
+ return self.raw.get('shades')
418
+
419
+ @property
420
+ def _semantic(self):
421
+ return self.raw.get('semantic')
422
+
423
+ def __repr__(self):
424
+ """Return a string representation of the current theme"""
425
+ return f"<Theme name={self.name} mode={self.mode}>"
426
+
427
+
428
+ def use_theme(name: str = None) -> ThemeProvider:
429
+ """Return the global ThemeProvider singleton instance.
430
+
431
+ Convenience helper that mirrors `use_style()` so callers can obtain
432
+ the current ThemeProvider without handling singleton state.
433
+
434
+ Args:
435
+ name: Optional theme name to switch to, or None to return current instance.
436
+
437
+ Returns:
438
+ Global ThemeProvider instance.
439
+ """
440
+ # If instance doesn't exist yet, create it with default theme
441
+ if ThemeProvider._instance is None:
442
+ return ThemeProvider(name or "dark")
443
+
444
+ # If name is provided and different, switch themes
445
+ if name is not None and name != ThemeProvider._instance.name:
446
+ ThemeProvider._instance.use(name)
447
+
448
+ # Return the singleton
449
+ return ThemeProvider._instance
@@ -0,0 +1,5 @@
1
+ """Shim so style imports keep working while runtime owns tk_patch."""
2
+
3
+ from bootstack.runtime.tk_patch import install_tk_autostyle
4
+
5
+ __all__ = ["install_tk_autostyle"]
@@ -0,0 +1,42 @@
1
+ from __future__ import annotations
2
+
3
+ COLOR_TOKENS = {
4
+ 'primary', 'secondary', 'success', 'info',
5
+ 'warning', 'danger', 'light', 'dark',
6
+ 'foreground', 'background', 'white', 'black',
7
+ 'blue', 'indigo', 'purple', 'red', 'orange',
8
+ 'yellow', 'green', 'teal', 'cyan', 'gray',
9
+ 'border'
10
+ }
11
+
12
+ WIDGET_CLASS_MAP = {
13
+ 'badge': 'TBadge',
14
+ 'button': 'TButton',
15
+ 'label': 'TLabel',
16
+ 'entry': 'TEntry',
17
+ 'frame': 'TFrame',
18
+ 'labelframe': 'TLabelframe',
19
+ 'progressbar': 'TProgressbar',
20
+ 'scale': 'TScale',
21
+ 'scrollbar': 'TScrollbar',
22
+ 'checkbutton': 'TCheckbutton',
23
+ 'radiobutton': 'TRadiobutton',
24
+ 'combobox': 'TCombobox',
25
+ 'notebook': 'TNotebook',
26
+ 'treeview': 'Treeview',
27
+ 'separator': 'TSeparator',
28
+ 'sizegrip': 'TSizegrip',
29
+ 'panedwindow': 'TPanedwindow',
30
+ 'spinbox': 'TSpinbox',
31
+ 'menubutton': 'TMenubutton',
32
+ 'field': 'TField',
33
+ 'toolbutton': 'Toolbutton',
34
+ 'tooltip': 'Tooltip',
35
+ 'calendar': 'TCalendar',
36
+ 'buttongroup': 'ButtonGroup'
37
+ }
38
+
39
+ WIDGET_NAME_MAP = {v: k for k, v in WIDGET_CLASS_MAP.items()}
40
+ CONTAINER_CLASSES = {'TFrame', 'TLabelframe'}
41
+ ORIENT_CLASSES = {'TProgressbar', 'TScale', 'TScrollbar', 'TPanedwindow', 'TSeparator'}
42
+ ICON_CLASSES = {'TLabel', 'TButton', 'TCheckbutton', 'TRadiobutton', 'TMenubutton'}
@@ -0,0 +1,32 @@
1
+ from tkinter.font import Font
2
+ from typing import Literal, Union
3
+
4
+ ColorShade = Literal[100, 200, 300, 400, 500, 600, 700, 800, 900]
5
+ ColorMode = Union[Literal['light', 'dark'], str]
6
+ ColorModel = Literal['hex', 'hsl', 'rgb']
7
+
8
+ # === Color Types ===
9
+
10
+ # supports subtle variants, e.g., 'primary[subtle]'
11
+ SemanticColor = Literal['primary', 'secondary', 'success', 'info', 'warning', 'danger', 'light', 'dark']
12
+ UtilityColor = Literal['foreground', 'background']
13
+
14
+ # supports shade variants 100-900, e.g. `blue[300]`
15
+ ShadeColor = Literal['blue', 'indigo', 'purple', 'red', 'orange', 'yellow', 'green', 'teal', 'cyan', 'gray']
16
+
17
+ # === Color Tokens ===
18
+
19
+ SurfaceColor = Union[SemanticColor, str]
20
+ ForegroundColor = Union[SemanticColor, ShadeColor, UtilityColor, str]
21
+ ThemeColor = Union[SemanticColor, ShadeColor, UtilityColor, str]
22
+ SeparatorColor = Union[Literal['border'], SemanticColor]
23
+ BorderColor = Union[Literal['border'], SemanticColor, ShadeColor, str]
24
+
25
+ # === Font Tokens ====
26
+
27
+ BootstrapFontType = Literal[
28
+ 'label', 'body', 'body-sm', 'body-lg', 'body-xl', 'caption',
29
+ 'display-xl', 'display-lg', 'heading-xl', 'heading-lg', 'heading-md', 'code'
30
+ ]
31
+
32
+ TypographyToken = Union[BootstrapFontType, str, Font]