bootstack 0.1.0a15__tar.gz → 0.1.1__tar.gz
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.
- {bootstack-0.1.0a15/src/bootstack.egg-info → bootstack-0.1.1}/PKG-INFO +23 -9
- {bootstack-0.1.0a15 → bootstack-0.1.1}/README.md +17 -6
- {bootstack-0.1.0a15 → bootstack-0.1.1}/pyproject.toml +9 -4
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/__init__.py +2 -1
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_runtime/app.py +11 -1
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_runtime/toplevel.py +23 -3
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_runtime/utility.py +21 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_runtime/window_utilities.py +73 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/cli/__init__.py +4 -1
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/cli/add.py +11 -4
- bootstack-0.1.1/src/bootstack/cli/dev.py +153 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/cli/run.py +1 -1
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/cli/templates/__init__.py +162 -193
- bootstack-0.1.1/src/bootstack/dev/__init__.py +36 -0
- bootstack-0.1.1/src/bootstack/dev/_capture.py +141 -0
- bootstack-0.1.1/src/bootstack/dev/_env.py +43 -0
- bootstack-0.1.1/src/bootstack/dev/_registry.py +140 -0
- bootstack-0.1.1/src/bootstack/dev/_reloader.py +351 -0
- bootstack-0.1.1/src/bootstack/dev/_reset.py +50 -0
- bootstack-0.1.1/src/bootstack/dev/_watcher.py +91 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/dialogs/_impl/dialog.py +24 -8
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/events/__init__.py +5 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/events/_payloads.py +18 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/images.py +3 -2
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/menubutton.py +3 -1
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/utils.py +12 -3
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/style.py +79 -51
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/style_builder_base.py +29 -6
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/style_resolver.py +10 -6
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/__init__.py +8 -13
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_core/base.py +34 -2
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_core/window_menu.py +16 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/avatar.py +1 -2
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/carousel.py +1 -2
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/chart.py +1 -2
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/expander.py +7 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/gallery.py +1 -2
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/list/listview.py +1 -2
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/meter.py +13 -15
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/pagestack.py +49 -5
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/picture.py +1 -2
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/shell/nav_panel.py +3 -1
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/shell/shell.py +48 -6
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/slider/_shared.py +12 -4
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/slider/rangeslider.py +18 -14
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/slider/slider.py +18 -14
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/tableview/tableview.py +23 -8
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/toast_stack.py +9 -4
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/tooltip.py +15 -1
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/combobox.py +9 -8
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/frame.py +4 -52
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/app.py +111 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/appshell.py +134 -1
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/button.py +1 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/label.py +1 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/menubutton.py +1 -0
- bootstack-0.1.1/src/bootstack/widgets/splash.py +367 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/tooltip.py +12 -2
- {bootstack-0.1.0a15 → bootstack-0.1.1/src/bootstack.egg-info}/PKG-INFO +23 -9
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack.egg-info/SOURCES.txt +13 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack.egg-info/requires.txt +2 -0
- bootstack-0.1.1/tests/test_dialog_anchor.py +44 -0
- bootstack-0.1.1/tests/test_events_doc_coverage.py +32 -0
- bootstack-0.1.1/tests/test_hot_reload.py +339 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/tests/test_images.py +27 -0
- bootstack-0.1.1/tests/test_on_color_contrast.py +65 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/tests/test_public_surface.py +35 -1
- {bootstack-0.1.0a15 → bootstack-0.1.1}/LICENSE +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/MANIFEST.in +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/NOTICE +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/setup.cfg +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/__main__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/capabilities/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/capabilities/after.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/capabilities/bind.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/capabilities/bindtags.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/capabilities/busy.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/capabilities/clipboard.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/capabilities/focus.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/capabilities/grab.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/capabilities/grid.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/capabilities/localization.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/capabilities/pack.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/capabilities/place.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/capabilities/selection.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/capabilities/signals.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/capabilities/winfo.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/colorutils.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/exceptions.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/images.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/mixins/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/mixins/ttk_state.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/mixins/widget.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/paths.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/publisher.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_core/variables.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_runtime/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_runtime/base_window.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_runtime/events.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_runtime/shortcuts.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_runtime/tk_patch.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/_runtime/visual_focus.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/bootstack.ico +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/bootstack.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/badge-pill.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/badge-square.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/button-compact.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/button-default.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/card.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/checkbox-checked.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/checkbox-indeterminate.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/checkbox-unchecked.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/field.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/input-addon-compact.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/input-addon-default.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/input-compact.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/input-default.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/list-item-separated.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/list-item.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/listrow-compact.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/listrow-default.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/manifest.toml +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/menu-item.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/navitem-compact.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/navitem-default.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/progressbar-h-compact.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/progressbar-h-default.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/progressbar-v-compact.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/progressbar-v-default.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/radiobutton.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/scrollbar-horizontal.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/scrollbar-vertical.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/slider-handle.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/slider-track-h.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/slider-track-v.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/switch-off.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/switch-on.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/tab-h.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/elements/tab-v.png +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/icons/bootstrap.ttf +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/icons/glyphmap.json +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/icons/icon_metrics.json +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/cli/__main__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/cli/appicon.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/cli/build.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/cli/config.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/cli/demo.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/cli/doctor.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/cli/icons.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/cli/promote.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/cli/pyinstaller.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/cli/start.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/clipboard.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/constants.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/data/README.md +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/data/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/data/_observable.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/data/base.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/data/file_source.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/data/memory_source.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/data/query.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/data/readers.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/data/sqlite_source.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/data/types.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/data/writers.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/dialogs/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/dialogs/_impl/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/dialogs/_impl/colorchooser.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/dialogs/_impl/datedialog.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/dialogs/_impl/filterdialog.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/dialogs/_impl/fontdialog.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/dialogs/_impl/formdialog.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/dialogs/_impl/message.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/dialogs/_impl/query.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/errors.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/events/_event.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/events/_subscription.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/i18n/README.md +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/i18n/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/i18n/catalog.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/i18n/intl_format.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/i18n/msgcat.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/i18n/specs.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/py.typed +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/scheduling/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/scheduling/_schedule.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/shortcuts.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/signals/README.md +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/signals/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/signals/integration.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/signals/signal.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/signals/types.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/store.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/streams/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/streams/_stream.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/badge.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/button.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/buttongroup.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/calendar.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/checkbutton.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/combobox.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/contextmenu.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/entry.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/expander.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/field.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/frame.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/label.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/labelframe.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/listview.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/menubar.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/panedwindow.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/progressbar.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/radiobutton.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/scale.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/scrollbar.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/separator.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/sidenav.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/sizegrip.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/spinbox.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/switch.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/tabitem.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/togglegroup.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/toolbutton.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/tooltip.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders/treeview.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders_tk/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/builders_tk/defaults.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/element.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/fonts.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/style_builder_mixed.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/style_builder_tk.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/style_builder_ttk.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/theme.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/theme_provider.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/themes/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/tk_patch.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/token_maps.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/types.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/typography.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/style/utility.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/types.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/validation/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/validation/types.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/validation/validation_result.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/validation/validation_rules.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_core/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_core/app_config.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_core/container.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_core/context.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_core/events.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_core/field_mixin.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_core/icon_image_props.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_core/image_binding.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_core/navmodel.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_core/options.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_core/selection_group.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_core/window_controls.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/_internal/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/_internal/wrapper_base.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/_parts/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/_parts/numberentry_part.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/_parts/spinnerentry_part.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/_parts/textentry_part.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/_dateutils.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/_image_fit.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/accordion.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/buttongroup.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/calendar.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/compositeframe.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/contextmenu.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/dateentry.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/dropdownbutton.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/field.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/form.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/list/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/list/listitem.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/menu/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/menu/model.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/menu/render_native.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/menu/render_themed.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/numericentry.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/passwordentry.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/pathentry.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/radiogroup.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/scrolledtext.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/scrolledtext.pyi +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/scrollview.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/selectbox.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/shell/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/shell/content_host.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/shell/layout.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/shell/providers.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/shell/rail.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/shell/workspace.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/sidenav/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/sidenav/header.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/sidenav/separator.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/slider/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/spinnerentry.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/tableview/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/tableview/types.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/tabs/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/tabs/tabitem.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/tabs/tabs.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/tabs/tabview.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/change.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/codeeditor.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/core.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/decoration.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/diff.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/filter.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/search_overlay.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/sidebar.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/style_registry.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/textarea.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textarea/undo.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/textentry.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/timeentry.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/toast.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/togglegroup.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/toolbar.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/tree/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/tree/source_binding.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/tree/treeitem.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/tree/treenode.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/composites/tree/treeview.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/mixins/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/mixins/configure_mixin.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/mixins/entry_mixin.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/mixins/font_mixin.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/mixins/icon_mixin.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/mixins/localization_mixin.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/mixins/signal_mixin.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/mixins/validation_mixin.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/__init__.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/_menubutton.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/badge.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/button.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/card.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/checkbutton.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/checktoggle.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/entry.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/flexframe.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/gridframe.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/label.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/labelframe.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/optionmenu.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/packframe.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/panedwindow.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/progressbar.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/radiobutton.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/radiotoggle.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/scrollbar.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/separator.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/sizegrip.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/spinbox.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/switch.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/_impl/primitives/treeview.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/avatar.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/boolean_controls.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/buttongroup.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/calendar.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/card.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/carousel.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/chart.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/codeeditor.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/contextmenu.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/datatable.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/datefield.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/divider.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/expander.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/form.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/gallery.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/gauge.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/grid.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/groupbox.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/listview.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/numberfield.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/pagestack.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/passwordfield.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/pathfield.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/picture.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/progressbar.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/radio_variants.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/radiogroup.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/scrollbar.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/scrollview.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/select.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/selectbutton.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/sidebar_toggle.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/sizegrip.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/slider.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/spinbox.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/spinnerfield.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/splitview.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/stacks.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/statusbar.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/tabs.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/textarea.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/textfield.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/theme_toggle.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/timefield.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/toast.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/togglegroup.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/toolbar.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/tree.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/types.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack/widgets/window.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack.egg-info/dependency_links.txt +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack.egg-info/entry_points.txt +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/src/bootstack.egg-info/top_level.txt +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/tests/test_images_gui.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/tests/test_intl_format.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/tests/test_navmodel.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/tests/test_store.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/tests/test_theme_from_existing.py +0 -0
- {bootstack-0.1.0a15 → bootstack-0.1.1}/tests/test_validation_rules.py +0 -0
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bootstack
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: From idea to a shipped desktop app, fast — a declarative, reactive, batteries-included Python UI framework with 60+ widgets, semantic theming, and built-in packaging.
|
|
5
5
|
Author-email: Israel Dryer <israel.dryer@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/israel-dryer/bootstack
|
|
8
8
|
Project-URL: Documentation, https://bootstack.org/
|
|
9
9
|
Keywords: tkinter,ttk,gui,desktop,python3,ui,widgets,theming,cross-platform,modern,framework,application-framework,desktop-ui,python-gui
|
|
10
|
-
Classifier: Development Status ::
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Environment :: Win32 (MS Windows)
|
|
12
|
-
Classifier: Environment ::
|
|
12
|
+
Classifier: Environment :: MacOS X
|
|
13
|
+
Classifier: Environment :: X11 Applications
|
|
13
14
|
Classifier: Intended Audience :: Developers
|
|
14
15
|
Classifier: Programming Language :: Python :: 3
|
|
15
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -22,6 +23,7 @@ Description-Content-Type: text/markdown
|
|
|
22
23
|
License-File: LICENSE
|
|
23
24
|
License-File: NOTICE
|
|
24
25
|
Requires-Dist: pillow<=12,>=10
|
|
26
|
+
Requires-Dist: pygments>=2.15
|
|
25
27
|
Requires-Dist: typing-extensions>=4.10
|
|
26
28
|
Requires-Dist: Babel>=2.12
|
|
27
29
|
Requires-Dist: python-dateutil>=2.9
|
|
@@ -46,6 +48,7 @@ Requires-Dist: pydata-sphinx-theme; extra == "docs"
|
|
|
46
48
|
Requires-Dist: sphinx-design; extra == "docs"
|
|
47
49
|
Requires-Dist: sphinx-copybutton; extra == "docs"
|
|
48
50
|
Requires-Dist: sphinx-autobuild; extra == "docs"
|
|
51
|
+
Requires-Dist: myst-parser; extra == "docs"
|
|
49
52
|
Dynamic: license-file
|
|
50
53
|
|
|
51
54
|

|
|
@@ -59,8 +62,7 @@ Dynamic: license-file
|
|
|
59
62
|

|
|
60
63
|

|
|
61
64
|
|
|
62
|
-
>
|
|
63
|
-
> **[bootstack.org](https://bootstack.org)**.
|
|
65
|
+
> Full documentation is at **[bootstack.org](https://bootstack.org)**.
|
|
64
66
|
|
|
65
67
|
### From idea to a shipped desktop app — fast.
|
|
66
68
|
|
|
@@ -94,15 +96,26 @@ Most Python desktop frameworks make you fight the framework. bootstack gets out
|
|
|
94
96
|
- **DataSource abstraction** — one interface over SQLite, in-memory, and file backends with filtering, sorting, pagination, and CRUD
|
|
95
97
|
- **Data visualization** — embed `matplotlib` (and `seaborn`) figures with `Chart`; they recolor with the theme and live-update from a `Signal` or `DataSource` (optional `viz` extra)
|
|
96
98
|
- **A real CLI** — scaffold, run, add pages/views/dialogs/themes, and package for distribution
|
|
99
|
+
- **Hot reload** — `bootstack dev app.py` updates the running app on save and keeps your place; module-level state and the active page survive the reload
|
|
97
100
|
- **PyInstaller packaging built in** — `bootstack build` produces a standalone executable; no separate toolchain required
|
|
98
101
|
|
|
102
|
+
## Hot reload
|
|
103
|
+
|
|
104
|
+
Edit and save — the running window updates in place. `bootstack dev app.py`
|
|
105
|
+
reloads the UI on every save while keeping your window position, module-level
|
|
106
|
+
state, and active page, so you never lose your spot.
|
|
107
|
+
|
|
108
|
+
<p align="center">
|
|
109
|
+
<video src="https://raw.githubusercontent.com/israel-dryer/bootstack/main/docs/_static/examples/live-reload.mp4" controls muted loop playsinline></video>
|
|
110
|
+
</p>
|
|
111
|
+
|
|
99
112
|
## Installation
|
|
100
113
|
|
|
101
114
|
Requires Python 3.12+ and Tk/Tcl (bundled with most Python distributions).
|
|
102
|
-
|
|
115
|
+
Install with pip:
|
|
103
116
|
|
|
104
117
|
```bash
|
|
105
|
-
python -m pip install
|
|
118
|
+
python -m pip install bootstack
|
|
106
119
|
```
|
|
107
120
|
|
|
108
121
|
See the [installation guide](https://bootstack.org/getting-started/installation/)
|
|
@@ -223,7 +236,7 @@ app.run()
|
|
|
223
236
|
|
|
224
237
|
## Features
|
|
225
238
|
|
|
226
|
-
- **Application scaffolding** — `App` for blank windows, `AppShell` for toolbar + sidebar + page-stack apps, `Workbench` for two-tier workspace apps, undecorated windows with custom chrome
|
|
239
|
+
- **Application scaffolding** — `App` for blank windows, `AppShell` for toolbar + sidebar + page-stack apps, `Workbench` for two-tier workspace apps, `Splash` for a startup intro screen, undecorated windows with custom chrome
|
|
227
240
|
- **60+ themed widgets** — primitives plus higher-level composites (DataTable, Tree, ListView, Calendar, DateField, Form, Gauge, ToggleGroup, PageStack, Carousel, Tooltip, and more)
|
|
228
241
|
- **Dialogs and messages** — `alert()` / `confirm()` / `ask_*()` prompts and `FormDialog` / `FontDialog` / `FilterDialog`, plus non-blocking `toast()`, `Notification`, and `Snackbar` surfaces
|
|
229
242
|
- **Layout containers** — `Column`, `Row`, `Grid`, and `Spacer` for declarative layouts; `Card`, `GroupBox`, `ScrollView`, `SplitView`, `Accordion`, `Divider`
|
|
@@ -248,7 +261,7 @@ app.run()
|
|
|
248
261
|
| **Data Display** | Label, Badge, ListView, Tree, DataTable, ProgressBar, Gauge |
|
|
249
262
|
| **Media** | Avatar, Picture, Gallery, Carousel |
|
|
250
263
|
| **Layout** | Column, Row, Grid, Spacer, Card, GroupBox, SplitView, ScrollView, Accordion, Divider |
|
|
251
|
-
| **Navigation** | AppShell, Workbench, Window, StatusBar, Tabs, PageStack |
|
|
264
|
+
| **Navigation** | AppShell, Workbench, Window, Splash, StatusBar, Tabs, PageStack |
|
|
252
265
|
| **Dialogs** | `alert()`, `confirm()`, `ask_*()` prompts, FormDialog, FontDialog, FilterDialog |
|
|
253
266
|
| **Overlays** | `toast()`, Notification, Snackbar, Tooltip |
|
|
254
267
|
| **Forms** | Form, with field-level validation |
|
|
@@ -264,6 +277,7 @@ bootstack appicon # Design an app icon and export it
|
|
|
264
277
|
bootstack start MyApp # Create a new project (basic template)
|
|
265
278
|
bootstack start MyApp --template appshell # ...or with sidebar navigation
|
|
266
279
|
bootstack run # Run the app defined in the project config
|
|
280
|
+
bootstack dev app.py # Run with hot reload (live edit-and-save)
|
|
267
281
|
bootstack add page Dashboard # Add a new page (appshell)
|
|
268
282
|
bootstack add view Settings # Add a new view (basic)
|
|
269
283
|
bootstack add dialog Preferences # Add a new dialog
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|

|
|
10
10
|

|
|
11
11
|
|
|
12
|
-
>
|
|
13
|
-
> **[bootstack.org](https://bootstack.org)**.
|
|
12
|
+
> Full documentation is at **[bootstack.org](https://bootstack.org)**.
|
|
14
13
|
|
|
15
14
|
### From idea to a shipped desktop app — fast.
|
|
16
15
|
|
|
@@ -44,15 +43,26 @@ Most Python desktop frameworks make you fight the framework. bootstack gets out
|
|
|
44
43
|
- **DataSource abstraction** — one interface over SQLite, in-memory, and file backends with filtering, sorting, pagination, and CRUD
|
|
45
44
|
- **Data visualization** — embed `matplotlib` (and `seaborn`) figures with `Chart`; they recolor with the theme and live-update from a `Signal` or `DataSource` (optional `viz` extra)
|
|
46
45
|
- **A real CLI** — scaffold, run, add pages/views/dialogs/themes, and package for distribution
|
|
46
|
+
- **Hot reload** — `bootstack dev app.py` updates the running app on save and keeps your place; module-level state and the active page survive the reload
|
|
47
47
|
- **PyInstaller packaging built in** — `bootstack build` produces a standalone executable; no separate toolchain required
|
|
48
48
|
|
|
49
|
+
## Hot reload
|
|
50
|
+
|
|
51
|
+
Edit and save — the running window updates in place. `bootstack dev app.py`
|
|
52
|
+
reloads the UI on every save while keeping your window position, module-level
|
|
53
|
+
state, and active page, so you never lose your spot.
|
|
54
|
+
|
|
55
|
+
<p align="center">
|
|
56
|
+
<video src="https://raw.githubusercontent.com/israel-dryer/bootstack/main/docs/_static/examples/live-reload.mp4" controls muted loop playsinline></video>
|
|
57
|
+
</p>
|
|
58
|
+
|
|
49
59
|
## Installation
|
|
50
60
|
|
|
51
61
|
Requires Python 3.12+ and Tk/Tcl (bundled with most Python distributions).
|
|
52
|
-
|
|
62
|
+
Install with pip:
|
|
53
63
|
|
|
54
64
|
```bash
|
|
55
|
-
python -m pip install
|
|
65
|
+
python -m pip install bootstack
|
|
56
66
|
```
|
|
57
67
|
|
|
58
68
|
See the [installation guide](https://bootstack.org/getting-started/installation/)
|
|
@@ -173,7 +183,7 @@ app.run()
|
|
|
173
183
|
|
|
174
184
|
## Features
|
|
175
185
|
|
|
176
|
-
- **Application scaffolding** — `App` for blank windows, `AppShell` for toolbar + sidebar + page-stack apps, `Workbench` for two-tier workspace apps, undecorated windows with custom chrome
|
|
186
|
+
- **Application scaffolding** — `App` for blank windows, `AppShell` for toolbar + sidebar + page-stack apps, `Workbench` for two-tier workspace apps, `Splash` for a startup intro screen, undecorated windows with custom chrome
|
|
177
187
|
- **60+ themed widgets** — primitives plus higher-level composites (DataTable, Tree, ListView, Calendar, DateField, Form, Gauge, ToggleGroup, PageStack, Carousel, Tooltip, and more)
|
|
178
188
|
- **Dialogs and messages** — `alert()` / `confirm()` / `ask_*()` prompts and `FormDialog` / `FontDialog` / `FilterDialog`, plus non-blocking `toast()`, `Notification`, and `Snackbar` surfaces
|
|
179
189
|
- **Layout containers** — `Column`, `Row`, `Grid`, and `Spacer` for declarative layouts; `Card`, `GroupBox`, `ScrollView`, `SplitView`, `Accordion`, `Divider`
|
|
@@ -198,7 +208,7 @@ app.run()
|
|
|
198
208
|
| **Data Display** | Label, Badge, ListView, Tree, DataTable, ProgressBar, Gauge |
|
|
199
209
|
| **Media** | Avatar, Picture, Gallery, Carousel |
|
|
200
210
|
| **Layout** | Column, Row, Grid, Spacer, Card, GroupBox, SplitView, ScrollView, Accordion, Divider |
|
|
201
|
-
| **Navigation** | AppShell, Workbench, Window, StatusBar, Tabs, PageStack |
|
|
211
|
+
| **Navigation** | AppShell, Workbench, Window, Splash, StatusBar, Tabs, PageStack |
|
|
202
212
|
| **Dialogs** | `alert()`, `confirm()`, `ask_*()` prompts, FormDialog, FontDialog, FilterDialog |
|
|
203
213
|
| **Overlays** | `toast()`, Notification, Snackbar, Tooltip |
|
|
204
214
|
| **Forms** | Form, with field-level validation |
|
|
@@ -214,6 +224,7 @@ bootstack appicon # Design an app icon and export it
|
|
|
214
224
|
bootstack start MyApp # Create a new project (basic template)
|
|
215
225
|
bootstack start MyApp --template appshell # ...or with sidebar navigation
|
|
216
226
|
bootstack run # Run the app defined in the project config
|
|
227
|
+
bootstack dev app.py # Run with hot reload (live edit-and-save)
|
|
217
228
|
bootstack add page Dashboard # Add a new page (appshell)
|
|
218
229
|
bootstack add view Settings # Add a new view (basic)
|
|
219
230
|
bootstack add dialog Preferences # Add a new dialog
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "bootstack"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.1"
|
|
8
8
|
description = "From idea to a shipped desktop app, fast — a declarative, reactive, batteries-included Python UI framework with 60+ widgets, semantic theming, and built-in packaging."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -29,9 +29,10 @@ keywords = [
|
|
|
29
29
|
"python-gui"
|
|
30
30
|
]
|
|
31
31
|
classifiers = [
|
|
32
|
-
"Development Status ::
|
|
32
|
+
"Development Status :: 4 - Beta",
|
|
33
33
|
"Environment :: Win32 (MS Windows)",
|
|
34
|
-
"Environment ::
|
|
34
|
+
"Environment :: MacOS X",
|
|
35
|
+
"Environment :: X11 Applications",
|
|
35
36
|
"Intended Audience :: Developers",
|
|
36
37
|
"Programming Language :: Python :: 3",
|
|
37
38
|
"Programming Language :: Python :: 3.12",
|
|
@@ -44,6 +45,9 @@ classifiers = [
|
|
|
44
45
|
dependencies = [
|
|
45
46
|
"pillow>=10,<=12",
|
|
46
47
|
|
|
48
|
+
# Syntax highlighting for the CodeEditor widget
|
|
49
|
+
"pygments>=2.15",
|
|
50
|
+
|
|
47
51
|
# Backported typing primitives (Self, Unpack, ParamSpec, etc.)
|
|
48
52
|
"typing-extensions>=4.10",
|
|
49
53
|
|
|
@@ -88,6 +92,7 @@ docs = [
|
|
|
88
92
|
"sphinx-design",
|
|
89
93
|
"sphinx-copybutton",
|
|
90
94
|
"sphinx-autobuild",
|
|
95
|
+
"myst-parser",
|
|
91
96
|
]
|
|
92
97
|
|
|
93
98
|
[project.urls]
|
|
@@ -109,7 +114,7 @@ where = ["src"]
|
|
|
109
114
|
]
|
|
110
115
|
|
|
111
116
|
[tool.bumpversion]
|
|
112
|
-
current_version = "0.1.
|
|
117
|
+
current_version = "0.1.1"
|
|
113
118
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)((?P<pre>a|b|rc)(?P<pre_n>\\d+))?"
|
|
114
119
|
serialize = [
|
|
115
120
|
"{major}.{minor}.{patch}{pre}{pre_n}",
|
|
@@ -60,6 +60,7 @@ from bootstack.dialogs import (
|
|
|
60
60
|
from bootstack.widgets.app import App
|
|
61
61
|
from bootstack.widgets.appshell import AppShell, Workbench
|
|
62
62
|
from bootstack.widgets.window import Window
|
|
63
|
+
from bootstack.widgets.splash import Splash
|
|
63
64
|
|
|
64
65
|
# ── Public widget layer ───────────────────────────────────────────────────────
|
|
65
66
|
from bootstack.widgets.stacks import Row, Column, Spacer
|
|
@@ -130,7 +131,7 @@ __all__ = [
|
|
|
130
131
|
"ask_date_range", "ask_item", "ask_color", "ask_font", "ask_filter",
|
|
131
132
|
"ask_save_file", "ask_open_file", "ask_open_files", "ask_directory",
|
|
132
133
|
# Application & windows
|
|
133
|
-
"App", "AppShell", "Workbench", "Window",
|
|
134
|
+
"App", "AppShell", "Workbench", "Window", "Splash",
|
|
134
135
|
# Layout
|
|
135
136
|
"Row", "Column", "Spacer", "Grid", "Card", "GroupBox", "Divider", "ScrollView",
|
|
136
137
|
"SplitView", "SplitPane", "Accordion", "AccordionSection",
|
|
@@ -630,6 +630,10 @@ class App(BaseWindow, WidgetCapabilitiesMixin, tkinter.Tk):
|
|
|
630
630
|
if self._on_close_kwarg is not None:
|
|
631
631
|
self.add_close_handler(self._on_close_kwarg)
|
|
632
632
|
|
|
633
|
+
# A registered intro splash (bs.Splash), or None. Set by the splash on
|
|
634
|
+
# construction; read at reveal time to defer the app's own appearance.
|
|
635
|
+
self._splash: Any = None
|
|
636
|
+
|
|
633
637
|
def mainloop(self, n=0) -> None:
|
|
634
638
|
"""Start the application event loop
|
|
635
639
|
|
|
@@ -639,7 +643,13 @@ class App(BaseWindow, WidgetCapabilitiesMixin, tkinter.Tk):
|
|
|
639
643
|
"""
|
|
640
644
|
if self._center_on_screen:
|
|
641
645
|
self.place_window_center()
|
|
642
|
-
self
|
|
646
|
+
splash = getattr(self, "_splash", None)
|
|
647
|
+
if splash is not None and splash.is_showing:
|
|
648
|
+
# An intro splash is up — defer the app's reveal until it dismisses,
|
|
649
|
+
# then reveal via this callback (see Splash._notify_app_ready).
|
|
650
|
+
splash._notify_app_ready(self.show)
|
|
651
|
+
else:
|
|
652
|
+
self.show()
|
|
643
653
|
super().mainloop(n=n)
|
|
644
654
|
|
|
645
655
|
def _do_close(self) -> None:
|
|
@@ -62,7 +62,13 @@ class Toplevel(BaseWindow, WidgetCapabilitiesMixin, tkinter.Toplevel):
|
|
|
62
62
|
resizable: Whether the user may resize the window as (x, y).
|
|
63
63
|
transient: Mark this window as transient for the given master.
|
|
64
64
|
overrideredirect: If True, instruct the window manager to ignore this window.
|
|
65
|
-
windowtype:
|
|
65
|
+
windowtype: Request a native window type, resolved per platform from a
|
|
66
|
+
single switch. Values: `'splash'`, `'tooltip'`, `'dock'`, `'utility'`.
|
|
67
|
+
On X11 it sets `-type`; on macOS it maps to a `MacWindowStyle`; on
|
|
68
|
+
Windows the chromeless types (`'splash'`/`'tooltip'`/`'dock'`) resolve
|
|
69
|
+
to override-redirect (borderless, no taskbar button) and `'utility'`
|
|
70
|
+
resolves to a tool window. The caller does not also need to pass
|
|
71
|
+
`overrideredirect=True` to get a chromeless window on Windows.
|
|
66
72
|
topmost: If True, keep this window above others (`-topmost`).
|
|
67
73
|
toolwindow: On Windows, request a toolwindow style (`-toolwindow`).
|
|
68
74
|
alpha: On Windows, the window alpha transparency (0.0–1.0) via `-alpha`.
|
|
@@ -124,6 +130,20 @@ class Toplevel(BaseWindow, WidgetCapabilitiesMixin, tkinter.Toplevel):
|
|
|
124
130
|
except tkinter.TclError:
|
|
125
131
|
pass
|
|
126
132
|
|
|
133
|
+
# Windows has no native window-type attribute, so translate windowtype
|
|
134
|
+
# to the matching primitive: the chromeless types resolve to
|
|
135
|
+
# override-redirect (borderless + no taskbar/Alt-Tab button), and
|
|
136
|
+
# 'utility' resolves to a tool window (minimal chrome). One switch then
|
|
137
|
+
# yields the right native window on all three platforms. (On Aqua the
|
|
138
|
+
# overrideredirect call is a no-op — MacWindowStyle handled it above.)
|
|
139
|
+
_effective_overrideredirect = overrideredirect
|
|
140
|
+
_effective_toolwindow = toolwindow
|
|
141
|
+
if windowtype is not None and self.winsys == "win32":
|
|
142
|
+
if windowtype in ("splash", "tooltip", "dock"):
|
|
143
|
+
_effective_overrideredirect = True
|
|
144
|
+
elif windowtype == "utility":
|
|
145
|
+
_effective_toolwindow = True
|
|
146
|
+
|
|
127
147
|
# Setup icon (use default bootstack icon if no icon provided)
|
|
128
148
|
self._setup_icon(icon, default_icon_enabled=True)
|
|
129
149
|
|
|
@@ -169,7 +189,7 @@ class Toplevel(BaseWindow, WidgetCapabilitiesMixin, tkinter.Toplevel):
|
|
|
169
189
|
maxsize=maxsize,
|
|
170
190
|
resizable=resizable,
|
|
171
191
|
transient=_resolved_transient,
|
|
172
|
-
overrideredirect=
|
|
192
|
+
overrideredirect=_effective_overrideredirect,
|
|
173
193
|
alpha=alpha,
|
|
174
194
|
window_style=window_style,
|
|
175
195
|
center_on_parent=_center_parent,
|
|
@@ -188,7 +208,7 @@ class Toplevel(BaseWindow, WidgetCapabilitiesMixin, tkinter.Toplevel):
|
|
|
188
208
|
if topmost:
|
|
189
209
|
self.attributes("-topmost", 1)
|
|
190
210
|
|
|
191
|
-
if
|
|
211
|
+
if _effective_toolwindow and self.winsys == "win32":
|
|
192
212
|
self.attributes("-toolwindow", 1)
|
|
193
213
|
|
|
194
214
|
# Register on_close handler
|
|
@@ -247,6 +247,27 @@ def scale_padding_floor(base: int) -> int:
|
|
|
247
247
|
return max(base, round(base * _ScalingState.get_ui_scale()))
|
|
248
248
|
|
|
249
249
|
|
|
250
|
+
def scale_icon_size(base: int) -> int:
|
|
251
|
+
"""Scale a logical icon size to physical pixels for the current DPI.
|
|
252
|
+
|
|
253
|
+
Icons are authored at logical (96-DPI) sizes. On a higher-DPI display the
|
|
254
|
+
surrounding fonts and layout scale up via Tk scaling, so an icon rendered at
|
|
255
|
+
its literal logical size lands too small for its container and gets resampled
|
|
256
|
+
by the display, reading as soft (#267). Rendering at the physical size makes
|
|
257
|
+
the glyph land 1:1 in the scaled layout, so it stays crisp.
|
|
258
|
+
|
|
259
|
+
Floors at `base` (mirrors `scale_padding_floor`) so an uninitialized scale
|
|
260
|
+
state or a sub-baseline display never renders an icon smaller than requested.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
base: The logical icon size in pixels at standard (96-DPI) scaling.
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
The DPI-scaled size, at least `base`.
|
|
267
|
+
"""
|
|
268
|
+
return max(base, round(base * _ScalingState.get_ui_scale()))
|
|
269
|
+
|
|
270
|
+
|
|
250
271
|
def scale_size(widget=None, size=None):
|
|
251
272
|
"""Scale the size based on the scaling factor of tkinter.
|
|
252
273
|
This is used most frequently to adjust the assets for
|
|
@@ -52,6 +52,79 @@ class WindowPositioning:
|
|
|
52
52
|
>>> # Returns adjusted coordinates within screen bounds
|
|
53
53
|
"""
|
|
54
54
|
|
|
55
|
+
# Per-process cache of the macOS visible frame (work area) per monitor rect,
|
|
56
|
+
# keyed by the full monitor bounds. Computed once via a clamped probe.
|
|
57
|
+
_work_area_cache: dict[Tuple[int, int, int, int], Tuple[int, int, int, int]] = {}
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def get_work_area(
|
|
61
|
+
root: Optional[tkinter.Misc],
|
|
62
|
+
monitor_rect: Tuple[int, int, int, int],
|
|
63
|
+
) -> Tuple[int, int, int, int]:
|
|
64
|
+
"""Return the usable area of a monitor, OS-reserved strips excluded.
|
|
65
|
+
|
|
66
|
+
On macOS the menu bar (top) and Dock sit *inside* the physical monitor
|
|
67
|
+
bounds, so anchoring a window to the raw monitor rect lands it under the
|
|
68
|
+
Dock. Tk exposes no `NSScreen.visibleFrame` accessor, so this clamps a
|
|
69
|
+
throwaway, fully transparent managed Toplevel to the monitor — Aqua snaps
|
|
70
|
+
a managed window to the visible frame — and reads back the realized
|
|
71
|
+
origin and size. The result is cached per monitor rect.
|
|
72
|
+
|
|
73
|
+
Off macOS the full `monitor_rect` is returned unchanged: the X11/Windows
|
|
74
|
+
transient path already clears the taskbar via a topmost overlay.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
root: A live widget used to parent the probe. If None, the full
|
|
78
|
+
`monitor_rect` is returned unchanged.
|
|
79
|
+
monitor_rect: `(x, y, w, h)` full bounds of the target monitor.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
`(x, y, w, h)` of the usable work area within `monitor_rect`.
|
|
83
|
+
"""
|
|
84
|
+
if root is None:
|
|
85
|
+
return monitor_rect
|
|
86
|
+
try:
|
|
87
|
+
if root.tk.call("tk", "windowingsystem") != "aqua":
|
|
88
|
+
return monitor_rect
|
|
89
|
+
except tkinter.TclError:
|
|
90
|
+
return monitor_rect
|
|
91
|
+
|
|
92
|
+
cached = WindowPositioning._work_area_cache.get(monitor_rect)
|
|
93
|
+
if cached is not None:
|
|
94
|
+
return cached
|
|
95
|
+
|
|
96
|
+
mx, my, mw, mh = monitor_rect
|
|
97
|
+
area = monitor_rect
|
|
98
|
+
probe: Optional[tkinter.Toplevel] = None
|
|
99
|
+
try:
|
|
100
|
+
probe = tkinter.Toplevel(root)
|
|
101
|
+
try:
|
|
102
|
+
probe.attributes("-alpha", 0.0) # invisible — no flash
|
|
103
|
+
except tkinter.TclError:
|
|
104
|
+
pass
|
|
105
|
+
# Oversize and place at the monitor origin; a managed Aqua window is
|
|
106
|
+
# clamped to that monitor's visible frame, so the realized geometry
|
|
107
|
+
# is the work area (menu bar + Dock excluded).
|
|
108
|
+
probe.geometry(f"{mw * 2}x{mh * 2}+{mx}+{my}")
|
|
109
|
+
probe.update_idletasks()
|
|
110
|
+
x, y = probe.winfo_rootx(), probe.winfo_rooty()
|
|
111
|
+
w, h = probe.winfo_width(), probe.winfo_height()
|
|
112
|
+
# Guard a degenerate probe (e.g. 1x1 if it never mapped): only trust
|
|
113
|
+
# a result that fills most of the monitor.
|
|
114
|
+
if w >= mw // 2 and h >= mh // 2:
|
|
115
|
+
area = (x, y, w, h)
|
|
116
|
+
except tkinter.TclError:
|
|
117
|
+
pass
|
|
118
|
+
finally:
|
|
119
|
+
if probe is not None:
|
|
120
|
+
try:
|
|
121
|
+
probe.destroy()
|
|
122
|
+
except tkinter.TclError:
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
WindowPositioning._work_area_cache[monitor_rect] = area
|
|
126
|
+
return area
|
|
127
|
+
|
|
55
128
|
@staticmethod
|
|
56
129
|
def _get_monitor_at_point(x: int, y: int) -> Optional[Tuple[int, int, int, int]]:
|
|
57
130
|
"""Find the monitor containing the given point.
|
|
@@ -9,6 +9,7 @@ The bootstack CLI provides commands for:
|
|
|
9
9
|
Usage:
|
|
10
10
|
bootstack start <appname> Create a new project
|
|
11
11
|
bootstack run [path] Run the application
|
|
12
|
+
bootstack dev [path] Run with hot reload (live edit-and-save)
|
|
12
13
|
bootstack promote --pyinstaller Enable PyInstaller support
|
|
13
14
|
bootstack build Build for distribution
|
|
14
15
|
bootstack add page <ClassName> Add a new page (AppShell)
|
|
@@ -26,7 +27,7 @@ import argparse
|
|
|
26
27
|
import sys
|
|
27
28
|
from typing import Sequence
|
|
28
29
|
|
|
29
|
-
from bootstack.cli import add, appicon, build, doctor, icons, promote, run, start
|
|
30
|
+
from bootstack.cli import add, appicon, build, dev, doctor, icons, promote, run, start
|
|
30
31
|
from bootstack.cli.demo import run_demo
|
|
31
32
|
|
|
32
33
|
|
|
@@ -42,6 +43,7 @@ Examples:
|
|
|
42
43
|
bootstack start MyApp --template appshell Create an AppShell project
|
|
43
44
|
bootstack start MyApp --theme superhero Use a specific theme
|
|
44
45
|
bootstack run Run the application
|
|
46
|
+
bootstack dev app.py Run with hot reload
|
|
45
47
|
bootstack promote --pyinstaller Enable PyInstaller support
|
|
46
48
|
bootstack build Build for distribution
|
|
47
49
|
bootstack add view SettingsView Add a new view
|
|
@@ -75,6 +77,7 @@ For more information on a command:
|
|
|
75
77
|
# Register commands
|
|
76
78
|
start.add_parser(subparsers)
|
|
77
79
|
run.add_parser(subparsers)
|
|
80
|
+
dev.add_parser(subparsers)
|
|
78
81
|
promote.add_parser(subparsers)
|
|
79
82
|
build.add_parser(subparsers)
|
|
80
83
|
add.add_parser(subparsers)
|
|
@@ -6,7 +6,12 @@ import argparse
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
8
|
from bootstack.cli.config import TtkbConfig, find_config
|
|
9
|
-
from bootstack.cli.templates import
|
|
9
|
+
from bootstack.cli.templates import (
|
|
10
|
+
_build_func_name,
|
|
11
|
+
create_dialog,
|
|
12
|
+
create_page,
|
|
13
|
+
create_view,
|
|
14
|
+
)
|
|
10
15
|
|
|
11
16
|
|
|
12
17
|
def add_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
@@ -202,6 +207,7 @@ def run_add_page(args: argparse.Namespace) -> None:
|
|
|
202
207
|
scrollable = args.scrollable
|
|
203
208
|
file_path = create_page(class_name, target_dir, scrollable=scrollable)
|
|
204
209
|
|
|
210
|
+
func_name = _build_func_name(class_name)
|
|
205
211
|
print(f"Created page: {file_path.relative_to(project_root)}")
|
|
206
212
|
print()
|
|
207
213
|
print("This created the file only - it is NOT yet shown in the sidebar.")
|
|
@@ -209,10 +215,11 @@ def run_add_page(args: argparse.Namespace) -> None:
|
|
|
209
215
|
print("inside your shell.page_nav() block:")
|
|
210
216
|
print()
|
|
211
217
|
import_module = f"{module_name}.pages" if module_name else "<module>.pages"
|
|
212
|
-
print(f" from {import_module}.{file_path.stem} import {
|
|
218
|
+
print(f" from {import_module}.{file_path.stem} import {func_name}")
|
|
213
219
|
scrollable_arg = ", scrollable=True" if scrollable else ""
|
|
214
|
-
print(f' with nav.add_page("<id>", text="<Label>", icon="<icon>"
|
|
215
|
-
print(f"
|
|
220
|
+
print(f' with nav.add_page("<id>", text="<Label>", icon="<icon>",')
|
|
221
|
+
print(f' padding=20, gap=12, horizontal_items="stretch"{scrollable_arg}):')
|
|
222
|
+
print(f" {func_name}()")
|
|
216
223
|
|
|
217
224
|
|
|
218
225
|
def run_add_dialog(args: argparse.Namespace) -> None:
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""bootstack dev command - Run an application with hot reload.
|
|
2
|
+
|
|
3
|
+
PROVISIONAL — the dev workflow ships in 0.1.0 but is carved out of the API
|
|
4
|
+
freeze and may change in a minor release.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import os
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
import threading
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from bootstack.cli.config import TtkbConfig, find_config
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def add_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
20
|
+
"""Add the 'dev' subcommand parser."""
|
|
21
|
+
parser = subparsers.add_parser(
|
|
22
|
+
"dev",
|
|
23
|
+
help="Run the application with hot reload",
|
|
24
|
+
description=(
|
|
25
|
+
"Run the application with live reload. Saving a source file updates "
|
|
26
|
+
"the running app. A module-level `with bs.App()` reloads in place "
|
|
27
|
+
"(state preserved); anything else falls back to a process restart."
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
parser.add_argument(
|
|
31
|
+
"path",
|
|
32
|
+
nargs="?",
|
|
33
|
+
default=None,
|
|
34
|
+
help="Path to entry point or directory with bootstack.toml (default: current directory)",
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"--restart",
|
|
38
|
+
action="store_true",
|
|
39
|
+
help="Always reload by restarting the process, even for a module-level app",
|
|
40
|
+
)
|
|
41
|
+
parser.set_defaults(func=run_dev)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def run_dev(args: argparse.Namespace) -> None:
|
|
45
|
+
"""Execute the dev command."""
|
|
46
|
+
resolved = _resolve_entry(args.path)
|
|
47
|
+
if resolved is None:
|
|
48
|
+
return
|
|
49
|
+
entry_point, project_root = resolved
|
|
50
|
+
|
|
51
|
+
if not entry_point.exists():
|
|
52
|
+
print(f"Error: Entry point '{entry_point}' does not exist.")
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
display_path = entry_point.relative_to(project_root)
|
|
57
|
+
except ValueError:
|
|
58
|
+
display_path = entry_point
|
|
59
|
+
print(f"Starting {display_path} with hot reload...")
|
|
60
|
+
print("(Save a file to reload. Press Ctrl+C to stop.)")
|
|
61
|
+
print()
|
|
62
|
+
|
|
63
|
+
env = dict(os.environ)
|
|
64
|
+
env["BOOTSTACK_DEV"] = "1"
|
|
65
|
+
env["BOOTSTACK_DEV_MODE"] = "restart" if args.restart else "inprocess"
|
|
66
|
+
|
|
67
|
+
src_dir = project_root / "src"
|
|
68
|
+
if src_dir.exists():
|
|
69
|
+
python_path = env.get("PYTHONPATH", "")
|
|
70
|
+
env["PYTHONPATH"] = (
|
|
71
|
+
f"{src_dir}{os.pathsep}{python_path}" if python_path else str(src_dir)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Supervise the app here rather than letting it os.execv itself on restart:
|
|
75
|
+
# on Windows execv does not replace the process in place — it spawns a new
|
|
76
|
+
# PID and terminates the caller, which would drop this supervisor after the
|
|
77
|
+
# first reload and orphan the app. The child re-launches by exiting with
|
|
78
|
+
# DEV_RESTART_EXIT_CODE; looping subprocess.run is portable and re-establishes
|
|
79
|
+
# cwd + env on every relaunch.
|
|
80
|
+
from bootstack.dev._env import DEV_RESTART_EXIT_CODE
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
while True:
|
|
84
|
+
result = subprocess.run(
|
|
85
|
+
[sys.executable, str(entry_point.resolve())],
|
|
86
|
+
cwd=str(project_root),
|
|
87
|
+
env=env,
|
|
88
|
+
)
|
|
89
|
+
if result.returncode == DEV_RESTART_EXIT_CODE:
|
|
90
|
+
continue # a valid edit asked for a clean restart
|
|
91
|
+
if result.returncode == 0:
|
|
92
|
+
sys.exit(0) # the app window was closed normally
|
|
93
|
+
# The app crashed (e.g. an edit raised at import). Don't end the
|
|
94
|
+
# session — stay alive and relaunch when the file is fixed and
|
|
95
|
+
# saved. This is the restart-mode counterpart to the in-process
|
|
96
|
+
# error banner's fix-and-save recovery; the child's own watcher
|
|
97
|
+
# died with it, so the supervisor watches in the meantime.
|
|
98
|
+
print(
|
|
99
|
+
f"\n[bootstack dev] app exited with code {result.returncode} - "
|
|
100
|
+
"waiting for a change to retry (Ctrl+C to stop)...",
|
|
101
|
+
flush=True,
|
|
102
|
+
)
|
|
103
|
+
_wait_for_change(str(project_root))
|
|
104
|
+
except KeyboardInterrupt:
|
|
105
|
+
print("\nStopped.")
|
|
106
|
+
sys.exit(0)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _wait_for_change(root: str) -> None:
|
|
110
|
+
"""Block until a watched source file under `root` changes (or Ctrl+C).
|
|
111
|
+
|
|
112
|
+
Used by the restart supervisor to stay alive after the app crashes: rather
|
|
113
|
+
than ending the dev session, it waits for the next save and lets the loop
|
|
114
|
+
relaunch.
|
|
115
|
+
"""
|
|
116
|
+
from bootstack.dev._watcher import PollWatcher
|
|
117
|
+
|
|
118
|
+
changed = threading.Event()
|
|
119
|
+
watcher = PollWatcher(root, lambda _paths: changed.set())
|
|
120
|
+
watcher.start()
|
|
121
|
+
try:
|
|
122
|
+
# Short timed waits keep Ctrl+C responsive on Windows, where a bare
|
|
123
|
+
# Event.wait() with no timeout can swallow the interrupt.
|
|
124
|
+
while not changed.wait(0.3):
|
|
125
|
+
pass
|
|
126
|
+
finally:
|
|
127
|
+
watcher.stop()
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _resolve_entry(path: str | None) -> tuple[Path, Path] | None:
|
|
131
|
+
"""Resolve (entry_point, project_root) from a path or bootstack.toml."""
|
|
132
|
+
if path:
|
|
133
|
+
p = Path(path)
|
|
134
|
+
if p.is_file() and p.suffix == ".py":
|
|
135
|
+
return p, p.parent
|
|
136
|
+
if p.is_dir():
|
|
137
|
+
config_path = p / "bootstack.toml"
|
|
138
|
+
if not config_path.exists():
|
|
139
|
+
print(f"Error: No bootstack.toml found in '{p}'")
|
|
140
|
+
return None
|
|
141
|
+
config = TtkbConfig.load(config_path)
|
|
142
|
+
return p / config.app.entry, p
|
|
143
|
+
print(f"Error: '{path}' is not a valid file or directory")
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
config_path = find_config()
|
|
147
|
+
if config_path is None:
|
|
148
|
+
print("Error: No bootstack.toml found in current directory or parents.")
|
|
149
|
+
print("Pass a path: bootstack dev app.py")
|
|
150
|
+
return None
|
|
151
|
+
config = TtkbConfig.load(config_path)
|
|
152
|
+
project_root = config_path.parent
|
|
153
|
+
return project_root / config.app.entry, project_root
|