shinyglass 0.5.0__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.
@@ -0,0 +1,31 @@
1
+ .Rproj.user
2
+ .Rhistory
3
+ .RData
4
+ .Ruserdata
5
+ .Renviron
6
+ *.o
7
+ *.so
8
+ *.tar.gz
9
+ .DS_Store
10
+ .Rcheck/
11
+ *Rcheck/
12
+ *.Rproj
13
+ inst/examples/screenshot*.png
14
+ visual-test-output/
15
+ docs/
16
+ inst/external/
17
+ # Python
18
+ python/**/__pycache__/
19
+ python/**/*.egg-info/
20
+ python/.pytest_cache/
21
+ python/.venv/
22
+ python/dist/
23
+ python/build/
24
+ python/.ruff_cache/
25
+ *.pyc
26
+ # Keep vendored static CSS/JS in git; ignore only caches
27
+
28
+ # Browser regression harness
29
+ node_modules/
30
+ test-results/
31
+ playwright-report/
@@ -0,0 +1,223 @@
1
+ Metadata-Version: 2.5
2
+ Name: shinyglass
3
+ Version: 0.5.0
4
+ Summary: Liquid Glass themes for Shiny for Python
5
+ Project-URL: Homepage, https://github.com/ericrayanderson/shinyglass
6
+ Project-URL: Repository, https://github.com/ericrayanderson/shinyglass
7
+ Project-URL: Issues, https://github.com/ericrayanderson/shinyglass/issues
8
+ Project-URL: Changelog, https://github.com/ericrayanderson/shinyglass/blob/main/NEWS.md
9
+ Author-email: Eric Anderson <eric.ray.anderson@gmail.com>
10
+ License-Expression: GPL-3.0-only
11
+ Keywords: bootstrap,bslib,glass,shiny,theme
12
+ Requires-Python: >=3.9
13
+ Requires-Dist: htmltools>=0.5
14
+ Requires-Dist: shiny>=1.0
15
+ Provides-Extra: dev
16
+ Requires-Dist: build; extra == 'dev'
17
+ Requires-Dist: libsass>=0.22; extra == 'dev'
18
+ Requires-Dist: matplotlib; extra == 'dev'
19
+ Requires-Dist: pytest>=7.0; extra == 'dev'
20
+ Provides-Extra: theme
21
+ Requires-Dist: libsass>=0.22; extra == 'theme'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # shinyglass (Python)
25
+
26
+ Liquid Glass themes for **Shiny for Python**, sharing design assets with the R
27
+ package under [`inst/`](https://github.com/ericrayanderson/shinyglass/tree/main/inst).
28
+
29
+ ## Install
30
+
31
+ ### From PyPI
32
+
33
+ ```bash
34
+ pip install shinyglass
35
+ ```
36
+
37
+ ### From a git checkout (developers)
38
+
39
+ ```bash
40
+ cd python
41
+ pip install -e ".[dev,theme]"
42
+ python scripts/vendor_assets.py # compile CSS/JS into src/shinyglass/static/
43
+ pytest
44
+ ```
45
+
46
+ ### From a built wheel
47
+
48
+ ```bash
49
+ cd python
50
+ python scripts/vendor_assets.py
51
+ python -m build --wheel
52
+ pip install dist/shinyglass-*.whl
53
+ ```
54
+
55
+ Default themes use **precompiled CSS**. **libsass is not required** at runtime.
56
+ For custom `primary` / `blur` / `radius`, install the optional extra:
57
+
58
+ ```bash
59
+ pip install "shinyglass[theme]" # pulls libsass
60
+ ```
61
+
62
+ ## Usage
63
+
64
+ ```python
65
+ from shiny import App, ui
66
+ from shinyglass import glass_theme
67
+
68
+ app_ui = ui.page_sidebar(
69
+ ui.sidebar("Filters", ui.input_text("q", "Query")),
70
+ ui.card(ui.card_header("Hello"), "Glass content"),
71
+ theme=glass_theme(preset="light"), # or "dark"
72
+ )
73
+
74
+ app = App(app_ui, None)
75
+ ```
76
+
77
+ ### Parameters (same spirit as R)
78
+
79
+ | Argument | Default | |
80
+ | --- | --- | --- |
81
+ | `preset` | `"light"` | `"light"`, `"dark"`, or `"auto"` |
82
+ | `primary` | `"#007AFF"` | Accent (custom → needs `[theme]`) |
83
+ | `blur` | `36` | Backdrop blur px |
84
+ | `saturation` | `200` | Backdrop saturate % |
85
+ | `radius` | `"1.5rem"` | Corner radius |
86
+ | `intensity` | `0.5` | Ultra Clear (`0`) to Tinted (`1`); iOS 27 midpoint. The web cannot read the OS slider. |
87
+ | `plot_surface` | `"clear"` | `"opaque"` densifies plot / table hosts (~94% panel); chrome stays glass |
88
+ | `scene` | `"default"` | `tahoe` / `dusk` / `mesh` / `aurora` / `harbor` / `grove` |
89
+ | `flatten` | `False` | Print / screenshot mode (or `?glass_flatten=1`) |
90
+ | `persist` | `False` | Remember preset, intensity, scene, plot surface |
91
+ | `refraction` | `True` | Chromium squircle lens. Safari/Firefox keep blur and tint |
92
+ | `morph` | `True` | Spring the active pill. Menus scale out of the trigger |
93
+ | `press` | `True` | Springy press on buttons and nav links |
94
+ | `scroll_edge` | `True` | Toolbar backing while content scrolls underneath |
95
+ | `minimize_tabs` | `True` | Shrink tab bars on scroll down |
96
+ | `icon_style` | `"clear"` | Monochrome bar icons. `"color"` keeps author colors |
97
+ | `floating_sidebar` | `False` | Opt-in detached sidebar |
98
+
99
+ ### Controls and tokens
100
+
101
+ These helpers match the R functions of the same names. Inputs take `id`
102
+ instead of `inputId`, and choice dicts map values to labels like
103
+ `ui.input_select()`.
104
+
105
+ ```python
106
+ from shiny import App, reactive, ui
107
+ import shinyglass as sg
108
+
109
+ theme = sg.glass_add_tokens(sg.glass_theme(preset="auto"), blur="28px")
110
+
111
+ app_ui = ui.page_sidebar(
112
+ ui.sidebar(
113
+ sg.glass_theme_toggle(),
114
+ sg.glass_preset_input(),
115
+ sg.glass_accent_input(),
116
+ sg.glass_scene_input(),
117
+ ui.input_action_button("flat", "Flatten for capture"),
118
+ ),
119
+ ui.card(ui.card_header("Hello"), "Glass content"),
120
+ title="Controls demo",
121
+ theme=theme,
122
+ )
123
+
124
+
125
+ def server(input, output, session):
126
+ sg.observe_glass_theme_toggle(input, session)
127
+ sg.observe_glass_preset_input(input, session)
128
+ sg.observe_glass_accent(input, session)
129
+ sg.observe_glass_scene(input, session)
130
+
131
+ @reactive.effect
132
+ @reactive.event(input.flat)
133
+ def _():
134
+ sg.glass_flatten(session, True)
135
+
136
+
137
+ app = App(app_ui, server)
138
+ ```
139
+
140
+ | Helper | What it does |
141
+ | --- | --- |
142
+ | `glass_theme_toggle()` | Light / Dark / Auto button group |
143
+ | `glass_preset_input()` | Light / Dark / Auto select, applied as soon as it changes |
144
+ | `glass_accent_input()` | iOS accent color wells (`glass_system_colors()`) |
145
+ | `glass_scene_input()` | Wallpaper scene select (`glass_scenes()`) |
146
+ | `observe_glass_*()` | Echo those inputs through `update_glass_theme()` on the server |
147
+ | `glass_flatten(session, on)` | Turn blur off for print, PDF, and screenshots |
148
+ | `glass_css_tokens()` | Catalog of public `--glass-*` CSS variables |
149
+ | `glass_token_pack(preset)` | Default variable values for light or dark |
150
+ | `glass_add_tokens(theme, ...)` | Override variables on a theme without editing SCSS |
151
+
152
+ ## Demo
153
+
154
+ ```bash
155
+ cd python
156
+ shiny run examples/app_sidebar.py
157
+ ```
158
+
159
+ ## Layout (Option 2)
160
+
161
+ | Path | Role |
162
+ | --- | --- |
163
+ | `../inst/scss/glass.scss` | Shared source of truth |
164
+ | `../inst/js/shiny-glass.js` | Shared JS |
165
+ | `src/shinyglass/static/` | **Vendored** copy + precompiled `theme-*.css` (ships in wheel) |
166
+ | `scripts/vendor_assets.py` | Build step: copy + compile |
167
+ | `hatch_build.py` | Hatch hook re-vendors when monorepo `inst/` is present |
168
+
169
+ Asset resolution order at runtime:
170
+
171
+ 1. Package `static/` (wheel / vendored)
172
+ 2. Monorepo `inst/` via `SHINYGLASS_PKG_ROOT` or path walk (editable dev)
173
+
174
+ ## Tests & CI
175
+
176
+ ```bash
177
+ pytest
178
+ ```
179
+
180
+ GitHub Actions (`.github/workflows/python.yml`):
181
+
182
+ 1. Editable install + vendor + sync check + pytest
183
+ 2. Build wheel, install in a **clean venv** with no monorepo `inst/`, re-run tests
184
+
185
+ ## R / Python feature scope
186
+
187
+ R is the primary implementation. Python is experimental and does not yet offer
188
+ all of the R runtime controls. Shared assets do not imply API parity.
189
+
190
+ | Feature | R | Python |
191
+ | --- | --- | --- |
192
+ | Presets | Light / Dark / Auto | Light / Dark / Auto |
193
+ | Live preset / accent updates | Public server and UI helpers | `update_glass_theme()` + shared JS |
194
+ | Intensity / material controls | Public API and slider | `intensity=` + `glass_intensity_slider()` |
195
+ | `plot_surface` / scenes / flatten | Public API | Same knobs on `glass_theme()` |
196
+ | Scroll-edge chrome densify | Shared JS / CSS | Shared vendored JS / CSS |
197
+ | Default blur / radius | 36 px / `1.5rem` | 36 px / `1.5rem` |
198
+ | Default CSS delivery | bslib Sass compilation | Precompiled wheel assets |
199
+ | Custom theme knobs | bslib / sass | Optional `[theme]` extra |
200
+ | Theme toggle, preset / accent / scene inputs | Public UI + observers | Same helpers and `observe_glass_*()` |
201
+ | Flatten for capture | `glass_flatten()` | `glass_flatten()` |
202
+ | CSS token catalog / overrides | `glass_css_tokens()`, `glass_token_pack()`, `glass_add_tokens()` | Same names |
203
+
204
+ When editing shared JavaScript, update its vendored copy too. When editing SCSS,
205
+ run `python scripts/vendor_assets.py` from this directory to rebuild wheel CSS.
206
+ The Python CI jobs verify editable and isolated wheel installations.
207
+
208
+ The editable job also re-runs `vendor_assets.py` and fails if the vendored
209
+ `glass.scss`, JavaScript, or `glass-rules-*.css` files differ from what is
210
+ committed. If that check fails, run `python python/scripts/vendor_assets.py`
211
+ from the repo root and commit the updated files under
212
+ `src/shinyglass/static/`. The `theme-*.css` bundles are not compared because
213
+ they include Bootstrap from whichever shiny version is installed.
214
+
215
+ ## Not yet
216
+
217
+ - Published PyPI release (package version tracks R **0.5.0**; not on PyPI)
218
+ - Full R demo gallery parity
219
+ - `glass_page()`, `glass_plot_surface_input()`, `glass_resolved_preset()`,
220
+ `observe_glass()` and the intensity / plot surface observers
221
+ - Plot and table helpers (`theme_glass()`, `plotly_glass()`, `gt_theme_glass()`,
222
+ `dt_options_glass()`, `glass_plot_colors()`)
223
+ - React components
@@ -0,0 +1,200 @@
1
+ # shinyglass (Python)
2
+
3
+ Liquid Glass themes for **Shiny for Python**, sharing design assets with the R
4
+ package under [`inst/`](https://github.com/ericrayanderson/shinyglass/tree/main/inst).
5
+
6
+ ## Install
7
+
8
+ ### From PyPI
9
+
10
+ ```bash
11
+ pip install shinyglass
12
+ ```
13
+
14
+ ### From a git checkout (developers)
15
+
16
+ ```bash
17
+ cd python
18
+ pip install -e ".[dev,theme]"
19
+ python scripts/vendor_assets.py # compile CSS/JS into src/shinyglass/static/
20
+ pytest
21
+ ```
22
+
23
+ ### From a built wheel
24
+
25
+ ```bash
26
+ cd python
27
+ python scripts/vendor_assets.py
28
+ python -m build --wheel
29
+ pip install dist/shinyglass-*.whl
30
+ ```
31
+
32
+ Default themes use **precompiled CSS**. **libsass is not required** at runtime.
33
+ For custom `primary` / `blur` / `radius`, install the optional extra:
34
+
35
+ ```bash
36
+ pip install "shinyglass[theme]" # pulls libsass
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ ```python
42
+ from shiny import App, ui
43
+ from shinyglass import glass_theme
44
+
45
+ app_ui = ui.page_sidebar(
46
+ ui.sidebar("Filters", ui.input_text("q", "Query")),
47
+ ui.card(ui.card_header("Hello"), "Glass content"),
48
+ theme=glass_theme(preset="light"), # or "dark"
49
+ )
50
+
51
+ app = App(app_ui, None)
52
+ ```
53
+
54
+ ### Parameters (same spirit as R)
55
+
56
+ | Argument | Default | |
57
+ | --- | --- | --- |
58
+ | `preset` | `"light"` | `"light"`, `"dark"`, or `"auto"` |
59
+ | `primary` | `"#007AFF"` | Accent (custom → needs `[theme]`) |
60
+ | `blur` | `36` | Backdrop blur px |
61
+ | `saturation` | `200` | Backdrop saturate % |
62
+ | `radius` | `"1.5rem"` | Corner radius |
63
+ | `intensity` | `0.5` | Ultra Clear (`0`) to Tinted (`1`); iOS 27 midpoint. The web cannot read the OS slider. |
64
+ | `plot_surface` | `"clear"` | `"opaque"` densifies plot / table hosts (~94% panel); chrome stays glass |
65
+ | `scene` | `"default"` | `tahoe` / `dusk` / `mesh` / `aurora` / `harbor` / `grove` |
66
+ | `flatten` | `False` | Print / screenshot mode (or `?glass_flatten=1`) |
67
+ | `persist` | `False` | Remember preset, intensity, scene, plot surface |
68
+ | `refraction` | `True` | Chromium squircle lens. Safari/Firefox keep blur and tint |
69
+ | `morph` | `True` | Spring the active pill. Menus scale out of the trigger |
70
+ | `press` | `True` | Springy press on buttons and nav links |
71
+ | `scroll_edge` | `True` | Toolbar backing while content scrolls underneath |
72
+ | `minimize_tabs` | `True` | Shrink tab bars on scroll down |
73
+ | `icon_style` | `"clear"` | Monochrome bar icons. `"color"` keeps author colors |
74
+ | `floating_sidebar` | `False` | Opt-in detached sidebar |
75
+
76
+ ### Controls and tokens
77
+
78
+ These helpers match the R functions of the same names. Inputs take `id`
79
+ instead of `inputId`, and choice dicts map values to labels like
80
+ `ui.input_select()`.
81
+
82
+ ```python
83
+ from shiny import App, reactive, ui
84
+ import shinyglass as sg
85
+
86
+ theme = sg.glass_add_tokens(sg.glass_theme(preset="auto"), blur="28px")
87
+
88
+ app_ui = ui.page_sidebar(
89
+ ui.sidebar(
90
+ sg.glass_theme_toggle(),
91
+ sg.glass_preset_input(),
92
+ sg.glass_accent_input(),
93
+ sg.glass_scene_input(),
94
+ ui.input_action_button("flat", "Flatten for capture"),
95
+ ),
96
+ ui.card(ui.card_header("Hello"), "Glass content"),
97
+ title="Controls demo",
98
+ theme=theme,
99
+ )
100
+
101
+
102
+ def server(input, output, session):
103
+ sg.observe_glass_theme_toggle(input, session)
104
+ sg.observe_glass_preset_input(input, session)
105
+ sg.observe_glass_accent(input, session)
106
+ sg.observe_glass_scene(input, session)
107
+
108
+ @reactive.effect
109
+ @reactive.event(input.flat)
110
+ def _():
111
+ sg.glass_flatten(session, True)
112
+
113
+
114
+ app = App(app_ui, server)
115
+ ```
116
+
117
+ | Helper | What it does |
118
+ | --- | --- |
119
+ | `glass_theme_toggle()` | Light / Dark / Auto button group |
120
+ | `glass_preset_input()` | Light / Dark / Auto select, applied as soon as it changes |
121
+ | `glass_accent_input()` | iOS accent color wells (`glass_system_colors()`) |
122
+ | `glass_scene_input()` | Wallpaper scene select (`glass_scenes()`) |
123
+ | `observe_glass_*()` | Echo those inputs through `update_glass_theme()` on the server |
124
+ | `glass_flatten(session, on)` | Turn blur off for print, PDF, and screenshots |
125
+ | `glass_css_tokens()` | Catalog of public `--glass-*` CSS variables |
126
+ | `glass_token_pack(preset)` | Default variable values for light or dark |
127
+ | `glass_add_tokens(theme, ...)` | Override variables on a theme without editing SCSS |
128
+
129
+ ## Demo
130
+
131
+ ```bash
132
+ cd python
133
+ shiny run examples/app_sidebar.py
134
+ ```
135
+
136
+ ## Layout (Option 2)
137
+
138
+ | Path | Role |
139
+ | --- | --- |
140
+ | `../inst/scss/glass.scss` | Shared source of truth |
141
+ | `../inst/js/shiny-glass.js` | Shared JS |
142
+ | `src/shinyglass/static/` | **Vendored** copy + precompiled `theme-*.css` (ships in wheel) |
143
+ | `scripts/vendor_assets.py` | Build step: copy + compile |
144
+ | `hatch_build.py` | Hatch hook re-vendors when monorepo `inst/` is present |
145
+
146
+ Asset resolution order at runtime:
147
+
148
+ 1. Package `static/` (wheel / vendored)
149
+ 2. Monorepo `inst/` via `SHINYGLASS_PKG_ROOT` or path walk (editable dev)
150
+
151
+ ## Tests & CI
152
+
153
+ ```bash
154
+ pytest
155
+ ```
156
+
157
+ GitHub Actions (`.github/workflows/python.yml`):
158
+
159
+ 1. Editable install + vendor + sync check + pytest
160
+ 2. Build wheel, install in a **clean venv** with no monorepo `inst/`, re-run tests
161
+
162
+ ## R / Python feature scope
163
+
164
+ R is the primary implementation. Python is experimental and does not yet offer
165
+ all of the R runtime controls. Shared assets do not imply API parity.
166
+
167
+ | Feature | R | Python |
168
+ | --- | --- | --- |
169
+ | Presets | Light / Dark / Auto | Light / Dark / Auto |
170
+ | Live preset / accent updates | Public server and UI helpers | `update_glass_theme()` + shared JS |
171
+ | Intensity / material controls | Public API and slider | `intensity=` + `glass_intensity_slider()` |
172
+ | `plot_surface` / scenes / flatten | Public API | Same knobs on `glass_theme()` |
173
+ | Scroll-edge chrome densify | Shared JS / CSS | Shared vendored JS / CSS |
174
+ | Default blur / radius | 36 px / `1.5rem` | 36 px / `1.5rem` |
175
+ | Default CSS delivery | bslib Sass compilation | Precompiled wheel assets |
176
+ | Custom theme knobs | bslib / sass | Optional `[theme]` extra |
177
+ | Theme toggle, preset / accent / scene inputs | Public UI + observers | Same helpers and `observe_glass_*()` |
178
+ | Flatten for capture | `glass_flatten()` | `glass_flatten()` |
179
+ | CSS token catalog / overrides | `glass_css_tokens()`, `glass_token_pack()`, `glass_add_tokens()` | Same names |
180
+
181
+ When editing shared JavaScript, update its vendored copy too. When editing SCSS,
182
+ run `python scripts/vendor_assets.py` from this directory to rebuild wheel CSS.
183
+ The Python CI jobs verify editable and isolated wheel installations.
184
+
185
+ The editable job also re-runs `vendor_assets.py` and fails if the vendored
186
+ `glass.scss`, JavaScript, or `glass-rules-*.css` files differ from what is
187
+ committed. If that check fails, run `python python/scripts/vendor_assets.py`
188
+ from the repo root and commit the updated files under
189
+ `src/shinyglass/static/`. The `theme-*.css` bundles are not compared because
190
+ they include Bootstrap from whichever shiny version is installed.
191
+
192
+ ## Not yet
193
+
194
+ - Published PyPI release (package version tracks R **0.5.0**; not on PyPI)
195
+ - Full R demo gallery parity
196
+ - `glass_page()`, `glass_plot_surface_input()`, `glass_resolved_preset()`,
197
+ `observe_glass()` and the intensity / plot surface observers
198
+ - Plot and table helpers (`theme_glass()`, `plotly_glass()`, `gt_theme_glass()`,
199
+ `dt_options_glass()`, `glass_plot_colors()`)
200
+ - React components
@@ -0,0 +1,151 @@
1
+ """Minimal page_sidebar demo for the shinyglass Python spike.
2
+
3
+ Run from the monorepo root (or with SHINYGLASS_PKG_ROOT set)::
4
+
5
+ cd python
6
+ pip install -e .
7
+ shiny run examples/app_sidebar.py
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from shiny import App, reactive, render, ui
13
+
14
+ from shinyglass import glass_intensity_slider, glass_theme, update_glass_theme
15
+
16
+ app_ui = ui.page_sidebar(
17
+ ui.sidebar(
18
+ glass_intensity_slider("glass_intensity", value=0.5),
19
+ ui.h4("Filters"),
20
+ ui.input_select(
21
+ "species",
22
+ "Species",
23
+ choices=["All", "setosa", "versicolor", "virginica"],
24
+ selected="All",
25
+ ),
26
+ ui.input_slider("n", "Sample n", min=20, max=150, value=80),
27
+ ui.input_switch("smooth", "Smooth", True),
28
+ ui.input_select(
29
+ "plot_surface",
30
+ "Plot / table surface",
31
+ choices={"clear": "Clear (show wallpaper)", "opaque": "Opaque (readable)"},
32
+ selected="clear",
33
+ ),
34
+ ui.input_select(
35
+ "scene",
36
+ "Scene",
37
+ choices={
38
+ "tahoe": "Tahoe",
39
+ "dusk": "Dusk",
40
+ "aurora": "Aurora",
41
+ "harbor": "Harbor",
42
+ "grove": "Grove",
43
+ },
44
+ selected="tahoe",
45
+ ),
46
+ title="Glass sidebar",
47
+ width=280,
48
+ ),
49
+ ui.h2("shinyglass · Shiny for Python"),
50
+ ui.layout_columns(
51
+ ui.value_box("Rows", ui.output_text("vb_n"), theme="primary"),
52
+ ui.value_box("Species", ui.output_text("vb_sp"), theme="success"),
53
+ ui.value_box("Mean SL", ui.output_text("vb_mu"), theme="info"),
54
+ fill=False,
55
+ ),
56
+ ui.layout_columns(
57
+ ui.card(
58
+ ui.card_header("Histogram"),
59
+ ui.output_plot("hist", height="280px"),
60
+ full_screen=True,
61
+ ),
62
+ ui.card(
63
+ ui.card_header("Notes"),
64
+ ui.p(
65
+ "Python spike reuses the R package ",
66
+ ui.tags.code("inst/scss/glass.scss"),
67
+ " and ",
68
+ ui.tags.code("inst/js/shiny-glass.js"),
69
+ " (Option 2 layout).",
70
+ ),
71
+ ui.input_action_button("go", "Notify", class_="btn-primary"),
72
+ ),
73
+ col_widths=[8, 4],
74
+ ),
75
+ title="shinyglass (Python)",
76
+ fillable=True,
77
+ theme=glass_theme(
78
+ preset="auto",
79
+ intensity=0.5,
80
+ plot_surface="clear",
81
+ scene="tahoe",
82
+ persist=True,
83
+ ),
84
+ )
85
+
86
+
87
+ def server(input, output, session):
88
+ @reactive.calc
89
+ def dat():
90
+ # Tiny in-memory iris subset without requiring pandas
91
+ import random
92
+
93
+ # Approximate iris sepal lengths by species for the demo
94
+ base = {
95
+ "setosa": [5.1, 4.9, 4.7, 4.6, 5.0, 5.4, 4.6, 5.0],
96
+ "versicolor": [7.0, 6.4, 6.9, 5.5, 6.5, 5.7, 6.3, 4.9],
97
+ "virginica": [6.3, 5.8, 7.1, 6.3, 6.5, 7.6, 4.9, 7.3],
98
+ }
99
+ species = input.species()
100
+ keys = list(base.keys()) if species == "All" else [species]
101
+ vals: list[tuple[str, float]] = []
102
+ for sp in keys:
103
+ for v in base[sp]:
104
+ vals.append((sp, v))
105
+ random.seed(1)
106
+ random.shuffle(vals)
107
+ n = min(input.n(), len(vals))
108
+ return vals[:n]
109
+
110
+ @render.text
111
+ def vb_n():
112
+ return str(len(dat()))
113
+
114
+ @render.text
115
+ def vb_sp():
116
+ return str(len({s for s, _ in dat()}))
117
+
118
+ @render.text
119
+ def vb_mu():
120
+ xs = [v for _, v in dat()]
121
+ return f"{sum(xs) / len(xs):.2f}" if xs else "—"
122
+
123
+ @render.plot
124
+ def hist():
125
+ import matplotlib.pyplot as plt
126
+
127
+ xs = [v for _, v in dat()]
128
+ fig, ax = plt.subplots()
129
+ ax.hist(xs, bins=10, color="#007AFF", edgecolor="none")
130
+ ax.set_xlabel("Sepal length")
131
+ ax.set_ylabel("Count")
132
+ fig.tight_layout()
133
+ return fig
134
+
135
+ @reactive.effect
136
+ @reactive.event(input.plot_surface)
137
+ def _plot_surface():
138
+ update_glass_theme(session, plot_surface=input.plot_surface())
139
+
140
+ @reactive.effect
141
+ @reactive.event(input.scene)
142
+ def _scene():
143
+ update_glass_theme(session, scene=input.scene())
144
+
145
+ @reactive.effect
146
+ @reactive.event(input.go)
147
+ def _notify():
148
+ ui.notification_show("shinyglass Python spike", type="message")
149
+
150
+
151
+ app = App(app_ui, server)
@@ -0,0 +1,39 @@
1
+ """Hatch build hook: vendor static assets when monorepo inst/ is available."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
9
+
10
+
11
+ class CustomBuildHook(BuildHookInterface):
12
+ """Before wheel/sdist: refresh static/ from monorepo if possible."""
13
+
14
+ PLUGIN_NAME = "custom"
15
+
16
+ def initialize(self, version: str, build_data: dict) -> None:
17
+ root = Path(self.root)
18
+ static_css = root / "src" / "shinyglass" / "static" / "theme-light.css"
19
+ monorepo_scss = root.parent / "inst" / "scss" / "glass.scss"
20
+ script = root / "scripts" / "vendor_assets.py"
21
+
22
+ if monorepo_scss.is_file() and script.is_file():
23
+ # Building from git checkout — always re-vendor so CSS matches SCSS.
24
+ sys.path.insert(0, str(root / "scripts"))
25
+ from vendor_assets import vendor # noqa: WPS433
26
+
27
+ print("hatch: vendoring assets from monorepo inst/")
28
+ vendor(quiet=False)
29
+ return
30
+
31
+ if not static_css.is_file():
32
+ raise RuntimeError(
33
+ "shinyglass static/theme-light.css is missing and monorepo "
34
+ "inst/scss was not found. Run `python scripts/vendor_assets.py` "
35
+ "from a full git checkout before building, or build from the "
36
+ "shinyglass repository root."
37
+ )
38
+
39
+ print("hatch: using committed/prebuilt static/ assets")
@@ -0,0 +1,50 @@
1
+ [build-system]
2
+ requires = ["hatchling", "libsass>=0.22", "shiny>=1.0", "htmltools>=0.5"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "shinyglass"
7
+ version = "0.5.0"
8
+ description = "Liquid Glass themes for Shiny for Python"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "GPL-3.0-only"
12
+ authors = [
13
+ { name = "Eric Anderson", email = "eric.ray.anderson@gmail.com" },
14
+ ]
15
+ keywords = ["shiny", "theme", "bootstrap", "glass", "bslib"]
16
+ dependencies = [
17
+ "shiny>=1.0",
18
+ "htmltools>=0.5",
19
+ ]
20
+
21
+ [project.optional-dependencies]
22
+ # Runtime Sass for custom primary/blur/radius (defaults use precompiled CSS)
23
+ theme = ["libsass>=0.22"]
24
+ dev = ["pytest>=7.0", "libsass>=0.22", "build", "matplotlib"]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/ericrayanderson/shinyglass"
28
+ Repository = "https://github.com/ericrayanderson/shinyglass"
29
+ Issues = "https://github.com/ericrayanderson/shinyglass/issues"
30
+ Changelog = "https://github.com/ericrayanderson/shinyglass/blob/main/NEWS.md"
31
+
32
+ [tool.hatch.build.targets.wheel]
33
+ packages = ["src/shinyglass"]
34
+
35
+ [tool.hatch.build.targets.sdist]
36
+ include = [
37
+ "src/shinyglass",
38
+ "scripts",
39
+ "hatch_build.py",
40
+ "README.md",
41
+ "examples",
42
+ "tests",
43
+ "pyproject.toml",
44
+ ]
45
+
46
+ [tool.hatch.build.hooks.custom]
47
+ path = "hatch_build.py"
48
+
49
+ [tool.pytest.ini_options]
50
+ testpaths = ["tests"]