reflex 0.8.16a1__py3-none-any.whl → 0.8.17__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.
Potentially problematic release.
This version of reflex might be problematic. Click here for more details.
- reflex/.templates/web/utils/state.js +3 -1
- reflex/app.py +11 -3
- reflex/compiler/templates.py +12 -2
- reflex/components/base/error_boundary.py +60 -43
- reflex/components/core/window_events.py +30 -9
- reflex/components/core/window_events.pyi +6 -5
- reflex/components/lucide/icon.py +2 -1
- reflex/components/lucide/icon.pyi +2 -1
- reflex/components/plotly/plotly.py +9 -9
- reflex/components/recharts/recharts.py +2 -2
- reflex/constants/installer.py +2 -2
- reflex/environment.py +31 -0
- reflex/istate/manager/__init__.py +24 -2
- reflex/istate/manager/disk.py +15 -5
- reflex/istate/manager/memory.py +13 -4
- reflex/istate/manager/redis.py +27 -6
- reflex/istate/proxy.py +14 -12
- reflex/plugins/tailwind_v4.py +2 -2
- reflex/state.py +49 -11
- reflex/utils/frontend_skeleton.py +2 -0
- {reflex-0.8.16a1.dist-info → reflex-0.8.17.dist-info}/METADATA +40 -11
- {reflex-0.8.16a1.dist-info → reflex-0.8.17.dist-info}/RECORD +25 -25
- {reflex-0.8.16a1.dist-info → reflex-0.8.17.dist-info}/WHEEL +0 -0
- {reflex-0.8.16a1.dist-info → reflex-0.8.17.dist-info}/entry_points.txt +0 -0
- {reflex-0.8.16a1.dist-info → reflex-0.8.17.dist-info}/licenses/LICENSE +0 -0
|
@@ -665,7 +665,9 @@ export const connect = async (
|
|
|
665
665
|
}
|
|
666
666
|
}
|
|
667
667
|
applyClientStorageDelta(client_storage, update.delta);
|
|
668
|
-
|
|
668
|
+
if (update.final !== null) {
|
|
669
|
+
event_processing = !update.final;
|
|
670
|
+
}
|
|
669
671
|
if (update.events) {
|
|
670
672
|
queueEvents(update.events, socket, false, navigate, params);
|
|
671
673
|
}
|
reflex/app.py
CHANGED
|
@@ -1556,11 +1556,14 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1556
1556
|
)
|
|
1557
1557
|
|
|
1558
1558
|
@contextlib.asynccontextmanager
|
|
1559
|
-
async def modify_state(
|
|
1559
|
+
async def modify_state(
|
|
1560
|
+
self, token: str, background: bool = False
|
|
1561
|
+
) -> AsyncIterator[BaseState]:
|
|
1560
1562
|
"""Modify the state out of band.
|
|
1561
1563
|
|
|
1562
1564
|
Args:
|
|
1563
1565
|
token: The token to modify the state for.
|
|
1566
|
+
background: Whether the modification is happening in a background task.
|
|
1564
1567
|
|
|
1565
1568
|
Yields:
|
|
1566
1569
|
The state to modify.
|
|
@@ -1581,7 +1584,10 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1581
1584
|
# When the state is modified reset dirty status and emit the delta to the frontend.
|
|
1582
1585
|
state._clean()
|
|
1583
1586
|
await self.event_namespace.emit_update(
|
|
1584
|
-
update=StateUpdate(
|
|
1587
|
+
update=StateUpdate(
|
|
1588
|
+
delta=delta,
|
|
1589
|
+
final=True if not background else None,
|
|
1590
|
+
),
|
|
1585
1591
|
token=token,
|
|
1586
1592
|
)
|
|
1587
1593
|
|
|
@@ -1758,7 +1764,9 @@ async def process(
|
|
|
1758
1764
|
constants.RouteVar.CLIENT_IP: client_ip,
|
|
1759
1765
|
})
|
|
1760
1766
|
# Get the state for the session exclusively.
|
|
1761
|
-
async with app.state_manager.modify_state(
|
|
1767
|
+
async with app.state_manager.modify_state(
|
|
1768
|
+
event.substate_token, event=event
|
|
1769
|
+
) as state:
|
|
1762
1770
|
# When this is a brand new instance of the state, signal the
|
|
1763
1771
|
# frontend to reload before processing it.
|
|
1764
1772
|
if (
|
reflex/compiler/templates.py
CHANGED
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
6
|
from collections.abc import Iterable, Mapping
|
|
7
|
-
from typing import TYPE_CHECKING, Any
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
8
8
|
|
|
9
9
|
from reflex import constants
|
|
10
10
|
from reflex.constants import Hooks
|
|
@@ -492,13 +492,21 @@ def package_json_template(
|
|
|
492
492
|
})
|
|
493
493
|
|
|
494
494
|
|
|
495
|
-
def vite_config_template(
|
|
495
|
+
def vite_config_template(
|
|
496
|
+
base: str,
|
|
497
|
+
hmr: bool,
|
|
498
|
+
force_full_reload: bool,
|
|
499
|
+
experimental_hmr: bool,
|
|
500
|
+
sourcemap: bool | Literal["inline", "hidden"],
|
|
501
|
+
):
|
|
496
502
|
"""Template for vite.config.js.
|
|
497
503
|
|
|
498
504
|
Args:
|
|
499
505
|
base: The base path for the Vite config.
|
|
500
506
|
hmr: Whether to enable hot module replacement.
|
|
501
507
|
force_full_reload: Whether to force a full reload on changes.
|
|
508
|
+
experimental_hmr: Whether to enable experimental HMR features.
|
|
509
|
+
sourcemap: The sourcemap configuration.
|
|
502
510
|
|
|
503
511
|
Returns:
|
|
504
512
|
Rendered vite.config.js content as string.
|
|
@@ -550,6 +558,7 @@ export default defineConfig((config) => ({{
|
|
|
550
558
|
].concat({"[fullReload()]" if force_full_reload else "[]"}),
|
|
551
559
|
build: {{
|
|
552
560
|
assetsDir: "{base}assets".slice(1),
|
|
561
|
+
sourcemap: {"true" if sourcemap is True else "false" if sourcemap is False else repr(sourcemap)},
|
|
553
562
|
rollupOptions: {{
|
|
554
563
|
onwarn(warning, warn) {{
|
|
555
564
|
if (warning.code === "EVAL" && warning.id && warning.id.endsWith("state.js")) return;
|
|
@@ -570,6 +579,7 @@ export default defineConfig((config) => ({{
|
|
|
570
579
|
}},
|
|
571
580
|
experimental: {{
|
|
572
581
|
enableNativePlugin: false,
|
|
582
|
+
hmr: {"true" if experimental_hmr else "false"},
|
|
573
583
|
}},
|
|
574
584
|
server: {{
|
|
575
585
|
port: process.env.PORT,
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from reflex.components.component import Component
|
|
6
6
|
from reflex.components.datadisplay.logo import svg_logo
|
|
7
|
-
from reflex.components.el import a, button,
|
|
7
|
+
from reflex.components.el import a, button, div, h2, hr, p, pre, svg
|
|
8
8
|
from reflex.event import EventHandler, set_clipboard
|
|
9
9
|
from reflex.state import FrontendEventExceptionState
|
|
10
10
|
from reflex.vars.base import Var
|
|
@@ -65,53 +65,67 @@ class ErrorBoundary(Component):
|
|
|
65
65
|
div(
|
|
66
66
|
div(
|
|
67
67
|
div(
|
|
68
|
+
svg(
|
|
69
|
+
svg.circle(cx="12", cy="12", r="10"),
|
|
70
|
+
svg.path(d="M16 16s-1.5-2-4-2-4 2-4 2"),
|
|
71
|
+
svg.line(x1="9", x2="9.01", y1="9", y2="9"),
|
|
72
|
+
svg.line(x1="15", x2="15.01", y1="9", y2="9"),
|
|
73
|
+
xmlns="http://www.w3.org/2000/svg",
|
|
74
|
+
width="25vmin",
|
|
75
|
+
view_box="0 0 24 24",
|
|
76
|
+
class_name="lucide lucide-frown-icon lucide-frown",
|
|
77
|
+
custom_attrs={
|
|
78
|
+
"fill": "none",
|
|
79
|
+
"stroke": "currentColor",
|
|
80
|
+
"stroke-width": "2",
|
|
81
|
+
"stroke-linecap": "round",
|
|
82
|
+
"stroke-linejoin": "round",
|
|
83
|
+
},
|
|
84
|
+
),
|
|
68
85
|
h2(
|
|
69
86
|
"An error occurred while rendering this page.",
|
|
70
|
-
font_size="
|
|
87
|
+
font_size="5vmin",
|
|
71
88
|
font_weight="bold",
|
|
72
89
|
),
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
),
|
|
87
|
-
width="100%",
|
|
88
|
-
max_height="50vh",
|
|
89
|
-
overflow="auto",
|
|
90
|
-
background="#000",
|
|
91
|
-
color="#fff",
|
|
92
|
-
border_radius="0.25rem",
|
|
93
|
-
),
|
|
94
|
-
button(
|
|
95
|
-
"Copy",
|
|
96
|
-
on_click=set_clipboard(
|
|
97
|
-
Var(_js_expr=_ERROR_DISPLAY)
|
|
98
|
-
),
|
|
99
|
-
padding="0.35rem 0.75rem",
|
|
100
|
-
margin="0.5rem",
|
|
101
|
-
background="#fff",
|
|
102
|
-
color="#000",
|
|
103
|
-
border="1px solid #000",
|
|
104
|
-
border_radius="0.25rem",
|
|
105
|
-
font_weight="bold",
|
|
90
|
+
opacity="0.5",
|
|
91
|
+
display="flex",
|
|
92
|
+
gap="4vmin",
|
|
93
|
+
align_items="center",
|
|
94
|
+
),
|
|
95
|
+
p(
|
|
96
|
+
"This is an error with the application itself. Refreshing the page might help.",
|
|
97
|
+
opacity="0.75",
|
|
98
|
+
margin_block="1rem",
|
|
99
|
+
),
|
|
100
|
+
div(
|
|
101
|
+
div(
|
|
102
|
+
pre(
|
|
103
|
+
Var(_js_expr=_ERROR_DISPLAY),
|
|
106
104
|
),
|
|
105
|
+
padding="0.5rem",
|
|
106
|
+
width="fit-content",
|
|
107
107
|
),
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
108
|
+
width="100%",
|
|
109
|
+
background="color-mix(in srgb, currentColor 5%, transparent)",
|
|
110
|
+
max_height="15rem",
|
|
111
|
+
overflow="auto",
|
|
112
|
+
border_radius="0.4rem",
|
|
113
|
+
),
|
|
114
|
+
button(
|
|
115
|
+
"Copy",
|
|
116
|
+
on_click=set_clipboard(Var(_js_expr=_ERROR_DISPLAY)),
|
|
117
|
+
padding="0.35rem 1.35rem",
|
|
118
|
+
margin_block="0.5rem",
|
|
119
|
+
margin_inline_start="auto",
|
|
120
|
+
background="color-mix(in srgb, currentColor 15%, transparent)",
|
|
121
|
+
border_radius="0.4rem",
|
|
122
|
+
width="fit-content",
|
|
123
|
+
_hover={
|
|
124
|
+
"background": "color-mix(in srgb, currentColor 25%, transparent)"
|
|
125
|
+
},
|
|
126
|
+
_active={
|
|
127
|
+
"background": "color-mix(in srgb, currentColor 35%, transparent)"
|
|
128
|
+
},
|
|
115
129
|
),
|
|
116
130
|
hr(
|
|
117
131
|
border_color="currentColor",
|
|
@@ -131,7 +145,10 @@ class ErrorBoundary(Component):
|
|
|
131
145
|
),
|
|
132
146
|
display="flex",
|
|
133
147
|
flex_direction="column",
|
|
134
|
-
gap="
|
|
148
|
+
gap="0.5rem",
|
|
149
|
+
max_width="min(80ch, 90vw)",
|
|
150
|
+
border_radius="0.25rem",
|
|
151
|
+
padding="1rem",
|
|
135
152
|
),
|
|
136
153
|
height="100%",
|
|
137
154
|
width="100%",
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
"""Window event listener component for Reflex."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, cast
|
|
6
|
+
|
|
3
7
|
import reflex as rx
|
|
4
8
|
from reflex.components.base.fragment import Fragment
|
|
9
|
+
from reflex.components.component import StatefulComponent, field
|
|
5
10
|
from reflex.constants.compiler import Hooks
|
|
6
11
|
from reflex.event import key_event, no_args_event_spec
|
|
7
12
|
from reflex.vars.base import Var, VarData
|
|
@@ -61,6 +66,23 @@ class WindowEventListener(Fragment):
|
|
|
61
66
|
on_popstate: rx.EventHandler[no_args_event_spec]
|
|
62
67
|
on_storage: rx.EventHandler[_on_storage_spec]
|
|
63
68
|
|
|
69
|
+
hooks: list[str] = field(default_factory=list, is_javascript_property=False)
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def create(cls, **props) -> WindowEventListener:
|
|
73
|
+
"""Create a WindowEventListener component.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
**props: The props to set on the component.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
The created component.
|
|
80
|
+
"""
|
|
81
|
+
real_component = cast("WindowEventListener", super().create(**props))
|
|
82
|
+
hooks = StatefulComponent._fix_event_triggers(real_component)
|
|
83
|
+
real_component.hooks = hooks
|
|
84
|
+
return real_component
|
|
85
|
+
|
|
64
86
|
def _exclude_props(self) -> list[str]:
|
|
65
87
|
"""Exclude event handler props from being passed to Fragment.
|
|
66
88
|
|
|
@@ -69,31 +91,30 @@ class WindowEventListener(Fragment):
|
|
|
69
91
|
"""
|
|
70
92
|
return [*super()._exclude_props(), *self.event_triggers.keys()]
|
|
71
93
|
|
|
72
|
-
def add_hooks(self) -> list[str | Var[
|
|
94
|
+
def add_hooks(self) -> list[str | Var[Any]]:
|
|
73
95
|
"""Add hooks to register window event listeners.
|
|
74
96
|
|
|
75
97
|
Returns:
|
|
76
98
|
The hooks to add to the component.
|
|
77
99
|
"""
|
|
78
|
-
hooks = []
|
|
100
|
+
hooks: list[str | Var[Any]] = [*self.hooks]
|
|
79
101
|
|
|
80
102
|
for prop_name, event_trigger in self.event_triggers.items():
|
|
81
103
|
# Get JS event name: remove on_ prefix and underscores
|
|
82
104
|
event_name = prop_name.removeprefix("on_").replace("_", "")
|
|
83
105
|
|
|
84
106
|
hook_expr = f"""
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
107
|
+
useEffect(() => {{
|
|
108
|
+
if (typeof window === 'undefined') return;
|
|
109
|
+
const fn = {Var.create(event_trigger)};
|
|
110
|
+
window.addEventListener('{event_name}', fn);
|
|
111
|
+
return () => window.removeEventListener('{event_name}', fn);
|
|
112
|
+
}}, []);
|
|
91
113
|
"""
|
|
92
114
|
|
|
93
115
|
hooks.append(
|
|
94
116
|
Var(
|
|
95
117
|
hook_expr,
|
|
96
|
-
_var_type="str",
|
|
97
118
|
_var_data=VarData(position=Hooks.HookPosition.POST_TRIGGER),
|
|
98
119
|
)
|
|
99
120
|
)
|
|
@@ -12,11 +12,11 @@ from reflex.event import EventType, KeyInputInfo, PointerEventInfo
|
|
|
12
12
|
from reflex.vars.base import Var
|
|
13
13
|
|
|
14
14
|
class WindowEventListener(Fragment):
|
|
15
|
-
def add_hooks(self) -> list[str | Var[str]]: ...
|
|
16
15
|
@classmethod
|
|
17
16
|
def create(
|
|
18
17
|
cls,
|
|
19
18
|
*children,
|
|
19
|
+
hooks: list[str] | None = None,
|
|
20
20
|
style: Sequence[Mapping[str, Any]]
|
|
21
21
|
| Mapping[str, Any]
|
|
22
22
|
| Var[Mapping[str, Any]]
|
|
@@ -62,10 +62,9 @@ class WindowEventListener(Fragment):
|
|
|
62
62
|
on_visibility_change: EventType[()] | EventType[bool] | None = None,
|
|
63
63
|
**props,
|
|
64
64
|
) -> WindowEventListener:
|
|
65
|
-
"""Create
|
|
65
|
+
"""Create a WindowEventListener component.
|
|
66
66
|
|
|
67
67
|
Args:
|
|
68
|
-
*children: The children of the component.
|
|
69
68
|
on_resize: Event handlers
|
|
70
69
|
style: The style of the component.
|
|
71
70
|
key: A unique key for the component.
|
|
@@ -73,10 +72,12 @@ class WindowEventListener(Fragment):
|
|
|
73
72
|
ref: The Var to pass as the ref to the component.
|
|
74
73
|
class_name: The class name for the component.
|
|
75
74
|
custom_attrs: custom attribute
|
|
76
|
-
**props: The props
|
|
75
|
+
**props: The props to set on the component.
|
|
77
76
|
|
|
78
77
|
Returns:
|
|
79
|
-
The component.
|
|
78
|
+
The created component.
|
|
80
79
|
"""
|
|
81
80
|
|
|
81
|
+
def add_hooks(self) -> list[str | Var[Any]]: ...
|
|
82
|
+
|
|
82
83
|
window_event_listener = WindowEventListener.create
|
reflex/components/lucide/icon.py
CHANGED
|
@@ -6,7 +6,7 @@ from reflex.utils.imports import ImportVar
|
|
|
6
6
|
from reflex.vars.base import LiteralVar, Var
|
|
7
7
|
from reflex.vars.sequence import LiteralStringVar, StringVar
|
|
8
8
|
|
|
9
|
-
LUCIDE_LIBRARY = "lucide-react@0.
|
|
9
|
+
LUCIDE_LIBRARY = "lucide-react@0.546.0"
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class LucideIconComponent(Component):
|
|
@@ -1115,6 +1115,7 @@ LUCIDE_ICON_LIST = [
|
|
|
1115
1115
|
"minimize",
|
|
1116
1116
|
"minus",
|
|
1117
1117
|
"monitor_check",
|
|
1118
|
+
"monitor_cloud",
|
|
1118
1119
|
"monitor_cog",
|
|
1119
1120
|
"monitor_dot",
|
|
1120
1121
|
"monitor_down",
|
|
@@ -11,7 +11,7 @@ from reflex.components.core.breakpoints import Breakpoints
|
|
|
11
11
|
from reflex.event import EventType, PointerEventInfo
|
|
12
12
|
from reflex.vars.base import Var
|
|
13
13
|
|
|
14
|
-
LUCIDE_LIBRARY = "lucide-react@0.
|
|
14
|
+
LUCIDE_LIBRARY = "lucide-react@0.546.0"
|
|
15
15
|
|
|
16
16
|
class LucideIconComponent(Component):
|
|
17
17
|
@classmethod
|
|
@@ -1176,6 +1176,7 @@ LUCIDE_ICON_LIST = [
|
|
|
1176
1176
|
"minimize",
|
|
1177
1177
|
"minus",
|
|
1178
1178
|
"monitor_check",
|
|
1179
|
+
"monitor_cloud",
|
|
1179
1180
|
"monitor_cog",
|
|
1180
1181
|
"monitor_dot",
|
|
1181
1182
|
"monitor_down",
|
|
@@ -72,7 +72,7 @@ class Plotly(NoSSRComponent):
|
|
|
72
72
|
|
|
73
73
|
library = "react-plotly.js@2.6.0"
|
|
74
74
|
|
|
75
|
-
lib_dependencies: list[str] = ["plotly.js@3.1.
|
|
75
|
+
lib_dependencies: list[str] = ["plotly.js@3.1.2"]
|
|
76
76
|
|
|
77
77
|
tag = "Plot"
|
|
78
78
|
|
|
@@ -303,7 +303,7 @@ class PlotlyBasic(Plotly):
|
|
|
303
303
|
|
|
304
304
|
library = "react-plotly.js@2.6.0"
|
|
305
305
|
|
|
306
|
-
lib_dependencies: list[str] = ["plotly.js-basic-dist-min@3.1.
|
|
306
|
+
lib_dependencies: list[str] = ["plotly.js-basic-dist-min@3.1.2"]
|
|
307
307
|
|
|
308
308
|
def add_imports(self) -> ImportDict | list[ImportDict]:
|
|
309
309
|
"""Add imports for the plotly basic component.
|
|
@@ -329,7 +329,7 @@ class PlotlyCartesian(Plotly):
|
|
|
329
329
|
|
|
330
330
|
library = "react-plotly.js@2.6.0"
|
|
331
331
|
|
|
332
|
-
lib_dependencies: list[str] = ["plotly.js-cartesian-dist-min@3.1.
|
|
332
|
+
lib_dependencies: list[str] = ["plotly.js-cartesian-dist-min@3.1.2"]
|
|
333
333
|
|
|
334
334
|
def add_imports(self) -> ImportDict | list[ImportDict]:
|
|
335
335
|
"""Add imports for the plotly cartesian component.
|
|
@@ -355,7 +355,7 @@ class PlotlyGeo(Plotly):
|
|
|
355
355
|
|
|
356
356
|
library = "react-plotly.js@2.6.0"
|
|
357
357
|
|
|
358
|
-
lib_dependencies: list[str] = ["plotly.js-geo-dist-min@3.1.
|
|
358
|
+
lib_dependencies: list[str] = ["plotly.js-geo-dist-min@3.1.2"]
|
|
359
359
|
|
|
360
360
|
def add_imports(self) -> ImportDict | list[ImportDict]:
|
|
361
361
|
"""Add imports for the plotly geo component.
|
|
@@ -381,7 +381,7 @@ class PlotlyGl3d(Plotly):
|
|
|
381
381
|
|
|
382
382
|
library = "react-plotly.js@2.6.0"
|
|
383
383
|
|
|
384
|
-
lib_dependencies: list[str] = ["plotly.js-gl3d-dist-min@3.1.
|
|
384
|
+
lib_dependencies: list[str] = ["plotly.js-gl3d-dist-min@3.1.2"]
|
|
385
385
|
|
|
386
386
|
def add_imports(self) -> ImportDict | list[ImportDict]:
|
|
387
387
|
"""Add imports for the plotly 3d component.
|
|
@@ -407,7 +407,7 @@ class PlotlyGl2d(Plotly):
|
|
|
407
407
|
|
|
408
408
|
library = "react-plotly.js@2.6.0"
|
|
409
409
|
|
|
410
|
-
lib_dependencies: list[str] = ["plotly.js-gl2d-dist-min@3.1.
|
|
410
|
+
lib_dependencies: list[str] = ["plotly.js-gl2d-dist-min@3.1.2"]
|
|
411
411
|
|
|
412
412
|
def add_imports(self) -> ImportDict | list[ImportDict]:
|
|
413
413
|
"""Add imports for the plotly 2d component.
|
|
@@ -433,7 +433,7 @@ class PlotlyMapbox(Plotly):
|
|
|
433
433
|
|
|
434
434
|
library = "react-plotly.js@2.6.0"
|
|
435
435
|
|
|
436
|
-
lib_dependencies: list[str] = ["plotly.js-mapbox-dist-min@3.1.
|
|
436
|
+
lib_dependencies: list[str] = ["plotly.js-mapbox-dist-min@3.1.2"]
|
|
437
437
|
|
|
438
438
|
def add_imports(self) -> ImportDict | list[ImportDict]:
|
|
439
439
|
"""Add imports for the plotly mapbox component.
|
|
@@ -459,7 +459,7 @@ class PlotlyFinance(Plotly):
|
|
|
459
459
|
|
|
460
460
|
library = "react-plotly.js@2.6.0"
|
|
461
461
|
|
|
462
|
-
lib_dependencies: list[str] = ["plotly.js-finance-dist-min@3.1.
|
|
462
|
+
lib_dependencies: list[str] = ["plotly.js-finance-dist-min@3.1.2"]
|
|
463
463
|
|
|
464
464
|
def add_imports(self) -> ImportDict | list[ImportDict]:
|
|
465
465
|
"""Add imports for the plotly finance component.
|
|
@@ -485,7 +485,7 @@ class PlotlyStrict(Plotly):
|
|
|
485
485
|
|
|
486
486
|
library = "react-plotly.js@2.6.0"
|
|
487
487
|
|
|
488
|
-
lib_dependencies: list[str] = ["plotly.js-strict-dist-min@3.1.
|
|
488
|
+
lib_dependencies: list[str] = ["plotly.js-strict-dist-min@3.1.2"]
|
|
489
489
|
|
|
490
490
|
def add_imports(self) -> ImportDict | list[ImportDict]:
|
|
491
491
|
"""Add imports for the plotly strict component.
|
|
@@ -8,7 +8,7 @@ from reflex.components.component import Component, MemoizationLeaf, NoSSRCompone
|
|
|
8
8
|
class Recharts(Component):
|
|
9
9
|
"""A component that wraps a recharts lib."""
|
|
10
10
|
|
|
11
|
-
library = "recharts@3.
|
|
11
|
+
library = "recharts@3.3.0"
|
|
12
12
|
|
|
13
13
|
def _get_style(self) -> dict:
|
|
14
14
|
return {"wrapperStyle": self.style}
|
|
@@ -17,7 +17,7 @@ class Recharts(Component):
|
|
|
17
17
|
class RechartsCharts(NoSSRComponent, MemoizationLeaf):
|
|
18
18
|
"""A component that wraps a recharts lib."""
|
|
19
19
|
|
|
20
|
-
library = "recharts@3.
|
|
20
|
+
library = "recharts@3.3.0"
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
LiteralAnimationEasing = Literal["ease", "ease-in", "ease-out", "ease-in-out", "linear"]
|
reflex/constants/installer.py
CHANGED
|
@@ -143,11 +143,11 @@ class PackageJson(SimpleNamespace):
|
|
|
143
143
|
"postcss-import": "16.1.1",
|
|
144
144
|
"@react-router/dev": _react_router_version,
|
|
145
145
|
"@react-router/fs-routes": _react_router_version,
|
|
146
|
-
"vite": "npm:rolldown-vite@7.1.
|
|
146
|
+
"vite": "npm:rolldown-vite@7.1.18",
|
|
147
147
|
}
|
|
148
148
|
OVERRIDES = {
|
|
149
149
|
# This should always match the `react` version in DEPENDENCIES for recharts compatibility.
|
|
150
150
|
"react-is": _react_version,
|
|
151
151
|
"cookie": "1.0.2",
|
|
152
|
-
"vite": "npm:rolldown-vite@7.1.
|
|
152
|
+
"vite": "npm:rolldown-vite@7.1.18",
|
|
153
153
|
}
|
reflex/environment.py
CHANGED
|
@@ -17,6 +17,7 @@ from typing import (
|
|
|
17
17
|
Annotated,
|
|
18
18
|
Any,
|
|
19
19
|
Generic,
|
|
20
|
+
Literal,
|
|
20
21
|
TypeVar,
|
|
21
22
|
get_args,
|
|
22
23
|
get_origin,
|
|
@@ -256,6 +257,27 @@ def interpret_env_var_value(
|
|
|
256
257
|
return interpret_existing_path_env(value, field_name)
|
|
257
258
|
if field_type is Plugin:
|
|
258
259
|
return interpret_plugin_env(value, field_name)
|
|
260
|
+
if get_origin(field_type) is Literal:
|
|
261
|
+
literal_values = get_args(field_type)
|
|
262
|
+
for literal_value in literal_values:
|
|
263
|
+
if isinstance(literal_value, str) and literal_value == value:
|
|
264
|
+
return literal_value
|
|
265
|
+
if isinstance(literal_value, bool):
|
|
266
|
+
try:
|
|
267
|
+
interpreted_bool = interpret_boolean_env(value, field_name)
|
|
268
|
+
if interpreted_bool == literal_value:
|
|
269
|
+
return interpreted_bool
|
|
270
|
+
except EnvironmentVarValueError:
|
|
271
|
+
continue
|
|
272
|
+
if isinstance(literal_value, int):
|
|
273
|
+
try:
|
|
274
|
+
interpreted_int = interpret_int_env(value, field_name)
|
|
275
|
+
if interpreted_int == literal_value:
|
|
276
|
+
return interpreted_int
|
|
277
|
+
except EnvironmentVarValueError:
|
|
278
|
+
continue
|
|
279
|
+
msg = f"Invalid literal value: {value!r} for {field_name}, expected one of {literal_values}"
|
|
280
|
+
raise EnvironmentVarValueError(msg)
|
|
259
281
|
if get_origin(field_type) in (list, Sequence):
|
|
260
282
|
return [
|
|
261
283
|
interpret_env_var_value(
|
|
@@ -687,6 +709,12 @@ class EnvironmentVariables:
|
|
|
687
709
|
# Whether to force a full reload on changes.
|
|
688
710
|
VITE_FORCE_FULL_RELOAD: EnvVar[bool] = env_var(False)
|
|
689
711
|
|
|
712
|
+
# Whether to enable Rolldown's experimental HMR.
|
|
713
|
+
VITE_EXPERIMENTAL_HMR: EnvVar[bool] = env_var(False)
|
|
714
|
+
|
|
715
|
+
# Whether to generate sourcemaps for the frontend.
|
|
716
|
+
VITE_SOURCEMAP: EnvVar[Literal[False, True, "inline", "hidden"]] = env_var(False) # noqa: RUF038
|
|
717
|
+
|
|
690
718
|
# Whether to enable SSR for the frontend.
|
|
691
719
|
REFLEX_SSR: EnvVar[bool] = env_var(True)
|
|
692
720
|
|
|
@@ -696,6 +724,9 @@ class EnvironmentVariables:
|
|
|
696
724
|
# How long to delay writing updated states to disk. (Higher values mean less writes, but more chance of lost data.)
|
|
697
725
|
REFLEX_STATE_MANAGER_DISK_DEBOUNCE_SECONDS: EnvVar[float] = env_var(2.0)
|
|
698
726
|
|
|
727
|
+
# How long to wait between automatic reload on frontend error to avoid reload loops.
|
|
728
|
+
REFLEX_AUTO_RELOAD_COOLDOWN_TIME_MS: EnvVar[int] = env_var(10_000)
|
|
729
|
+
|
|
699
730
|
|
|
700
731
|
environment = EnvironmentVariables()
|
|
701
732
|
|
|
@@ -4,14 +4,27 @@ import contextlib
|
|
|
4
4
|
import dataclasses
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
6
|
from collections.abc import AsyncIterator
|
|
7
|
+
from typing import TypedDict
|
|
8
|
+
|
|
9
|
+
from typing_extensions import ReadOnly, Unpack
|
|
7
10
|
|
|
8
11
|
from reflex import constants
|
|
9
12
|
from reflex.config import get_config
|
|
13
|
+
from reflex.event import Event
|
|
10
14
|
from reflex.state import BaseState
|
|
11
15
|
from reflex.utils import console, prerequisites
|
|
12
16
|
from reflex.utils.exceptions import InvalidStateManagerModeError
|
|
13
17
|
|
|
14
18
|
|
|
19
|
+
class StateModificationContext(TypedDict, total=False):
|
|
20
|
+
"""The context for modifying state."""
|
|
21
|
+
|
|
22
|
+
event: ReadOnly[Event]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
EmptyContext = StateModificationContext()
|
|
26
|
+
|
|
27
|
+
|
|
15
28
|
@dataclasses.dataclass
|
|
16
29
|
class StateManager(ABC):
|
|
17
30
|
"""A class to manage many client states."""
|
|
@@ -71,21 +84,30 @@ class StateManager(ABC):
|
|
|
71
84
|
"""
|
|
72
85
|
|
|
73
86
|
@abstractmethod
|
|
74
|
-
async def set_state(
|
|
87
|
+
async def set_state(
|
|
88
|
+
self,
|
|
89
|
+
token: str,
|
|
90
|
+
state: BaseState,
|
|
91
|
+
**context: Unpack[StateModificationContext],
|
|
92
|
+
):
|
|
75
93
|
"""Set the state for a token.
|
|
76
94
|
|
|
77
95
|
Args:
|
|
78
96
|
token: The token to set the state for.
|
|
79
97
|
state: The state to set.
|
|
98
|
+
context: The state modification context.
|
|
80
99
|
"""
|
|
81
100
|
|
|
82
101
|
@abstractmethod
|
|
83
102
|
@contextlib.asynccontextmanager
|
|
84
|
-
async def modify_state(
|
|
103
|
+
async def modify_state(
|
|
104
|
+
self, token: str, **context: Unpack[StateModificationContext]
|
|
105
|
+
) -> AsyncIterator[BaseState]:
|
|
85
106
|
"""Modify the state for a token while holding exclusive lock.
|
|
86
107
|
|
|
87
108
|
Args:
|
|
88
109
|
token: The token to modify the state for.
|
|
110
|
+
context: The state modification context.
|
|
89
111
|
|
|
90
112
|
Yields:
|
|
91
113
|
The state for the token.
|
reflex/istate/manager/disk.py
CHANGED
|
@@ -9,10 +9,14 @@ from collections.abc import AsyncIterator
|
|
|
9
9
|
from hashlib import md5
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
|
-
from typing_extensions import override
|
|
12
|
+
from typing_extensions import Unpack, override
|
|
13
13
|
|
|
14
14
|
from reflex.environment import environment
|
|
15
|
-
from reflex.istate.manager import
|
|
15
|
+
from reflex.istate.manager import (
|
|
16
|
+
StateManager,
|
|
17
|
+
StateModificationContext,
|
|
18
|
+
_default_token_expiration,
|
|
19
|
+
)
|
|
16
20
|
from reflex.state import BaseState, _split_substate_key, _substate_key
|
|
17
21
|
from reflex.utils import console, path_ops, prerequisites
|
|
18
22
|
from reflex.utils.misc import run_in_thread
|
|
@@ -301,12 +305,15 @@ class StateManagerDisk(StateManager):
|
|
|
301
305
|
await asyncio.sleep(0) # Yield to allow the task to start.
|
|
302
306
|
|
|
303
307
|
@override
|
|
304
|
-
async def set_state(
|
|
308
|
+
async def set_state(
|
|
309
|
+
self, token: str, state: BaseState, **context: Unpack[StateModificationContext]
|
|
310
|
+
):
|
|
305
311
|
"""Set the state for a token.
|
|
306
312
|
|
|
307
313
|
Args:
|
|
308
314
|
token: The token to set the state for.
|
|
309
315
|
state: The state to set.
|
|
316
|
+
context: The state modification context.
|
|
310
317
|
"""
|
|
311
318
|
client_token, _ = _split_substate_key(token)
|
|
312
319
|
if self._write_debounce_seconds > 0:
|
|
@@ -325,11 +332,14 @@ class StateManagerDisk(StateManager):
|
|
|
325
332
|
|
|
326
333
|
@override
|
|
327
334
|
@contextlib.asynccontextmanager
|
|
328
|
-
async def modify_state(
|
|
335
|
+
async def modify_state(
|
|
336
|
+
self, token: str, **context: Unpack[StateModificationContext]
|
|
337
|
+
) -> AsyncIterator[BaseState]:
|
|
329
338
|
"""Modify the state for a token while holding exclusive lock.
|
|
330
339
|
|
|
331
340
|
Args:
|
|
332
341
|
token: The token to modify the state for.
|
|
342
|
+
context: The state modification context.
|
|
333
343
|
|
|
334
344
|
Yields:
|
|
335
345
|
The state for the token.
|
|
@@ -344,7 +354,7 @@ class StateManagerDisk(StateManager):
|
|
|
344
354
|
async with self._states_locks[client_token]:
|
|
345
355
|
state = await self.get_state(token)
|
|
346
356
|
yield state
|
|
347
|
-
await self.set_state(token, state)
|
|
357
|
+
await self.set_state(token, state, **context)
|
|
348
358
|
|
|
349
359
|
async def close(self):
|
|
350
360
|
"""Close the state manager, flushing any pending writes to disk."""
|