gtm-ui-native 0.0.3.1__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.
- cli/README.md +103 -0
- cli/__init__.py +0 -0
- cli/dev.py +330 -0
- cli/main.py +893 -0
- cli/preset_codec.py +136 -0
- cli/py.typed +0 -0
- cli/scrollbar_css.py +444 -0
- cli/shimmer_css.py +104 -0
- cli/tailwind_config.py +3 -0
- components/__init__.py +1 -0
- components/chart/__init__.py +1 -0
- components/chart/chart_tooltip.py +117 -0
- components/core/__init__.py +1 -0
- components/core/core.py +54 -0
- components/core/hugeicon.py +68 -0
- components/py.typed +0 -0
- components/ui/__init__.py +1 -0
- components/ui/accordion.py +115 -0
- components/ui/attachment.py +263 -0
- components/ui/avatar.py +172 -0
- components/ui/badge.py +79 -0
- components/ui/bubble.py +169 -0
- components/ui/button.py +148 -0
- components/ui/button_group.py +104 -0
- components/ui/card.py +140 -0
- components/ui/checkbox.py +107 -0
- components/ui/collapsible.py +71 -0
- components/ui/dialog.py +225 -0
- components/ui/field.py +257 -0
- components/ui/frame.py +175 -0
- components/ui/input.py +51 -0
- components/ui/input_group.py +246 -0
- components/ui/marker.py +97 -0
- components/ui/menu.py +234 -0
- components/ui/message.py +128 -0
- components/ui/radio_group.py +127 -0
- components/ui/select.py +115 -0
- components/ui/separator.py +45 -0
- components/ui/spinner.py +25 -0
- components/ui/switch.py +117 -0
- components/ui/table.py +551 -0
- components/ui/tabs.py +234 -0
- components/ui/textarea.py +46 -0
- gtm_ui_native-0.0.3.1.dist-info/METADATA +123 -0
- gtm_ui_native-0.0.3.1.dist-info/RECORD +348 -0
- gtm_ui_native-0.0.3.1.dist-info/WHEEL +4 -0
- gtm_ui_native-0.0.3.1.dist-info/entry_points.txt +3 -0
- gtm_ui_native-0.0.3.1.dist-info/licenses/LICENSE.md +21 -0
- native/__init__.py +0 -0
- native/config.py +68 -0
- native/core/__init__.py +17 -0
- native/core/constants.py +21 -0
- native/core/utils.py +39 -0
- native/engine/__init__.py +0 -0
- native/engine/constants.py +9 -0
- native/engine/generator.py +75 -0
- native/engine/parser.py +284 -0
- native/engine/utils.py +4 -0
- native/export.py +146 -0
- native/hooks/__init__.py +0 -0
- native/hooks/client_state.py +0 -0
- native/hooks/head_components.py +54 -0
- native/hooks/stylesheets.py +26 -0
- native/lib/blocks/__init__.py +0 -0
- native/lib/blocks/area_chart_01.py +134 -0
- native/lib/blocks/area_chart_02.py +132 -0
- native/lib/blocks/bar_chart_01.py +125 -0
- native/lib/blocks/bar_chart_02.py +111 -0
- native/lib/blocks/bar_chart_03.py +132 -0
- native/lib/blocks/bar_chart_04.py +97 -0
- native/lib/blocks/kpi_card_01.py +62 -0
- native/lib/blocks/kpi_card_02.py +131 -0
- native/lib/blocks/line_chart_01.py +151 -0
- native/lib/blocks/line_chart_02.py +170 -0
- native/lib/blocks/line_chart_03.py +196 -0
- native/lib/blocks/line_chart_04.py +105 -0
- native/lib/charts/area/__init__.py +0 -0
- native/lib/charts/area/v1.py +75 -0
- native/lib/charts/area/v2.py +76 -0
- native/lib/charts/area/v3.py +76 -0
- native/lib/charts/area/v4.py +84 -0
- native/lib/charts/area/v5.py +140 -0
- native/lib/charts/area/v6.py +85 -0
- native/lib/charts/area/v7.py +95 -0
- native/lib/charts/area/v8.py +131 -0
- native/lib/charts/bar/__init__.py +0 -0
- native/lib/charts/bar/v1.py +80 -0
- native/lib/charts/bar/v2.py +75 -0
- native/lib/charts/bar/v3.py +87 -0
- native/lib/charts/bar/v4.py +86 -0
- native/lib/charts/bar/v5.py +125 -0
- native/lib/charts/bar/v6.py +89 -0
- native/lib/charts/bar/v7.py +74 -0
- native/lib/charts/bar/v9.py +99 -0
- native/lib/charts/line/__init__.py +0 -0
- native/lib/charts/line/v1.py +70 -0
- native/lib/charts/line/v2.py +70 -0
- native/lib/charts/line/v3.py +77 -0
- native/lib/charts/line/v4.py +78 -0
- native/lib/charts/line/v5.py +70 -0
- native/lib/charts/line/v6.py +62 -0
- native/lib/charts/line/v7.py +85 -0
- native/lib/charts/line/v8.py +97 -0
- native/lib/components/__init__.py +0 -0
- native/lib/components/accordion_basic.py +45 -0
- native/lib/components/attachment_group.py +49 -0
- native/lib/components/attachment_with_image.py +61 -0
- native/lib/components/attachment_with_sizes.py +33 -0
- native/lib/components/attachment_with_states.py +82 -0
- native/lib/components/attachment_with_trigger.py +37 -0
- native/lib/components/avatar_badge_with_icon.py +23 -0
- native/lib/components/avatar_basic.py +19 -0
- native/lib/components/avatar_dropdown_menu.py +22 -0
- native/lib/components/avatar_group.py +31 -0
- native/lib/components/avatar_group_count_icon.py +34 -0
- native/lib/components/avatar_sizes.py +32 -0
- native/lib/components/avatar_with_badge.py +22 -0
- native/lib/components/avatar_with_group_count.py +31 -0
- native/lib/components/badge_as_link.py +15 -0
- native/lib/components/badge_custom_colors.py +29 -0
- native/lib/components/badge_variants.py +14 -0
- native/lib/components/badge_with_icon.py +20 -0
- native/lib/components/badge_wtih_spinner.py +20 -0
- native/lib/components/bubble_group.py +39 -0
- native/lib/components/bubble_link_button.py +50 -0
- native/lib/components/bubble_reactions.py +60 -0
- native/lib/components/bubble_with_align.py +22 -0
- native/lib/components/bubble_with_variants.py +65 -0
- native/lib/components/button_as_link.py +11 -0
- native/lib/components/button_default.py +7 -0
- native/lib/components/button_destructive.py +7 -0
- native/lib/components/button_ghost.py +7 -0
- native/lib/components/button_group_dropdown.py +33 -0
- native/lib/components/button_group_input.py +17 -0
- native/lib/components/button_group_orientation.py +23 -0
- native/lib/components/button_group_separator.py +12 -0
- native/lib/components/button_group_size.py +29 -0
- native/lib/components/button_group_split.py +17 -0
- native/lib/components/button_icon.py +12 -0
- native/lib/components/button_link.py +7 -0
- native/lib/components/button_loading.py +21 -0
- native/lib/components/button_outline.py +7 -0
- native/lib/components/button_rounded.py +20 -0
- native/lib/components/button_secondary.py +7 -0
- native/lib/components/button_sizes.py +50 -0
- native/lib/components/button_with_icon.py +20 -0
- native/lib/components/card_edge_to_edge.py +37 -0
- native/lib/components/card_image.py +31 -0
- native/lib/components/card_small.py +61 -0
- native/lib/components/card_spacing.py +115 -0
- native/lib/components/checkbox_basic.py +21 -0
- native/lib/components/checkbox_description.py +28 -0
- native/lib/components/checkbox_disabled.py +24 -0
- native/lib/components/checkbox_group.py +70 -0
- native/lib/components/collapsible_basic.py +35 -0
- native/lib/components/collapsible_nested.py +75 -0
- native/lib/components/dialog_close_button.py +45 -0
- native/lib/components/dialog_no_close_button.py +18 -0
- native/lib/components/dialog_sticky_footer.py +46 -0
- native/lib/components/field_general.py +122 -0
- native/lib/components/frame_basic.py +21 -0
- native/lib/components/frame_dense.py +23 -0
- native/lib/components/frame_no_border.py +24 -0
- native/lib/components/frame_stacked.py +22 -0
- native/lib/components/input_basic_demo.py +14 -0
- native/lib/components/input_custom_input.py +15 -0
- native/lib/components/input_disabled.py +15 -0
- native/lib/components/input_email.py +14 -0
- native/lib/components/input_file_input.py +13 -0
- native/lib/components/input_group_block_end.py +51 -0
- native/lib/components/input_group_block_start.py +54 -0
- native/lib/components/input_group_button.py +82 -0
- native/lib/components/input_group_dropdown.py +45 -0
- native/lib/components/input_group_icons.py +43 -0
- native/lib/components/input_group_inline_end.py +19 -0
- native/lib/components/input_group_inline_start.py +18 -0
- native/lib/components/input_group_spinner.py +46 -0
- native/lib/components/input_group_text.py +51 -0
- native/lib/components/input_password.py +14 -0
- native/lib/components/marker_border.py +25 -0
- native/lib/components/marker_links_and_buttons.py +35 -0
- native/lib/components/marker_separator.py +21 -0
- native/lib/components/marker_shimmer.py +18 -0
- native/lib/components/marker_status.py +21 -0
- native/lib/components/marker_variants.py +20 -0
- native/lib/components/marker_with_icon.py +24 -0
- native/lib/components/menu_basic.py +16 -0
- native/lib/components/menu_destructive.py +16 -0
- native/lib/components/menu_icons.py +16 -0
- native/lib/components/menu_shortcuts.py +19 -0
- native/lib/components/message_with_actions.py +67 -0
- native/lib/components/message_with_attachments.py +67 -0
- native/lib/components/message_with_avatar.py +62 -0
- native/lib/components/message_with_group.py +38 -0
- native/lib/components/message_with_header_footer.py +35 -0
- native/lib/components/radio_group_choice_card.py +35 -0
- native/lib/components/radio_group_description.py +32 -0
- native/lib/components/radio_group_disabled.py +31 -0
- native/lib/components/radio_group_fieldset.py +37 -0
- native/lib/components/radio_group_invalid.py +35 -0
- native/lib/components/select_basic.py +24 -0
- native/lib/components/select_disabled.py +23 -0
- native/lib/components/select_form.py +39 -0
- native/lib/components/select_optgroup.py +37 -0
- native/lib/components/select_sizes.py +22 -0
- native/lib/components/separator_horizontal.py +21 -0
- native/lib/components/separator_list.py +26 -0
- native/lib/components/separator_menu.py +32 -0
- native/lib/components/separator_vertical.py +14 -0
- native/lib/components/spinner_badge.py +13 -0
- native/lib/components/spinner_button.py +13 -0
- native/lib/components/spinner_marker.py +27 -0
- native/lib/components/spinner_size.py +13 -0
- native/lib/components/switch_basic.py +18 -0
- native/lib/components/switch_disabled.py +21 -0
- native/lib/components/switch_invalid.py +23 -0
- native/lib/components/switch_sizes.py +32 -0
- native/lib/components/switch_with_label.py +20 -0
- native/lib/components/table_demo.py +212 -0
- native/lib/components/table_demo_two.py +311 -0
- native/lib/components/tabs_basic.py +81 -0
- native/lib/components/tabs_disabled.py +17 -0
- native/lib/components/tabs_icons.py +22 -0
- native/lib/components/tabs_line_variant.py +15 -0
- native/lib/components/tabs_vertical.py +18 -0
- native/lib/components/textarea_basic_demo.py +7 -0
- native/lib/components/textarea_disabled.py +20 -0
- native/lib/components/textarea_field.py +21 -0
- native/lib/components/textarea_invalid.py +23 -0
- native/lib/components/typography_blockquote.py +10 -0
- native/lib/components/typography_h1.py +8 -0
- native/lib/components/typography_h2.py +8 -0
- native/lib/components/typography_h3.py +8 -0
- native/lib/components/typography_h4.py +8 -0
- native/lib/components/typography_inline_code.py +8 -0
- native/lib/components/typography_list.py +10 -0
- native/lib/components/typography_p.py +8 -0
- native/lib/components/typography_table.py +58 -0
- native/lib/dashboards/__init__.py +0 -0
- native/lib/dashboards/dashboard_01.py +358 -0
- native/lib/dashboards/dashboard_02.py +283 -0
- native/lib/dashboards/dashboard_03.py +444 -0
- native/lib/examples/__init__.py +0 -0
- native/lib/examples/card_01.py +80 -0
- native/lib/examples/card_02.py +59 -0
- native/lib/examples/card_03.py +81 -0
- native/lib/examples/card_04.py +92 -0
- native/lib/examples/card_05.py +64 -0
- native/lib/examples/card_06.py +81 -0
- native/lib/examples/card_07.py +48 -0
- native/lib/examples/card_08.py +37 -0
- native/lib/examples/card_09.py +28 -0
- native/lib/examples/card_10.py +76 -0
- native/lib/examples/card_11.py +128 -0
- native/lib/examples/card_12.py +39 -0
- native/lib/examples/card_13.py +33 -0
- native/lib/examples/card_14.py +35 -0
- native/lib/examples/card_15.py +65 -0
- native/lib/examples/decex_3.py +44 -0
- native/lib/examples/docex_1.py +31 -0
- native/lib/examples/docex_2.py +32 -0
- native/lib/examples/docex_4.py +48 -0
- native/lib/examples/docex_5.py +66 -0
- native/lib/typeset/set_four.md +28 -0
- native/lib/typeset/set_one.md +241 -0
- native/lib/typeset/set_three.md +35 -0
- native/lib/typeset/set_two.md +49 -0
- native/lib/utilities/__init__.py +0 -0
- native/lib/utilities/scroll_fade_demo.py +20 -0
- native/lib/utilities/scroll_fade_disabled.py +48 -0
- native/lib/utilities/scroll_fade_edges.py +109 -0
- native/lib/utilities/scroll_fade_horizontal.py +35 -0
- native/lib/utilities/scroll_fade_no_fade.py +20 -0
- native/lib/utilities/scroll_fade_size.py +48 -0
- native/lib/utilities/shimmer_angle.py +17 -0
- native/lib/utilities/shimmer_color.py +15 -0
- native/lib/utilities/shimmer_duration.py +20 -0
- native/lib/utilities/shimmer_none.py +9 -0
- native/lib/utilities/shimmer_play_once.py +28 -0
- native/lib/utilities/shimmer_reverse.py +12 -0
- native/lib/utilities/shimmer_spread.py +17 -0
- native/native.py +13 -0
- native/pages/__init__.py +0 -0
- native/pages/blocks.py +148 -0
- native/pages/components.py +295 -0
- native/pages/create.py +438 -0
- native/pages/dashboard.py +40 -0
- native/pages/docs.py +245 -0
- native/pages/landing.py +88 -0
- native/pages/typeset.py +274 -0
- native/py.typed +0 -0
- native/registry/__init__.py +0 -0
- native/registry/anatomy/accordion.py +12 -0
- native/registry/anatomy/attachment.py +10 -0
- native/registry/anatomy/avatar.py +6 -0
- native/registry/anatomy/badge.py +3 -0
- native/registry/anatomy/bubble.py +6 -0
- native/registry/anatomy/button.py +3 -0
- native/registry/anatomy/button_group.py +7 -0
- native/registry/anatomy/card.py +11 -0
- native/registry/anatomy/checkbox.py +5 -0
- native/registry/anatomy/collapsible.py +6 -0
- native/registry/anatomy/dialog.py +10 -0
- native/registry/anatomy/field.py +10 -0
- native/registry/anatomy/frame.py +11 -0
- native/registry/anatomy/input.py +3 -0
- native/registry/anatomy/input_group.py +6 -0
- native/registry/anatomy/marker.py +6 -0
- native/registry/anatomy/menu.py +12 -0
- native/registry/anatomy/message.py +11 -0
- native/registry/anatomy/radio_group.py +6 -0
- native/registry/anatomy/select.py +13 -0
- native/registry/anatomy/separator.py +3 -0
- native/registry/anatomy/spinner.py +3 -0
- native/registry/anatomy/switch.py +3 -0
- native/registry/anatomy/table.py +15 -0
- native/registry/anatomy/tabs.py +8 -0
- native/registry/anatomy/textarea.py +3 -0
- native/registry/colors.py +266 -0
- native/registry/components.py +176 -0
- native/registry/fonts.py +97 -0
- native/registry/radii.py +24 -0
- native/registry/styles.py +61 -0
- native/registry/themes.py +435 -0
- native/registry/typeset_options.py +77 -0
- native/templates/__init__.py +0 -0
- native/templates/_copy_btn.py +45 -0
- native/templates/_dashboard.py +84 -0
- native/templates/_dashboard_example.py +63 -0
- native/templates/_demo.py +81 -0
- native/templates/_get_code.py +89 -0
- native/templates/_get_typeset_code.py +115 -0
- native/templates/_intro.py +169 -0
- native/templates/_meta_tags.py +39 -0
- native/templates/_source.py +57 -0
- native/templates/_usage.py +112 -0
- native/templates/anatomy.py +147 -0
- native/templates/dashboard_introspect.py +65 -0
- native/templates/docpage.py +41 -0
- native/templates/footer.py +35 -0
- native/templates/github.py +25 -0
- native/templates/layout.py +57 -0
- native/templates/masonary.py +42 -0
- native/templates/navbar.py +116 -0
- native/templates/sidebar.py +145 -0
- native/templates/sublayout.py +63 -0
- native/templates/theme.py +47 -0
- native/templates/toc.py +225 -0
cli/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# buridan
|
|
2
|
+
|
|
3
|
+
A command line interface (CLI) for adding components and themes to your project.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
uv add gtm-ui-native
|
|
9
|
+
# or
|
|
10
|
+
pip install gtm-ui-native
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The package on PyPI is `gtm-ui-native`; the command it installs is `buridan`.
|
|
14
|
+
|
|
15
|
+
## Package naming
|
|
16
|
+
|
|
17
|
+
This fork publishes `gtm-ui-native` from [elviskahoro/native](https://github.com/elviskahoro/native). Upstream `LineIndent/native` publishes `buridan`, and `LineIndent/ui` publishes `buridan-create`. All three install a `buridan` command, so install only one per environment.
|
|
18
|
+
|
|
19
|
+
## create
|
|
20
|
+
|
|
21
|
+
Opens the Buridan Native theme builder in your browser. Use it to customize your design system and generate a unique preset ID.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
buridan create
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## init
|
|
28
|
+
|
|
29
|
+
Initialize Buridan Native in your project. This command sets up CSS utilities (shimmer, scroll fade) in `assets/globals.css` and updates `rxconfig.py` with the required Tailwind configuration.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
buridan init
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## apply
|
|
36
|
+
|
|
37
|
+
Apply a theme preset to your project. Generates `:root` and `.dark` CSS variable blocks in `assets/globals.css` based on the preset ID from the theme builder.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
buridan apply --preset <ID>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Arguments:
|
|
44
|
+
|
|
45
|
+
- `--preset`: The theme preset ID from the Buridan Native theme builder. Use `b0` for the default theme.
|
|
46
|
+
|
|
47
|
+
Example:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
buridan apply --preset b0
|
|
51
|
+
buridan apply --preset b1000
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## add
|
|
55
|
+
|
|
56
|
+
Add components and their dependencies to your project.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
buridan add <name>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
You can add multiple components at once:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
buridan add button input select
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Blocks (charts, dashboards, etc.) can be added the same way:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
buridan add line_chart_01
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Components are placed in `components/`, blocks in `blocks/`. Dependencies are resolved and added automatically.
|
|
75
|
+
|
|
76
|
+
> **Note:** Components require a theme to render correctly. Run `buridan apply` before using components.
|
|
77
|
+
|
|
78
|
+
## list
|
|
79
|
+
|
|
80
|
+
Display all available components and blocks.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
buridan list
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Recommended workflow
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
buridan create # build your theme, copy the preset ID
|
|
90
|
+
buridan init # set up utilities and Tailwind config
|
|
91
|
+
buridan apply --preset <ID> # apply your theme
|
|
92
|
+
buridan add button input select # add the components you need
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
# Documentation
|
|
96
|
+
|
|
97
|
+
Visit [https://native.buridan.dev/components](https://ui.buridan.dev/components) to see all the components.
|
|
98
|
+
|
|
99
|
+
Visit [https://native.buridan.dev/docs/getting-started/cli](https://ui.buridan.dev/docs/getting-started/cli) section to get started.
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
Licensed under the [MIT license](https://github.com/elviskahoro/native/blob/main/LICENSE.md)
|
cli/__init__.py
ADDED
|
File without changes
|
cli/dev.py
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import subprocess # nosec B404 - fixed-argv dev runner
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import questionary
|
|
7
|
+
from InquirerPy.base.control import Choice
|
|
8
|
+
from InquirerPy.prompts.fuzzy import FuzzyPrompt
|
|
9
|
+
|
|
10
|
+
DOCS_DIR = Path("docs")
|
|
11
|
+
MIN_PATH_PARTS = 2
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def build_index() -> dict[str, list[str]]:
|
|
15
|
+
"""Builds:
|
|
16
|
+
{
|
|
17
|
+
"getting-started": ["introduction", "installation"],
|
|
18
|
+
"components": ["button", "input"]
|
|
19
|
+
}.
|
|
20
|
+
"""
|
|
21
|
+
sections: dict[str, list[str]] = {}
|
|
22
|
+
|
|
23
|
+
for file in DOCS_DIR.glob("**/*.md"):
|
|
24
|
+
rel = file.relative_to(DOCS_DIR)
|
|
25
|
+
parts = rel.with_suffix("").parts
|
|
26
|
+
|
|
27
|
+
if len(parts) < MIN_PATH_PARTS:
|
|
28
|
+
continue
|
|
29
|
+
|
|
30
|
+
section = parts[0].replace("_", "-")
|
|
31
|
+
page = parts[1].replace("_", "-")
|
|
32
|
+
|
|
33
|
+
sections.setdefault(section, []).append(page)
|
|
34
|
+
|
|
35
|
+
for pages in sections.values():
|
|
36
|
+
pages.sort()
|
|
37
|
+
|
|
38
|
+
return sections
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def build_arg_parser() -> argparse.ArgumentParser:
|
|
42
|
+
parser = argparse.ArgumentParser(
|
|
43
|
+
prog="dev",
|
|
44
|
+
description="Run the docs site with a specific set of pages loaded.",
|
|
45
|
+
epilog=(
|
|
46
|
+
"Examples:\n"
|
|
47
|
+
" uv run dev interactive picker\n"
|
|
48
|
+
" uv run dev button input load pages by name\n"
|
|
49
|
+
" uv run dev components/button load by full path\n"
|
|
50
|
+
" uv run dev --section components load a whole section\n"
|
|
51
|
+
" uv run dev --section components auth load multiple sections\n"
|
|
52
|
+
" uv run dev prod run full site, prod mode\n"
|
|
53
|
+
),
|
|
54
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
"pages",
|
|
59
|
+
nargs="*",
|
|
60
|
+
help="page names (e.g. 'button') or full paths (e.g. 'components/button')",
|
|
61
|
+
)
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"--section",
|
|
64
|
+
"-s",
|
|
65
|
+
nargs="+",
|
|
66
|
+
default=[],
|
|
67
|
+
metavar="SECTION",
|
|
68
|
+
help="load every page in the given section(s)",
|
|
69
|
+
)
|
|
70
|
+
parser.add_argument(
|
|
71
|
+
"--prod",
|
|
72
|
+
action="store_true",
|
|
73
|
+
help="run in prod mode (loads the full site)",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
return parser
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _resolve_single_name(
|
|
80
|
+
name: str,
|
|
81
|
+
name_to_paths: dict[str, list[str]],
|
|
82
|
+
sections: dict[str, list[str]],
|
|
83
|
+
) -> tuple[str | None, str | None]:
|
|
84
|
+
"""Resolves a single page name (bare or full path).
|
|
85
|
+
|
|
86
|
+
Returns: (resolved_path, error_message) tuple. error_message is None if successful.
|
|
87
|
+
"""
|
|
88
|
+
if "/" in name:
|
|
89
|
+
section, _, page = name.partition("/")
|
|
90
|
+
if page in sections.get(section, []):
|
|
91
|
+
return name, None
|
|
92
|
+
return None, f"page not found: '{name}'"
|
|
93
|
+
|
|
94
|
+
matches = name_to_paths.get(name)
|
|
95
|
+
if not matches:
|
|
96
|
+
return None, f"page not found: '{name}'"
|
|
97
|
+
if len(matches) > 1:
|
|
98
|
+
error = (
|
|
99
|
+
f"'{name}' is ambiguous (matches: {', '.join(matches)}) "
|
|
100
|
+
f"— use the full path, e.g. '{matches[0]}'"
|
|
101
|
+
)
|
|
102
|
+
return None, error
|
|
103
|
+
return matches[0], None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _add_path_if_new(path: str, seen: set[str], resolved: list[str]) -> None:
|
|
107
|
+
"""Add path to resolved list if not already seen (deduplication)."""
|
|
108
|
+
if path not in seen:
|
|
109
|
+
seen.add(path)
|
|
110
|
+
resolved.append(path)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _process_section_names(
|
|
114
|
+
section_names: list[str],
|
|
115
|
+
sections: dict[str, list[str]],
|
|
116
|
+
) -> tuple[list[str], list[str]]:
|
|
117
|
+
"""Expands section names into full page paths.
|
|
118
|
+
|
|
119
|
+
Returns: (paths, errors) tuple with all expanded paths and error messages.
|
|
120
|
+
"""
|
|
121
|
+
paths = []
|
|
122
|
+
errors = []
|
|
123
|
+
for section in section_names:
|
|
124
|
+
if section not in sections:
|
|
125
|
+
errors.append(f"section not found: '{section}'")
|
|
126
|
+
continue
|
|
127
|
+
paths.extend(f"{section}/{page}" for page in sections[section])
|
|
128
|
+
return paths, errors
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def resolve_pages(
|
|
132
|
+
names: list[str],
|
|
133
|
+
section_names: list[str],
|
|
134
|
+
sections: dict[str, list[str]],
|
|
135
|
+
) -> list[str] | None:
|
|
136
|
+
"""Resolves bare page names ('button'), full paths ('components/button'),
|
|
137
|
+
and --section names into a deduped list of full 'section/page' paths.
|
|
138
|
+
|
|
139
|
+
Returns None (and prints errors) if anything couldn't be resolved.
|
|
140
|
+
"""
|
|
141
|
+
name_to_paths: dict[str, list[str]] = {}
|
|
142
|
+
for section, pages in sections.items():
|
|
143
|
+
for page in pages:
|
|
144
|
+
name_to_paths.setdefault(page, []).append(f"{section}/{page}")
|
|
145
|
+
|
|
146
|
+
resolved = []
|
|
147
|
+
seen: set[str] = set()
|
|
148
|
+
errors = []
|
|
149
|
+
|
|
150
|
+
for name in names:
|
|
151
|
+
path, error = _resolve_single_name(name, name_to_paths, sections)
|
|
152
|
+
if error:
|
|
153
|
+
errors.append(error)
|
|
154
|
+
elif path:
|
|
155
|
+
_add_path_if_new(path, seen, resolved)
|
|
156
|
+
|
|
157
|
+
section_paths, section_errors = _process_section_names(section_names, sections)
|
|
158
|
+
errors.extend(section_errors)
|
|
159
|
+
for path in section_paths:
|
|
160
|
+
_add_path_if_new(path, seen, resolved)
|
|
161
|
+
|
|
162
|
+
if errors:
|
|
163
|
+
for e in errors:
|
|
164
|
+
print(f"Error: {e}")
|
|
165
|
+
return None
|
|
166
|
+
|
|
167
|
+
if not resolved:
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
return resolved
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def select_env() -> str | None:
|
|
174
|
+
env = questionary.select(
|
|
175
|
+
"Run mode:",
|
|
176
|
+
choices=["dev", "prod"],
|
|
177
|
+
).ask()
|
|
178
|
+
|
|
179
|
+
if env is None:
|
|
180
|
+
return None
|
|
181
|
+
|
|
182
|
+
return env
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def select_pages_fuzzy(sections: dict[str, list[str]]) -> list[str] | None:
|
|
186
|
+
"""Fuzzy search + multi-select over all pages."""
|
|
187
|
+
choices = []
|
|
188
|
+
|
|
189
|
+
for section, pages in sections.items():
|
|
190
|
+
for page in pages:
|
|
191
|
+
path = f"{section}/{page}"
|
|
192
|
+
choices.append(Choice(value=path, name=f" {path}"))
|
|
193
|
+
|
|
194
|
+
result = FuzzyPrompt(
|
|
195
|
+
message=(
|
|
196
|
+
"Search pages (type to filter, tab to select multiple, enter to confirm):"
|
|
197
|
+
),
|
|
198
|
+
choices=choices,
|
|
199
|
+
multiselect=True,
|
|
200
|
+
max_height="70%",
|
|
201
|
+
instruction="(type a page or section/page path)",
|
|
202
|
+
).execute()
|
|
203
|
+
|
|
204
|
+
if not result:
|
|
205
|
+
return None
|
|
206
|
+
|
|
207
|
+
pages = []
|
|
208
|
+
seen = set()
|
|
209
|
+
|
|
210
|
+
for item in result:
|
|
211
|
+
if item not in seen:
|
|
212
|
+
seen.add(item)
|
|
213
|
+
pages.append(item)
|
|
214
|
+
|
|
215
|
+
return pages
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def run_reflex(env: str, pages: list[str] | None) -> str | None:
|
|
219
|
+
"""Runs Reflex and prints correct URLs ONLY after the actual port is known."""
|
|
220
|
+
if env == "dev":
|
|
221
|
+
os.environ["BURIDAN_DEV_MODE"] = "true"
|
|
222
|
+
|
|
223
|
+
if pages:
|
|
224
|
+
os.environ["BURIDAN_DEV_PAGES"] = ",".join(pages)
|
|
225
|
+
else:
|
|
226
|
+
os.environ.pop("BURIDAN_DEV_PAGES", None)
|
|
227
|
+
else:
|
|
228
|
+
os.environ.pop("BURIDAN_DEV_MODE", None)
|
|
229
|
+
os.environ.pop("BURIDAN_DEV_PAGES", None)
|
|
230
|
+
|
|
231
|
+
cmd = ["uv", "run", "reflex", "run", "--env", env]
|
|
232
|
+
|
|
233
|
+
process = subprocess.Popen( # noqa: S603 # nosec B603 - fixed argv, env validated upstream
|
|
234
|
+
cmd,
|
|
235
|
+
stdout=subprocess.PIPE,
|
|
236
|
+
stderr=subprocess.STDOUT,
|
|
237
|
+
text=True,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
port = None
|
|
241
|
+
|
|
242
|
+
if process.stdout is None:
|
|
243
|
+
msg = "reflex subprocess has no stdout pipe"
|
|
244
|
+
raise RuntimeError(msg)
|
|
245
|
+
|
|
246
|
+
for line in process.stdout:
|
|
247
|
+
print(line, end="")
|
|
248
|
+
|
|
249
|
+
# Detect actual runtime port from Reflex output (only once)
|
|
250
|
+
if port is None and "App running at:" in line:
|
|
251
|
+
try:
|
|
252
|
+
port = line.split("http://localhost:")[1].split("/")[0]
|
|
253
|
+
|
|
254
|
+
# Print correct clickable URLs ONLY when server is ready
|
|
255
|
+
if pages:
|
|
256
|
+
base_url = f"http://localhost:{port}/docs"
|
|
257
|
+
|
|
258
|
+
print("\nLoading specified URLs:")
|
|
259
|
+
for page in pages:
|
|
260
|
+
print(f"{base_url}/{page}")
|
|
261
|
+
|
|
262
|
+
except IndexError:
|
|
263
|
+
pass
|
|
264
|
+
|
|
265
|
+
return port
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _run_interactive_mode(sections: dict[str, list[str]]) -> None:
|
|
269
|
+
"""Interactive mode: prompts for environment and pages, then runs reflex."""
|
|
270
|
+
env = select_env()
|
|
271
|
+
if env is None:
|
|
272
|
+
print("Exiting")
|
|
273
|
+
return
|
|
274
|
+
|
|
275
|
+
if env == "prod":
|
|
276
|
+
print("Launching full site (prod mode)...")
|
|
277
|
+
run_reflex(env, pages=None)
|
|
278
|
+
return
|
|
279
|
+
|
|
280
|
+
selected_pages = select_pages_fuzzy(sections)
|
|
281
|
+
if selected_pages is None:
|
|
282
|
+
print("Exiting")
|
|
283
|
+
return
|
|
284
|
+
|
|
285
|
+
confirm = questionary.confirm(
|
|
286
|
+
f"Run reflex in {env} with {len(selected_pages)} pages?",
|
|
287
|
+
).ask()
|
|
288
|
+
|
|
289
|
+
if not confirm:
|
|
290
|
+
print("Cancelled")
|
|
291
|
+
return
|
|
292
|
+
|
|
293
|
+
run_reflex(env, selected_pages)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def main() -> None:
|
|
297
|
+
sections = build_index()
|
|
298
|
+
|
|
299
|
+
parser = build_arg_parser()
|
|
300
|
+
args = parser.parse_args()
|
|
301
|
+
|
|
302
|
+
if args.prod or (args.pages and args.pages[0] == "prod"):
|
|
303
|
+
print("Launching full site (prod mode)...")
|
|
304
|
+
run_reflex("prod", pages=None)
|
|
305
|
+
return
|
|
306
|
+
|
|
307
|
+
requested_pages = list(args.pages)
|
|
308
|
+
if requested_pages and requested_pages[0] == "dev":
|
|
309
|
+
requested_pages = requested_pages[1:]
|
|
310
|
+
|
|
311
|
+
if requested_pages or args.section:
|
|
312
|
+
resolved_pages = resolve_pages(requested_pages, args.section, sections)
|
|
313
|
+
if resolved_pages is None:
|
|
314
|
+
return
|
|
315
|
+
|
|
316
|
+
print(f"Running reflex in dev with {len(resolved_pages)} page(s):")
|
|
317
|
+
for page in resolved_pages:
|
|
318
|
+
print(f" {page}")
|
|
319
|
+
|
|
320
|
+
run_reflex("dev", resolved_pages)
|
|
321
|
+
return
|
|
322
|
+
|
|
323
|
+
_run_interactive_mode(sections)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
if __name__ == "__main__":
|
|
327
|
+
try:
|
|
328
|
+
main()
|
|
329
|
+
except KeyboardInterrupt:
|
|
330
|
+
print("\nExiting")
|