reflex 0.8.16__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.

@@ -665,7 +665,9 @@ export const connect = async (
665
665
  }
666
666
  }
667
667
  applyClientStorageDelta(client_storage, update.delta);
668
- event_processing = !update.final;
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(self, token: str) -> AsyncIterator[BaseState]:
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(delta=delta),
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(event.substate_token) as 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 (
@@ -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(base: str, hmr: bool, force_full_reload: bool):
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, details, div, h2, hr, p, pre, summary
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="1.25rem",
87
+ font_size="5vmin",
71
88
  font_weight="bold",
72
89
  ),
73
- p(
74
- "This is an error with the application itself.",
75
- opacity="0.75",
76
- ),
77
- details(
78
- summary("Error message", padding="0.5rem"),
79
- div(
80
- div(
81
- pre(
82
- Var(_js_expr=_ERROR_DISPLAY),
83
- ),
84
- padding="0.5rem",
85
- width="fit-content",
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
- display="flex",
109
- flex_direction="column",
110
- gap="1rem",
111
- max_width="50ch",
112
- border="1px solid #888888",
113
- border_radius="0.25rem",
114
- padding="1rem",
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="1rem",
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[str]]:
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
- useEffect(() => {{
86
- if (typeof window === 'undefined') return;
87
-
88
- window.addEventListener('{event_name}', {event_trigger});
89
- return () => window.removeEventListener('{event_name}', {event_trigger});
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
  )
@@ -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.545.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.545.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.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.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.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.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.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.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.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.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.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.2.1"
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.2.1"
20
+ library = "recharts@3.3.0"
21
21
 
22
22
 
23
23
  LiteralAnimationEasing = Literal["ease", "ease-in", "ease-out", "ease-in-out", "linear"]
@@ -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.17",
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.17",
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(self, token: str, state: BaseState):
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(self, token: str) -> AsyncIterator[BaseState]:
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.
@@ -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 StateManager, _default_token_expiration
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(self, token: str, state: BaseState):
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(self, token: str) -> AsyncIterator[BaseState]:
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."""
@@ -5,9 +5,9 @@ import contextlib
5
5
  import dataclasses
6
6
  from collections.abc import AsyncIterator
7
7
 
8
- from typing_extensions import override
8
+ from typing_extensions import Unpack, override
9
9
 
10
- from reflex.istate.manager import StateManager
10
+ from reflex.istate.manager import StateManager, StateModificationContext
11
11
  from reflex.state import BaseState, _split_substate_key
12
12
 
13
13
 
@@ -43,23 +43,32 @@ class StateManagerMemory(StateManager):
43
43
  return self.states[token]
44
44
 
45
45
  @override
46
- async def set_state(self, token: str, state: BaseState):
46
+ async def set_state(
47
+ self,
48
+ token: str,
49
+ state: BaseState,
50
+ **context: Unpack[StateModificationContext],
51
+ ):
47
52
  """Set the state for a token.
48
53
 
49
54
  Args:
50
55
  token: The token to set the state for.
51
56
  state: The state to set.
57
+ context: The state modification context.
52
58
  """
53
59
  token = _split_substate_key(token)[0]
54
60
  self.states[token] = state
55
61
 
56
62
  @override
57
63
  @contextlib.asynccontextmanager
58
- async def modify_state(self, token: str) -> AsyncIterator[BaseState]:
64
+ async def modify_state(
65
+ self, token: str, **context: Unpack[StateModificationContext]
66
+ ) -> AsyncIterator[BaseState]:
59
67
  """Modify the state for a token while holding exclusive lock.
60
68
 
61
69
  Args:
62
70
  token: The token to modify the state for.
71
+ context: The state modification context.
63
72
 
64
73
  Yields:
65
74
  The state for the token.
@@ -10,11 +10,15 @@ from collections.abc import AsyncIterator
10
10
  from redis import ResponseError
11
11
  from redis.asyncio import Redis
12
12
  from redis.asyncio.client import PubSub
13
- from typing_extensions import override
13
+ from typing_extensions import Unpack, override
14
14
 
15
15
  from reflex.config import get_config
16
16
  from reflex.environment import environment
17
- from reflex.istate.manager import StateManager, _default_token_expiration
17
+ from reflex.istate.manager import (
18
+ StateManager,
19
+ StateModificationContext,
20
+ _default_token_expiration,
21
+ )
18
22
  from reflex.state import BaseState, _split_substate_key, _substate_key
19
23
  from reflex.utils import console
20
24
  from reflex.utils.exceptions import (
@@ -261,7 +265,9 @@ class StateManagerRedis(StateManager):
261
265
  self,
262
266
  token: str,
263
267
  state: BaseState,
268
+ *,
264
269
  lock_id: bytes | None = None,
270
+ **context: Unpack[StateModificationContext],
265
271
  ):
266
272
  """Set the state for a token.
267
273
 
@@ -269,6 +275,7 @@ class StateManagerRedis(StateManager):
269
275
  token: The token to set the state for.
270
276
  state: The state to set.
271
277
  lock_id: If provided, the lock_key must be set to this value to set the state.
278
+ context: The event context.
272
279
 
273
280
  Raises:
274
281
  LockExpiredError: If lock_id is provided and the lock for the token is not held by that ID.
@@ -283,6 +290,11 @@ class StateManagerRedis(StateManager):
283
290
  f"Lock expired for token {token} while processing. Consider increasing "
284
291
  f"`app.state_manager.lock_expiration` (currently {self.lock_expiration}) "
285
292
  "or use `@rx.event(background=True)` decorator for long-running tasks."
293
+ + (
294
+ f" Happened in event: {event.name}"
295
+ if (event := context.get("event")) is not None
296
+ else ""
297
+ )
286
298
  )
287
299
  raise LockExpiredError(msg)
288
300
  if lock_id is not None:
@@ -292,7 +304,12 @@ class StateManagerRedis(StateManager):
292
304
  if time_taken > self.lock_warning_threshold / 1000:
293
305
  console.warn(
294
306
  f"Lock for token {token} was held too long {time_taken=}s, "
295
- f"use `@rx.event(background=True)` decorator for long-running tasks.",
307
+ f"use `@rx.event(background=True)` decorator for long-running tasks."
308
+ + (
309
+ f" Happened in event: {event.name}"
310
+ if (event := context.get("event")) is not None
311
+ else ""
312
+ ),
296
313
  dedupe=True,
297
314
  )
298
315
 
@@ -308,7 +325,8 @@ class StateManagerRedis(StateManager):
308
325
  self.set_state(
309
326
  _substate_key(client_token, substate),
310
327
  substate,
311
- lock_id,
328
+ lock_id=lock_id,
329
+ **context,
312
330
  ),
313
331
  name=f"reflex_set_state|{client_token}|{substate.get_full_name()}",
314
332
  )
@@ -330,11 +348,14 @@ class StateManagerRedis(StateManager):
330
348
 
331
349
  @override
332
350
  @contextlib.asynccontextmanager
333
- async def modify_state(self, token: str) -> AsyncIterator[BaseState]:
351
+ async def modify_state(
352
+ self, token: str, **context: Unpack[StateModificationContext]
353
+ ) -> AsyncIterator[BaseState]:
334
354
  """Modify the state for a token while holding exclusive lock.
335
355
 
336
356
  Args:
337
357
  token: The token to modify the state for.
358
+ context: The state modification context.
338
359
 
339
360
  Yields:
340
361
  The state for the token.
@@ -342,7 +363,7 @@ class StateManagerRedis(StateManager):
342
363
  async with self._lock(token) as lock_id:
343
364
  state = await self.get_state(token)
344
365
  yield state
345
- await self.set_state(token, state, lock_id)
366
+ await self.set_state(token, state, lock_id=lock_id, **context)
346
367
 
347
368
  @staticmethod
348
369
  def _lock_key(token: str) -> bytes:
reflex/istate/proxy.py CHANGED
@@ -131,7 +131,9 @@ class StateProxy(wrapt.ObjectProxy):
131
131
 
132
132
  await self._self_actx_lock.acquire()
133
133
  self._self_actx_lock_holder = current_task
134
- self._self_actx = self._self_app.modify_state(token=self._self_substate_token)
134
+ self._self_actx = self._self_app.modify_state(
135
+ token=self._self_substate_token, background=True
136
+ )
135
137
  mutable_state = await self._self_actx.__aenter__()
136
138
  super().__setattr__(
137
139
  "__wrapped__", mutable_state.get_substate(self._self_substate_path)
@@ -198,7 +200,7 @@ class StateProxy(wrapt.ObjectProxy):
198
200
  )
199
201
  raise ImmutableStateError(msg)
200
202
 
201
- value = super().__getattr__(name)
203
+ value = super().__getattr__(name) # pyright: ignore[reportAttributeAccessIssue]
202
204
  if not name.startswith("_self_") and isinstance(value, MutableProxy):
203
205
  # ensure mutations to these containers are blocked unless proxy is _mutable
204
206
  return ImmutableMutableProxy(
@@ -397,7 +399,7 @@ class MutableProxy(wrapt.ObjectProxy):
397
399
  }
398
400
 
399
401
  # Dynamically generated classes for tracking dataclass mutations.
400
- __dataclass_proxies__: dict[type, type] = {}
402
+ __dataclass_proxies__: dict[str, type] = {}
401
403
 
402
404
  def __new__(cls, wrapped: Any, *args, **kwargs) -> MutableProxy:
403
405
  """Create a proxy instance for a mutable object that tracks changes.
@@ -427,7 +429,7 @@ class MutableProxy(wrapt.ObjectProxy):
427
429
  },
428
430
  )
429
431
  cls = cls.__dataclass_proxies__[wrapper_cls_name]
430
- return super().__new__(cls)
432
+ return super().__new__(cls) # pyright: ignore[reportArgumentType]
431
433
 
432
434
  def __init__(self, wrapped: Any, state: BaseState, field_name: str):
433
435
  """Create a proxy for a mutable object that tracks changes.
@@ -543,7 +545,7 @@ class MutableProxy(wrapt.ObjectProxy):
543
545
  Returns:
544
546
  The attribute value.
545
547
  """
546
- value = super().__getattr__(__name)
548
+ value = super().__getattr__(__name) # pyright: ignore[reportAttributeAccessIssue]
547
549
 
548
550
  if callable(value):
549
551
  if __name in self.__mark_dirty_attrs__:
@@ -554,7 +556,7 @@ class MutableProxy(wrapt.ObjectProxy):
554
556
  # Wrap methods that may return mutable objects tied to the state.
555
557
  value = wrapt.FunctionWrapper(
556
558
  value,
557
- self._wrap_recursive_decorator,
559
+ self._wrap_recursive_decorator, # pyright: ignore[reportArgumentType]
558
560
  )
559
561
 
560
562
  if (
@@ -564,8 +566,8 @@ class MutableProxy(wrapt.ObjectProxy):
564
566
  ):
565
567
  # Wrap methods called on Base subclasses, which might do _anything_
566
568
  return wrapt.FunctionWrapper(
567
- functools.partial(value.__func__, self), # pyright: ignore [reportFunctionMemberAccess]
568
- self._wrap_recursive_decorator,
569
+ functools.partial(value.__func__, self), # pyright: ignore [reportFunctionMemberAccess, reportAttributeAccessIssue]
570
+ self._wrap_recursive_decorator, # pyright: ignore[reportArgumentType]
569
571
  )
570
572
 
571
573
  if is_mutable_type(type(value)) and __name not in (
@@ -587,7 +589,7 @@ class MutableProxy(wrapt.ObjectProxy):
587
589
  Returns:
588
590
  The item value.
589
591
  """
590
- value = super().__getitem__(key)
592
+ value = super().__getitem__(key) # pyright: ignore[reportAttributeAccessIssue]
591
593
  if isinstance(key, slice) and isinstance(value, list):
592
594
  return [self._wrap_recursive(item) for item in value]
593
595
  # Recursively wrap mutable items retrieved through this proxy.
@@ -599,7 +601,7 @@ class MutableProxy(wrapt.ObjectProxy):
599
601
  Yields:
600
602
  Each item value (possibly wrapped in MutableProxy).
601
603
  """
602
- for value in super().__iter__():
604
+ for value in super().__iter__(): # pyright: ignore[reportAttributeAccessIssue]
603
605
  # Recursively wrap mutable items retrieved through this proxy.
604
606
  yield self._wrap_recursive(value)
605
607
 
@@ -617,7 +619,7 @@ class MutableProxy(wrapt.ObjectProxy):
617
619
  Args:
618
620
  key: The key of the item.
619
621
  """
620
- self._mark_dirty(super().__delitem__, args=(key,))
622
+ self._mark_dirty(super().__delitem__, args=(key,)) # pyright: ignore[reportAttributeAccessIssue]
621
623
 
622
624
  def __setitem__(self, key: str, value: Any):
623
625
  """Set the item on the proxied object and mark state dirty.
@@ -626,7 +628,7 @@ class MutableProxy(wrapt.ObjectProxy):
626
628
  key: The key of the item.
627
629
  value: The value of the item.
628
630
  """
629
- self._mark_dirty(super().__setitem__, args=(key, value))
631
+ self._mark_dirty(super().__setitem__, args=(key, value)) # pyright: ignore[reportAttributeAccessIssue]
630
632
 
631
633
  def __setattr__(self, name: str, value: Any):
632
634
  """Set the attribute on the proxied object and mark state dirty.
@@ -17,7 +17,7 @@ class Constants(SimpleNamespace):
17
17
  """Tailwind constants."""
18
18
 
19
19
  # The Tailwindcss version
20
- VERSION = "tailwindcss@4.1.14"
20
+ VERSION = "tailwindcss@4.1.15"
21
21
  # The Tailwind config.
22
22
  CONFIG = "tailwind.config.js"
23
23
  # Default Tailwind content paths
@@ -156,7 +156,7 @@ class TailwindV4Plugin(TailwindPlugin):
156
156
  return [
157
157
  *super().get_frontend_development_dependencies(**context),
158
158
  Constants.VERSION,
159
- "@tailwindcss/postcss@4.1.14",
159
+ "@tailwindcss/postcss@4.1.15",
160
160
  ]
161
161
 
162
162
  def pre_compile(self, **context):
reflex/state.py CHANGED
@@ -11,11 +11,12 @@ import datetime
11
11
  import functools
12
12
  import inspect
13
13
  import pickle
14
+ import re
14
15
  import sys
15
16
  import time
16
17
  import typing
17
18
  import warnings
18
- from collections.abc import AsyncIterator, Callable, Sequence
19
+ from collections.abc import AsyncIterator, Callable, Iterator, Sequence
19
20
  from enum import Enum
20
21
  from hashlib import md5
21
22
  from importlib.util import find_spec
@@ -34,6 +35,7 @@ from reflex.event import (
34
35
  Event,
35
36
  EventHandler,
36
37
  EventSpec,
38
+ call_script,
37
39
  fix_events,
38
40
  )
39
41
  from reflex.istate import HANDLED_PICKLE_ERRORS, debug_failed_pickles
@@ -523,15 +525,17 @@ class BaseState(EvenMoreBasicBaseState):
523
525
 
524
526
  new_backend_vars = {
525
527
  name: value if not isinstance(value, Field) else value.default_value()
526
- for name, value in list(cls.__dict__.items())
527
- if types.is_backend_base_variable(name, cls)
528
+ for mixin_cls in [*cls._mixins(), cls]
529
+ for name, value in list(mixin_cls.__dict__.items())
530
+ if types.is_backend_base_variable(name, mixin_cls)
528
531
  }
529
532
  # Add annotated backend vars that may not have a default value.
530
533
  new_backend_vars.update({
531
534
  name: cls._get_var_default(name, annotation_value)
532
- for name, annotation_value in cls._get_type_hints().items()
535
+ for mixin_cls in [*cls._mixins(), cls]
536
+ for name, annotation_value in mixin_cls._get_type_hints().items()
533
537
  if name not in new_backend_vars
534
- and types.is_backend_base_variable(name, cls)
538
+ and types.is_backend_base_variable(name, mixin_cls)
535
539
  })
536
540
 
537
541
  cls.backend_vars = {
@@ -579,9 +583,6 @@ class BaseState(EvenMoreBasicBaseState):
579
583
  cls.computed_vars[name] = newcv
580
584
  cls.vars[name] = newcv
581
585
  continue
582
- if types.is_backend_base_variable(name, mixin_cls):
583
- cls.backend_vars[name] = copy.deepcopy(value)
584
- continue
585
586
  if events.get(name) is not None:
586
587
  continue
587
588
  if not cls._item_is_event_handler(name, value):
@@ -1779,7 +1780,7 @@ class BaseState(EvenMoreBasicBaseState):
1779
1780
  return StateUpdate(
1780
1781
  delta=delta,
1781
1782
  events=fixed_events,
1782
- final=final if not handler.is_background else True,
1783
+ final=final if not handler.is_background else None,
1783
1784
  )
1784
1785
  except Exception as ex:
1785
1786
  state._clean()
@@ -2471,11 +2472,34 @@ def dynamic(func: Callable[[T], Component]):
2471
2472
  return wrapper
2472
2473
 
2473
2474
 
2475
+ # sessionStorage key holding the ms timestamp of the last reload on error
2476
+ LAST_RELOADED_KEY = "reflex_last_reloaded_on_error"
2477
+
2478
+
2474
2479
  class FrontendEventExceptionState(State):
2475
2480
  """Substate for handling frontend exceptions."""
2476
2481
 
2482
+ # If the frontend error message contains any of these strings, automatically reload the page.
2483
+ auto_reload_on_errors: ClassVar[list[re.Pattern]] = [
2484
+ re.compile( # Chrome/Edge
2485
+ re.escape("TypeError: Cannot read properties of null")
2486
+ ),
2487
+ re.compile(re.escape("TypeError: null is not an object")), # Safari
2488
+ re.compile(r"TypeError: can't access property \".*\" of null"), # Firefox
2489
+ # Firefox: property access is on a function that returns null.
2490
+ re.compile(
2491
+ re.escape("TypeError: can't access property \"")
2492
+ + r".*"
2493
+ + re.escape('", ')
2494
+ + r".*"
2495
+ + re.escape(" is null")
2496
+ ),
2497
+ ]
2498
+
2477
2499
  @event
2478
- def handle_frontend_exception(self, info: str, component_stack: str) -> None:
2500
+ def handle_frontend_exception(
2501
+ self, info: str, component_stack: str
2502
+ ) -> Iterator[EventSpec]:
2479
2503
  """Handle frontend exceptions.
2480
2504
 
2481
2505
  If a frontend exception handler is provided, it will be called.
@@ -2485,7 +2509,21 @@ class FrontendEventExceptionState(State):
2485
2509
  info: The exception information.
2486
2510
  component_stack: The stack trace of the component where the exception occurred.
2487
2511
 
2512
+ Yields:
2513
+ Optional auto-reload event for certain errors outside cooldown period.
2488
2514
  """
2515
+ # Handle automatic reload for certain errors.
2516
+ if type(self).auto_reload_on_errors and any(
2517
+ error.search(info) for error in type(self).auto_reload_on_errors
2518
+ ):
2519
+ yield call_script(
2520
+ f"const last_reload = parseInt(window.sessionStorage.getItem('{LAST_RELOADED_KEY}')) || 0;"
2521
+ f"if (Date.now() - last_reload > {environment.REFLEX_AUTO_RELOAD_COOLDOWN_TIME_MS.get()})"
2522
+ "{"
2523
+ f"window.sessionStorage.setItem('{LAST_RELOADED_KEY}', Date.now().toString());"
2524
+ "window.location.reload();"
2525
+ "}"
2526
+ )
2489
2527
  prerequisites.get_and_validate_app().app.frontend_exception_handler(
2490
2528
  Exception(info)
2491
2529
  )
@@ -2684,7 +2722,7 @@ class StateUpdate:
2684
2722
  events: list[Event] = dataclasses.field(default_factory=list)
2685
2723
 
2686
2724
  # Whether this is the final state update for the event.
2687
- final: bool = True
2725
+ final: bool | None = True
2688
2726
 
2689
2727
  def json(self) -> str:
2690
2728
  """Convert the state update to a JSON string.
@@ -195,6 +195,8 @@ def _compile_vite_config(config: Config):
195
195
  base=base,
196
196
  hmr=environment.VITE_HMR.get(),
197
197
  force_full_reload=environment.VITE_FORCE_FULL_RELOAD.get(),
198
+ experimental_hmr=environment.VITE_EXPERIMENTAL_HMR.get(),
199
+ sourcemap=environment.VITE_SOURCEMAP.get(),
198
200
  )
199
201
 
200
202
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reflex
3
- Version: 0.8.16
3
+ Version: 0.8.17
4
4
  Summary: Web apps in pure Python.
5
5
  Project-URL: homepage, https://reflex.dev
6
6
  Project-URL: repository, https://github.com/reflex-dev/reflex
@@ -31,12 +31,12 @@ Requires-Dist: pydantic<3.0,>=1.10.21
31
31
  Requires-Dist: python-multipart<1.0,>=0.0.20
32
32
  Requires-Dist: python-socketio<6.0,>=5.12.0
33
33
  Requires-Dist: redis<7.0,>=5.2.1
34
- Requires-Dist: reflex-hosting-cli>=0.1.55
34
+ Requires-Dist: reflex-hosting-cli>=0.1.57
35
35
  Requires-Dist: rich<15,>=13
36
36
  Requires-Dist: sqlmodel<0.1,>=0.0.27
37
37
  Requires-Dist: starlette>=0.47.0
38
38
  Requires-Dist: typing-extensions>=4.13.0
39
- Requires-Dist: wrapt<2.0,>=1.17.0
39
+ Requires-Dist: wrapt<3.0,>=1.17.0
40
40
  Provides-Extra: db
41
41
  Requires-Dist: alembic<2.0,>=1.15.2; extra == 'db'
42
42
  Requires-Dist: pydantic<3.0,>=1.10.21; extra == 'db'
@@ -86,25 +86,50 @@ See our [architecture page](https://reflex.dev/blog/2024-03-21-reflex-architectu
86
86
 
87
87
  ## ⚙️ Installation
88
88
 
89
- Open a terminal and run (Requires Python 3.10+):
90
-
91
- ```bash
92
- pip install reflex
93
- ```
89
+ **Important:** We strongly recommend using a virtual environment to ensure the `reflex` command is available in your PATH.
94
90
 
95
91
  ## 🥳 Create your first app
96
92
 
97
- Installing `reflex` also installs the `reflex` command line tool.
93
+ ### 1. Create the project directory
98
94
 
99
- Test that the install was successful by creating a new project. (Replace `my_app_name` with your project name):
95
+ Replace `my_app_name` with your project name:
100
96
 
101
97
  ```bash
102
98
  mkdir my_app_name
103
99
  cd my_app_name
100
+ ```
101
+
102
+ ### 2. Set up a virtual environment
103
+
104
+ Create and activate virtual environment
105
+
106
+ ```bash
107
+ # On Windows:
108
+ python -m venv .venv
109
+ .venv\Scripts\activate
110
+
111
+ # On macOS/Linux:
112
+ python3 -m venv .venv
113
+ source .venv/bin/activate
114
+ ```
115
+
116
+ ### 3. Install Reflex
117
+
118
+ Reflex is available as a pip package (Requires Python 3.10+):
119
+
120
+ ```bash
121
+ pip install reflex
122
+ ```
123
+
124
+ ### 4. Initialize the project
125
+
126
+ This command initializes a template app in your new directory:
127
+
128
+ ```bash
104
129
  reflex init
105
130
  ```
106
131
 
107
- This command initializes a template app in your new directory.
132
+ ### 5. Run the app
108
133
 
109
134
  You can run this app in development mode:
110
135
 
@@ -116,6 +141,10 @@ You should see your app running at http://localhost:3000.
116
141
 
117
142
  Now you can modify the source code in `my_app_name/my_app_name.py`. Reflex has fast refreshes so you can see your changes instantly when you save your code.
118
143
 
144
+ ### Troubleshooting
145
+
146
+ If you installed Reflex without a virtual environment and the `reflex` command is not found, you can run commands using: `python3 -m reflex init` and `python3 -m reflex run`
147
+
119
148
  ## 🫧 Example App
120
149
 
121
150
  Let's go over an example: creating an image generation UI around [DALL·E](https://platform.openai.com/docs/guides/images/image-generation?context=node). For simplicity, we just call the [OpenAI API](https://platform.openai.com/docs/api-reference/authentication), but you could replace this with an ML model run locally.
@@ -2,18 +2,18 @@ reflex/__init__.py,sha256=7iJASSyU1dxLM-l6q6gFAkw6FniXvawAekgfwN5zKjM,10328
2
2
  reflex/__init__.pyi,sha256=Yy3exOO_7-O7fCjTKO1VDFbjPyeMM7F12WBnEXWx_tk,10428
3
3
  reflex/__main__.py,sha256=6cVrGEyT3j3tEvlEVUatpaYfbB5EF3UVY-6vc_Z7-hw,108
4
4
  reflex/admin.py,sha256=Nbc38y-M8iaRBvh1W6DQu_D3kEhO8JFvxrog4q2cB_E,434
5
- reflex/app.py,sha256=noSvr5FnmbNeQM7c_wK8fAedaMkzdTB2Lu49P6WULNM,79082
5
+ reflex/app.py,sha256=s3Grrt3p_Lsk9I1gOhEaxyMj0Zj0IgmfFOzmqlQU3IM,79352
6
6
  reflex/assets.py,sha256=l5O_mlrTprC0lF7Rc_McOe3a0OtSLnRdNl_PqCpDCBA,3431
7
7
  reflex/base.py,sha256=ROoDZCLWyEdVqfYKnhYKPZINklTl5nHKWo2x0J4MfoE,2327
8
8
  reflex/config.py,sha256=LsHAtdH4nkSn3q_Ie-KNdOGdflLXrFICUQov29oFjVk,21229
9
- reflex/environment.py,sha256=i2uDE6OZk8ISl4gzw8ulpt5hB0qKNVtjZ7lDJIgEw-g,24970
9
+ reflex/environment.py,sha256=-RpVj3yM8lX4c8T7Z3YAAX0PV92DHILEuHkbxWwq0kk,26460
10
10
  reflex/event.py,sha256=8vaqS2MBVTQwMVEemtKpjLlMxdlM95IO8u8U9YB_31Q,76443
11
11
  reflex/model.py,sha256=d9qCuIEK0mscG2Ng3HTsEgS39rXOfzqXzLZOl3DG_fc,22487
12
12
  reflex/page.py,sha256=ssCbMVFuIy60vH-YhJUzN0OxzUwXFCCD3ej56dVjp3g,3525
13
13
  reflex/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  reflex/reflex.py,sha256=urU9hMWIkmVl6BPuw_-NmfaE8Zlls4s_t31ueFl4uBo,25915
15
15
  reflex/route.py,sha256=TnS4m6Hm-b3LfGFpm37iAMEd-_JISAouPW5FqUxTAfU,7858
16
- reflex/state.py,sha256=IQW8zSlxGCnSlQSFx-MQSsW80sXp-litEaRv8LouZQo,95389
16
+ reflex/state.py,sha256=oBDulNnUSU10EjoWo6sPQwykT9cxNcXm_uw2qCJyQg0,96993
17
17
  reflex/style.py,sha256=q5Zyc16ULY9xw9CKPPawknrBXIcNNdaSLALXAgdcm2g,13298
18
18
  reflex/testing.py,sha256=EzFAVOD9iRfTlQ805NJG6vUynRt1TEkPxId5aCK8eAQ,41974
19
19
  reflex/.templates/apps/blank/assets/favicon.ico,sha256=baxxgDAQ2V4-G5Q4S2yK5uUJTUGkv-AOWBQ0xd6myUo,4286
@@ -30,7 +30,7 @@ reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js,sha2
30
30
  reflex/.templates/web/components/shiki/code.js,sha256=4Es1pxsr-lX4hTQ5mglrwwC6O_SI-z-O60k03z8VFzQ,1144
31
31
  reflex/.templates/web/styles/__reflex_style_reset.css,sha256=qbC6JIT643YEsvSQ0D7xBmWE5vXy94JGrKNihRuEjnA,8913
32
32
  reflex/.templates/web/utils/react-theme.js,sha256=Aa-RND3ooGCXW6Zavzitc-v0ciKlcQDTFlDtE4mPkFI,2713
33
- reflex/.templates/web/utils/state.js,sha256=7gRWc8ABMOKY3KMF6SpaVEhNILW03wivd7eENVC7ADg,34858
33
+ reflex/.templates/web/utils/state.js,sha256=4vbzLxusJhiFLvmODxX5gB7drCtNtgSXzc5j3MLW_Cs,34899
34
34
  reflex/.templates/web/utils/helpers/dataeditor.js,sha256=pG6MgsHuStDR7-qPipzfiK32j9bKDBa-4hZ0JSUo4JM,1623
35
35
  reflex/.templates/web/utils/helpers/debounce.js,sha256=xGhtTRtS_xIcaeqnYVvYJNseLgQVk-DW-eFiHJYO9As,528
36
36
  reflex/.templates/web/utils/helpers/paste.js,sha256=ef30HsR83jRzzvZnl8yV79yqFP8TC_u8SlN99cCS_OM,1799
@@ -43,7 +43,7 @@ reflex/app_mixins/middleware.py,sha256=BKhe0jUFO1_TylEC48LUZyaeYyPmAYW-NV4H5Rw22
43
43
  reflex/app_mixins/mixin.py,sha256=R1YncalqDrbdPZvpKVbm72ZKmQZxYAWfuFq9JknzTqQ,305
44
44
  reflex/compiler/__init__.py,sha256=r8jqmDSFf09iV2lHlNhfc9XrTLjNxfDNwPYlxS4cmHE,27
45
45
  reflex/compiler/compiler.py,sha256=aaAgnbplyzkTWSfRKkvtT70l72qkbDdwQut-5OPWbL0,29114
46
- reflex/compiler/templates.py,sha256=iw6yYC23iFlUfM_7LAMTQyLjSWIAddEKInHbPqjMEwo,20611
46
+ reflex/compiler/templates.py,sha256=vIpdW0j0Ti1h3VnWmrS6S4OtDRglYHynTT7959sHI5w,20987
47
47
  reflex/compiler/utils.py,sha256=TvVSVhlYPpNEH3-PUnih-RMpHa4HmgGCSCnUxASdzFE,19633
48
48
  reflex/components/__init__.py,sha256=eWpgWFbSQDj2TpGp6StEbxU7roQgzY7ZM0XIcIc5RE8,588
49
49
  reflex/components/__init__.pyi,sha256=7VFHtJGIjvGtD3IiPk848IPWYSCcPRT1EyPGljLhYlU,736
@@ -61,7 +61,7 @@ reflex/components/base/body.py,sha256=KLPOhxVsKyjPwrY9AziCOOG_c9ckOqIhI4n2i3_Q3N
61
61
  reflex/components/base/body.pyi,sha256=u_Y3lBkSwGZlnsHWKr_Rtm49UI2bP8Rx2e99gkshENw,2357
62
62
  reflex/components/base/document.py,sha256=Fr7y22NbeKeiz8kWPH2q5BpFjKdq-AmY-sxZilee_H8,636
63
63
  reflex/components/base/document.pyi,sha256=PlvclB2vyGcQ8zLJpFac_37LHeXLwpe8Qh0UqzXAPfE,11788
64
- reflex/components/base/error_boundary.py,sha256=sp6W7G6uFWdls6nzl17XSizSSm6zS2OAu3ZqTE2W1CE,6438
64
+ reflex/components/base/error_boundary.py,sha256=eXLO4SxNx2QoIC5tXk7f0kMulXNDJJaEkZNJ985FAcw,7456
65
65
  reflex/components/base/error_boundary.pyi,sha256=SvKnZ09jwIL_4-J5BKVZuATclrB4o3Eh457iTmmoNb4,2926
66
66
  reflex/components/base/fragment.py,sha256=ys7wkokq-N8WBxa9fqkEaNIrBlSximyD7vqlFVe02hQ,342
67
67
  reflex/components/base/fragment.pyi,sha256=1vl8p6SCWd0_QzYVAb-Em70IX6FwIWFvJLxMl5OJTwU,2401
@@ -97,7 +97,7 @@ reflex/components/core/sticky.py,sha256=UD5x4cHqRkwUY87L5tmKwQYqW-j5fELqiPaozoSQ
97
97
  reflex/components/core/sticky.pyi,sha256=_iqFMzHRaHcL23TYy0sL4x7Sdc5rEcp5W2ESTOFlPdc,10282
98
98
  reflex/components/core/upload.py,sha256=NbcpZ_zNltvBQFBQ-sJNriQg3Bw9Q8VwUL7cKY2kKIU,15165
99
99
  reflex/components/core/upload.pyi,sha256=TQVUHt47YsfleD_O-gbLJMzie5slnjft-KVs_7jjRtQ,16820
100
- reflex/components/core/window_events.py,sha256=opbuO20zVxt252kQLk49V7cltb_Um2oh7iePeGNJ538,3355
100
+ reflex/components/core/window_events.py,sha256=21IX9CqdkpLuExnxhYOmwj9FJORLJXLz09uF8Hn7wKc,3954
101
101
  reflex/components/core/window_events.pyi,sha256=RWE1wTGGERr8dSmdyfHtyEUJXXEHN_rR5ZM7WzeTD4g,3226
102
102
  reflex/components/core/layout/__init__.py,sha256=znldZaj_NGt8qCZDG70GMwjMTskcvCf_2N_EjCAHwdc,30
103
103
  reflex/components/datadisplay/__init__.py,sha256=L8pWWKNHWdUD2fbZRoEKjd_8c_hpDdGYO463hwkoIi4,438
@@ -139,8 +139,8 @@ reflex/components/gridjs/__init__.py,sha256=xJwDm1AZ70L5-t9LLqZwGUtDpijbf1KuMYDT
139
139
  reflex/components/gridjs/datatable.py,sha256=7JKrRw1zkpFB0_wwoaIhrVrldsm7-dyi3PASgqLq8Hc,4224
140
140
  reflex/components/gridjs/datatable.pyi,sha256=kFgv82vCgfdWZaUq4bZ73G8X3mkw6ecvSRkZ9G9-28E,5185
141
141
  reflex/components/lucide/__init__.py,sha256=EggTK2MuQKQeOBLKW-mF0VaDK9zdWBImu1HO2dvHZbE,73
142
- reflex/components/lucide/icon.py,sha256=kApdAoKlDFsy5785776DBVIwY0s7ceOX_5aSA7YoRWc,35375
143
- reflex/components/lucide/icon.pyi,sha256=oFXjvb5SBULQlbeGDY4K5WWkqemktGIaCsiyrentjKY,37987
142
+ reflex/components/lucide/icon.py,sha256=WLEzDeVlS68XWj1y4F0DV6pa8S19g-vwuZxMe6BT2Yo,35396
143
+ reflex/components/lucide/icon.pyi,sha256=O4HM8EACvZt3s8s8KgC0QLYITvy-gHLn3D01jRcHk8Q,38008
144
144
  reflex/components/markdown/__init__.py,sha256=Dfl1At5uYoY7H4ufZU_RY2KOGQDLtj75dsZ2BTqqAns,87
145
145
  reflex/components/markdown/markdown.py,sha256=Sg3AioKZsNn27KdOzR3o53k1bHzPa2pjpHFVYbxBgCg,16464
146
146
  reflex/components/markdown/markdown.pyi,sha256=5SbgUBrklIdxEJOHtOqKVM2aobgulnkWp5DEGUFNmEI,4323
@@ -148,7 +148,7 @@ reflex/components/moment/__init__.py,sha256=jGnZgRBivYJQPIcFgtLaXFteCeIG3gGH5ACX
148
148
  reflex/components/moment/moment.py,sha256=fhhzrun9USb8J30btpyyDD3JuXF_N7EL5Dou3x7NQYw,4080
149
149
  reflex/components/moment/moment.pyi,sha256=3CQSmhMF4MetFCrjuds9wpz7FnFeXdXzR39A8n3nhy0,2872
150
150
  reflex/components/plotly/__init__.py,sha256=6B_woBJhkrVA9O_AbOTbsA_SxWsqjicYHmLA9FLjGfU,650
151
- reflex/components/plotly/plotly.py,sha256=4PugE2Gy2x02CSz_0GbnUbuIJAfjydQoMWxv1K43kQA,15172
151
+ reflex/components/plotly/plotly.py,sha256=xy9MNprtmO67RIrFkc2D9QfbjzP8eX4Tkk4UfPz5JBM,15172
152
152
  reflex/components/plotly/plotly.pyi,sha256=5Nhph2PL9eIb-Xd6CUZHQf79P-tOazy1alwRQl7CKWw,46821
153
153
  reflex/components/radix/__init__.py,sha256=fRsLvIO3MrTtPOXtmnxYDB9phvzlcbyB_utgpafYMho,474
154
154
  reflex/components/radix/__init__.pyi,sha256=ke_dGrpFMNHd3MgQ9qiStSQDlGcJ39KVSrpIxayBU3c,3927
@@ -301,7 +301,7 @@ reflex/components/recharts/general.py,sha256=c3SRcKzNvSeKcbVInl-zNrDxNe0Lgejqcbl
301
301
  reflex/components/recharts/general.pyi,sha256=e77vGL8lo-t1jI79h0O12qSK5e9wbjerPslCxQQgSBw,23462
302
302
  reflex/components/recharts/polar.py,sha256=zocHpwWQ0lbg4BTnEBwQ6J9SSJsOYRwZGf9UPzxoNKs,15682
303
303
  reflex/components/recharts/polar.pyi,sha256=8ShEcGK9KJyu0FN6KPys1kgAYrzOZ6wtiRuytHJ--38,26937
304
- reflex/components/recharts/recharts.py,sha256=rAarxOawhHiWUDIHNrizmChitgoNV4zas1y5SuSsr7E,3221
304
+ reflex/components/recharts/recharts.py,sha256=8GJIjIcJMHQCFV5gkHOhqa1L9DzSFO_bBIgzIichyx0,3221
305
305
  reflex/components/recharts/recharts.pyi,sha256=9j8cVSMqyBkkOBBrx9pzDpP1clnbM9kD0TTViTUGDYc,7084
306
306
  reflex/components/sonner/__init__.py,sha256=L_mdRIy7-ccRGSz5VK6J8O-c-e-D1p9xWw29_ErrvGg,68
307
307
  reflex/components/sonner/toast.py,sha256=VBtoRnxBhKD029njAjhbLeYtiUSL3aNPOSmA9Lmsqvg,12512
@@ -319,7 +319,7 @@ reflex/constants/compiler.py,sha256=1FXPYQNotaSrTwWcOspA1gCVmEdoiWkNMbbrz_qU0YU,
319
319
  reflex/constants/config.py,sha256=8OIjiBdZZJrRVHsNBheMwopE9AwBFFzau0SXqXKcrPg,1715
320
320
  reflex/constants/custom_components.py,sha256=joJt4CEt1yKy7wsBH6vYo7_QRW0O_fWXrrTf0VY2q14,1317
321
321
  reflex/constants/event.py,sha256=tgoynWQi2L0_Kqc3XhXo7XXL76A-OKhJGHRrNjm7gFw,2885
322
- reflex/constants/installer.py,sha256=nzyvtCXgcGt7Anbbx4gC3s7QYdupFVZZUOfztxgjk14,4191
322
+ reflex/constants/installer.py,sha256=iZvdnTIl2zh0_fPr1G-RsOiHbA4vnnse8ykPJVFBUbE,4191
323
323
  reflex/constants/route.py,sha256=UBjqaAOxiUxlDZCSY4O2JJChKvA4MZrhUU0E5rNvKbM,2682
324
324
  reflex/constants/state.py,sha256=VrEeYxXfE9ss8RmOHIXD4T6EGsV9PDqbtMCQMmZxW3I,383
325
325
  reflex/constants/utils.py,sha256=e1ChEvbHfmE_V2UJvCSUhD_qTVAIhEGPpRJSqdSd6PA,780
@@ -331,13 +331,13 @@ reflex/experimental/hooks.py,sha256=CHYGrAE5t8riltrJmDFgJ4D2Vhmhw-y3B3MSGNlOQow,
331
331
  reflex/istate/__init__.py,sha256=afq_pCS5B_REC-Kl3Rbaa538uWi59xNz4INeuENcWnk,2039
332
332
  reflex/istate/data.py,sha256=ZT7uW_Ish3A0V4x2Lu8PD__GkiOd3nHY0lAeGcghp68,7872
333
333
  reflex/istate/dynamic.py,sha256=xOQ9upZVPf6ngqcLQZ9HdAAYmoWwJ8kRFPH34Q5HTiM,91
334
- reflex/istate/proxy.py,sha256=BwlJYWR5FMItqbHVulhgI_TH8jtP69HkXXpTtClXXcs,25993
334
+ reflex/istate/proxy.py,sha256=nqL-MPWp2Ldo8GsjCdzfeWK1wQ9LK-fUoB8XyIZfJwQ,26458
335
335
  reflex/istate/storage.py,sha256=gCPoiZxuG-Rw0y-Pz3OC7rv4o08dQ_jK1fE2u8Jhxqg,4339
336
336
  reflex/istate/wrappers.py,sha256=p8uuioXRbR5hperwbOJHUcWdu7hukLikQdoR7qrnKsI,909
337
- reflex/istate/manager/__init__.py,sha256=jG5A1lmVtU7BC3dRMfXhd7jI3keUVX8QMEXTteTzhO8,3876
338
- reflex/istate/manager/disk.py,sha256=2gtMg2aQK1ZuIbgz7EHshOBUGNTHxI__4D3HDTdvePo,13450
339
- reflex/istate/manager/memory.py,sha256=5rAJlgQAhBB0B3hQT3TYyu-N37TzRvhLcCItcyWkrcw,2477
340
- reflex/istate/manager/redis.py,sha256=TBOdZfeXigL6nbKVDOgkxX595ZQJtRYOEToSlE6MZyM,18297
337
+ reflex/istate/manager/__init__.py,sha256=hTg5uqxVbz-xayUZNin-wP51PfAkz1CHDez-jncXTTg,4406
338
+ reflex/istate/manager/disk.py,sha256=RVWDnPt4d2a0El8RBnWDVCksLKv7rPuWbkUPR6Y7Szc,13736
339
+ reflex/istate/manager/memory.py,sha256=tnK2JzJNcEbiXAdGIT5tNA0U1-mQZoeXKF8XNJCfnts,2760
340
+ reflex/istate/manager/redis.py,sha256=LGR8ilvrCZmxKznhIKBHP4-Oe_JT5htcoJldVH53MlU,19020
341
341
  reflex/middleware/__init__.py,sha256=x7xTeDuc73Hjj43k1J63naC9x8vzFxl4sq7cCFBX7sk,111
342
342
  reflex/middleware/hydrate_middleware.py,sha256=1ch7bx2ZhojOR15b-LHD2JztrWCnpPJjTe8MWHJe-5Y,1510
343
343
  reflex/middleware/middleware.py,sha256=p5VVoIgQ_NwOg_GOY6g0S4fmrV76_VE1zt-HiwbMw-s,1158
@@ -347,7 +347,7 @@ reflex/plugins/base.py,sha256=5BgzCM7boj9kJ6FGzVzVlgQk-crJuVmOLCl1PXvv4-E,3372
347
347
  reflex/plugins/shared_tailwind.py,sha256=XPnswswPW3UIeEu5ghecdEeYtpikG5ksD92sM-VwKYM,7221
348
348
  reflex/plugins/sitemap.py,sha256=X_CtH5B1w3CZno-gdPj1rp63WjOuNjFnX4B3fx_-VFQ,6135
349
349
  reflex/plugins/tailwind_v3.py,sha256=jCEZ5UYdr706Mw48L-WSHOUB6O55o1C3uG6AMwXqZoI,4810
350
- reflex/plugins/tailwind_v4.py,sha256=8G7iXj6wYRRJlCWlce_vtCgs1YittFvLucyUP-sPXhc,5230
350
+ reflex/plugins/tailwind_v4.py,sha256=Cjp3vJnrZDYiflwm4imj4SEI0pYBv36mLvmpbI6olao,5230
351
351
  reflex/utils/__init__.py,sha256=y-AHKiRQAhk2oAkvn7W8cRVTZVK625ff8tTwvZtO7S4,24
352
352
  reflex/utils/build.py,sha256=j-OY90O7gMP_bclVt_6J3Q2GFgOHQH_uFpTfdaWmuqU,9746
353
353
  reflex/utils/codespaces.py,sha256=SIATnmlGCABPvjvRIENUCwP-fcjqKhdoOYiFY_Eua6M,4339
@@ -358,7 +358,7 @@ reflex/utils/exceptions.py,sha256=Wwu7Ji2xgq521bJKtU2NgjwhmFfnG8erirEVN2h8S-g,88
358
358
  reflex/utils/exec.py,sha256=xMwRnCd9SyTbvwBPS5i3C6ZcorLSFViqB_fOoCqsDEc,22833
359
359
  reflex/utils/export.py,sha256=dR8Q7OymxcS8PCvx5zV2kOZmjKgOmuAfKfL8XifHJd4,2907
360
360
  reflex/utils/format.py,sha256=BAq-BO0ON5ionaDaO0yJIZ3Y1YT7hhpYlGHZ5-x9w4o,20981
361
- reflex/utils/frontend_skeleton.py,sha256=FqvNWclY_lRR-odjQmP-xaY3uiWVD2l5qqExt8qRzEI,8769
361
+ reflex/utils/frontend_skeleton.py,sha256=Tr3GUsvJoA6PceVt1jHhowtObgrnRvaeJc4ABTrfv18,8887
362
362
  reflex/utils/imports.py,sha256=SlQfMTbJasXHxrpcHdWKPWiIZ1Kn2-tulMF32_YA2ek,4262
363
363
  reflex/utils/js_runtimes.py,sha256=Xia4pIyWGwUmNnTgELm04oxVQobwdQvmK87AHTr3B1o,13542
364
364
  reflex/utils/lazy_loader.py,sha256=BiY9OvmAJDCz10qpuyTYv9duXgMFQa6RXKQmTO9hqKU,4453
@@ -387,8 +387,8 @@ reflex/vars/number.py,sha256=Cejba-47shtQt-j0uD_HRfTGOm1IF1uZ1WwpWSrcLSE,28865
387
387
  reflex/vars/object.py,sha256=j3b-j66Qa0XDJofMkcJtb8e-TdNx2_hjyEPnrGJEaFY,17833
388
388
  reflex/vars/sequence.py,sha256=OyCfMsv50Zr6W26DMISWjLX6FzK3rbxNcgKepgYr7Pk,52326
389
389
  scripts/hatch_build.py,sha256=-4pxcLSFmirmujGpQX9UUxjhIC03tQ_fIQwVbHu9kc0,1861
390
- reflex-0.8.16.dist-info/METADATA,sha256=w8BQum0qhf-ClDDsjNvLxelDGVbH3OdDbBUk5BtjOxY,12560
391
- reflex-0.8.16.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
392
- reflex-0.8.16.dist-info/entry_points.txt,sha256=Rxt4dXc7MLBNt5CSHTehVPuSe9Xqow4HLX55nD9tQQ0,45
393
- reflex-0.8.16.dist-info/licenses/LICENSE,sha256=dw3zLrp9f5ObD7kqS32vWfhcImfO52PMmRqvtxq_YEE,11358
394
- reflex-0.8.16.dist-info/RECORD,,
390
+ reflex-0.8.17.dist-info/METADATA,sha256=8G9xli6KokdQj47HhQ8cKHo0nfVhTnHasc2K6A4A5fk,13100
391
+ reflex-0.8.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
392
+ reflex-0.8.17.dist-info/entry_points.txt,sha256=Rxt4dXc7MLBNt5CSHTehVPuSe9Xqow4HLX55nD9tQQ0,45
393
+ reflex-0.8.17.dist-info/licenses/LICENSE,sha256=dw3zLrp9f5ObD7kqS32vWfhcImfO52PMmRqvtxq_YEE,11358
394
+ reflex-0.8.17.dist-info/RECORD,,