reflex 0.8.0a1__py3-none-any.whl → 0.8.0a3__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.

@@ -2,7 +2,7 @@
2
2
  {% from "web/pages/macros.js.jinja2" import renderHooks %}
3
3
 
4
4
  {% block early_imports %}
5
- import '$/styles/__reflex_global_styles.css'
5
+ import reflexGlobalStyles from '$/styles/__reflex_global_styles.css?url';
6
6
  {% endblock %}
7
7
 
8
8
  {% block declaration %}
@@ -20,6 +20,10 @@ import * as {{library_alias}} from "{{library_path}}";
20
20
  {% endblock %}
21
21
 
22
22
  {% block export %}
23
+ export const links = () => [
24
+ { rel: 'stylesheet', href: reflexGlobalStyles, type: 'text/css' }
25
+ ];
26
+
23
27
  function AppWrap({children}) {
24
28
  {{ renderHooks(hooks) }}
25
29
 
@@ -2,7 +2,11 @@
2
2
  {% from 'web/pages/macros.js.jinja2' import renderHooksWithMemo %}
3
3
  {% set all_hooks = component._get_all_hooks() %}
4
4
 
5
+ {% if export %}
5
6
  export function {{tag_name}} () {
7
+ {% else %}
8
+ function {{tag_name}} () {
9
+ {% endif %}
6
10
  {{ renderHooksWithMemo(all_hooks, memo_trigger_hooks) }}
7
11
 
8
12
  return (
@@ -69,8 +69,6 @@ export const initialEvents = () => []
69
69
 
70
70
  export const isDevMode = {{ is_dev_mode|json_dumps }}
71
71
 
72
- export const lastCompiledTimeStamp = {{ last_compiled_time|json_dumps }}
73
-
74
72
  export function UploadFilesProvider({ children }) {
75
73
  const [filesById, setFilesById] = useState({})
76
74
  refs["__clear_selected_files"] = (id) => setFilesById(filesById => {
@@ -2,9 +2,9 @@ import { route } from "@react-router/dev/routes";
2
2
  import { flatRoutes } from "@react-router/fs-routes";
3
3
 
4
4
  export default [
5
- route("*", "routes/[404]_._index.js"),
6
- route("404", "routes/[404]_._index.js", { id: "404" }),
5
+ route("404", "routes/[404]_._index.jsx", { id: "404" }),
7
6
  ...(await flatRoutes({
8
- ignoredRouteFiles: ["routes/\\[404\\]_._index.js"],
7
+ ignoredRouteFiles: ["routes/\\[404\\]_._index.jsx"],
9
8
  })),
9
+ route("*", "routes/[404]_._index.jsx"),
10
10
  ];
@@ -1,11 +1,6 @@
1
1
  import { useTheme } from "$/utils/react-theme";
2
2
  import { createElement } from "react";
3
- import {
4
- ColorModeContext,
5
- defaultColorMode,
6
- isDevMode,
7
- lastCompiledTimeStamp,
8
- } from "$/utils/context";
3
+ import { ColorModeContext, defaultColorMode } from "$/utils/context";
9
4
 
10
5
  export default function RadixThemesColorModeProvider({ children }) {
11
6
  const { theme, resolvedTheme, setTheme } = useTheme();
@@ -8,9 +8,13 @@ import {
8
8
  useMemo,
9
9
  } from "react";
10
10
 
11
- import { isDevMode, lastCompiledTimeStamp } from "$/utils/context";
11
+ import { isDevMode, defaultColorMode } from "$/utils/context";
12
12
 
13
- const ThemeContext = createContext();
13
+ const ThemeContext = createContext({
14
+ theme: defaultColorMode,
15
+ resolvedTheme: defaultColorMode !== "system" ? defaultColorMode : "light",
16
+ setTheme: () => {},
17
+ });
14
18
 
15
19
  export function ThemeProvider({ children, defaultTheme = "system" }) {
16
20
  const [theme, setTheme] = useState(defaultTheme);
@@ -28,11 +32,10 @@ export function ThemeProvider({ children, defaultTheme = "system" }) {
28
32
  firstRender.current = false;
29
33
 
30
34
  if (isDevMode) {
31
- const lastCompiledTimeInLocalStorage =
32
- localStorage.getItem("last_compiled_time");
33
- if (lastCompiledTimeInLocalStorage !== lastCompiledTimeStamp) {
35
+ const lastCompiledTheme = localStorage.getItem("last_compiled_theme");
36
+ if (lastCompiledTheme !== defaultColorMode) {
34
37
  // on app startup, make sure the application color mode is persisted correctly.
35
- localStorage.setItem("last_compiled_time", lastCompiledTimeStamp);
38
+ localStorage.setItem("last_compiled_theme", defaultColorMode);
36
39
  return;
37
40
  }
38
41
  }
@@ -842,7 +842,12 @@ export const useEventLoop = (
842
842
  const params = useRef(paramsR);
843
843
 
844
844
  useEffect(() => {
845
- params.current = paramsR;
845
+ const { "*": splat, ...remainingParams } = paramsR;
846
+ if (splat) {
847
+ params.current = { ...remainingParams, splat: splat.split("/") };
848
+ } else {
849
+ params.current = remainingParams;
850
+ }
846
851
  }, [paramsR]);
847
852
 
848
853
  // Function to add new events to the event queue.
reflex/app.py CHANGED
@@ -890,7 +890,7 @@ class App(MiddlewareMixin, LifespanMixin):
890
890
  def _check_routes_conflict(self, new_route: str):
891
891
  """Verify if there is any conflict between the new route and any existing route.
892
892
 
893
- Based on conflicts that NextJS would throw if not intercepted.
893
+ Based on conflicts that React Router would throw if not intercepted.
894
894
 
895
895
  Raises:
896
896
  RouteValueError: exception showing which conflict exist with the route to be added
@@ -4,7 +4,6 @@ from __future__ import annotations
4
4
 
5
5
  import sys
6
6
  from collections.abc import Iterable, Sequence
7
- from datetime import datetime
8
7
  from inspect import getmodule
9
8
  from pathlib import Path
10
9
  from typing import TYPE_CHECKING
@@ -126,21 +125,18 @@ def _compile_contexts(state: type[BaseState] | None, theme: Component | None) ->
126
125
  if appearance is None or str(LiteralVar.create(appearance)) == '"inherit"':
127
126
  appearance = LiteralVar.create(SYSTEM_COLOR_MODE)
128
127
 
129
- last_compiled_time = str(datetime.now())
130
128
  return (
131
129
  templates.CONTEXT.render(
132
130
  initial_state=utils.compile_state(state),
133
131
  state_name=state.get_name(),
134
132
  client_storage=utils.compile_client_storage(state),
135
133
  is_dev_mode=not is_prod_mode(),
136
- last_compiled_time=last_compiled_time,
137
134
  default_color_mode=appearance,
138
135
  )
139
136
  if state
140
137
  else templates.CONTEXT.render(
141
138
  is_dev_mode=not is_prod_mode(),
142
139
  default_color_mode=appearance,
143
- last_compiled_time=last_compiled_time,
144
140
  )
145
141
  )
146
142
 
@@ -429,7 +425,7 @@ def _compile_stateful_components(
429
425
 
430
426
  # Include custom code in the shared component.
431
427
  rendered_components.update(
432
- dict.fromkeys(component._get_all_custom_code()),
428
+ dict.fromkeys(component._get_all_custom_code(export=True)),
433
429
  )
434
430
 
435
431
  # Include all imports in the shared component.
@@ -113,10 +113,10 @@ def from_string(source: str) -> Template:
113
113
  # Template for the Reflex config file.
114
114
  RXCONFIG = get_template("app/rxconfig.py.jinja2")
115
115
 
116
- # Code to render a NextJS Document root.
116
+ # Code to render the Document root.
117
117
  DOCUMENT_ROOT = get_template("web/pages/_document.js.jinja2")
118
118
 
119
- # Code to render NextJS App root.
119
+ # Code to render App root.
120
120
  APP_ROOT = get_template("web/pages/_app.js.jinja2")
121
121
 
122
122
  # Template for the theme file.
@@ -128,7 +128,7 @@ CONTEXT = get_template("web/utils/context.js.jinja2")
128
128
  # Template to render a component tag.
129
129
  COMPONENT = get_template("web/pages/component.js.jinja2")
130
130
 
131
- # Code to render a single NextJS page.
131
+ # Code to render a single react page.
132
132
  PAGE = get_template("web/pages/index.js.jinja2")
133
133
 
134
134
  # Code to render the custom components page.
reflex/compiler/utils.py CHANGED
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  import asyncio
6
6
  import concurrent.futures
7
7
  import traceback
8
- from collections.abc import Sequence
8
+ from collections.abc import Mapping, Sequence
9
9
  from datetime import datetime
10
10
  from pathlib import Path
11
11
  from typing import Any
@@ -174,6 +174,18 @@ def save_error(error: Exception) -> str:
174
174
  return str(log_path)
175
175
 
176
176
 
177
+ def _sorted_keys(d: Mapping[str, Any]) -> dict[str, Any]:
178
+ """Sort the keys of a dictionary.
179
+
180
+ Args:
181
+ d: The dictionary to sort.
182
+
183
+ Returns:
184
+ A new dictionary with sorted keys.
185
+ """
186
+ return dict(sorted(d.items(), key=lambda kv: kv[0]))
187
+
188
+
177
189
  def compile_state(state: type[BaseState]) -> dict:
178
190
  """Compile the state of the app.
179
191
 
@@ -198,10 +210,10 @@ def compile_state(state: type[BaseState]) -> dict:
198
210
  console.warn(
199
211
  f"Had to get initial state in a thread 🤮 {resolved_initial_state}",
200
212
  )
201
- return resolved_initial_state
213
+ return _sorted_keys(resolved_initial_state)
202
214
 
203
215
  # Normally the compile runs before any event loop starts, we asyncio.run is available for calling.
204
- return asyncio.run(_resolve_delta(initial_state))
216
+ return _sorted_keys(asyncio.run(_resolve_delta(initial_state)))
205
217
 
206
218
 
207
219
  def _compile_client_storage_field(
@@ -403,7 +415,10 @@ def create_theme(style: ComponentStyle) -> dict:
403
415
 
404
416
  def _format_route_part(part: str) -> str:
405
417
  if part.startswith("[") and part.endswith("]"):
406
- return f"${part}_"
418
+ if part.startswith(("[...", "[[...")):
419
+ return "$"
420
+ # We don't add [] here since we are reusing them from the input
421
+ return "$" + part + "_"
407
422
  return "[" + part + "]_"
408
423
 
409
424
 
@@ -411,9 +426,8 @@ def _path_to_file_stem(path: str) -> str:
411
426
  if path == "index":
412
427
  return "_index"
413
428
  path = path if path != "index" else "/"
414
- return (
415
- ".".join([_format_route_part(part) for part in path.split("/")]) + "._index"
416
- ).lstrip(".")
429
+ name = ".".join([_format_route_part(part) for part in path.split("/")]).lstrip(".")
430
+ return name + "._index" if not name.endswith("$") else name
417
431
 
418
432
 
419
433
  def get_page_path(path: str) -> str:
@@ -429,7 +443,7 @@ def get_page_path(path: str) -> str:
429
443
  get_web_dir()
430
444
  / constants.Dirs.PAGES
431
445
  / constants.Dirs.ROUTES
432
- / (_path_to_file_stem(path) + constants.Ext.JS)
446
+ / (_path_to_file_stem(path) + constants.Ext.JSX)
433
447
  )
434
448
 
435
449
 
@@ -477,7 +491,7 @@ def get_components_path() -> str:
477
491
  return str(
478
492
  get_web_dir()
479
493
  / constants.Dirs.UTILS
480
- / (constants.PageNames.COMPONENTS + constants.Ext.JS),
494
+ / (constants.PageNames.COMPONENTS + constants.Ext.JSX),
481
495
  )
482
496
 
483
497
 
@@ -490,7 +504,7 @@ def get_stateful_components_path() -> str:
490
504
  return str(
491
505
  get_web_dir()
492
506
  / constants.Dirs.UTILS
493
- / (constants.PageNames.STATEFUL_COMPONENTS + constants.Ext.JS)
507
+ / (constants.PageNames.STATEFUL_COMPONENTS + constants.Ext.JSX)
494
508
  )
495
509
 
496
510
 
@@ -2293,16 +2293,19 @@ class StatefulComponent(BaseComponent):
2293
2293
  tag_to_stateful_component: ClassVar[dict[str, StatefulComponent]] = {}
2294
2294
 
2295
2295
  # Reference to the original component that was memoized into this component.
2296
- component: Component
2297
-
2298
- # The rendered (memoized) code that will be emitted.
2299
- code: str
2296
+ component: Component = field(
2297
+ default_factory=Component, is_javascript_property=False
2298
+ )
2300
2299
 
2301
2300
  # How many times this component is referenced in the app.
2302
- references: int = 0
2301
+ references: int = field(default=0, is_javascript_property=False)
2303
2302
 
2304
2303
  # Whether the component has already been rendered to a shared file.
2305
- rendered_as_shared: bool = False
2304
+ rendered_as_shared: bool = field(default=False, is_javascript_property=False)
2305
+
2306
+ memo_trigger_hooks: list[str] = field(
2307
+ default_factory=list, is_javascript_property=False
2308
+ )
2306
2309
 
2307
2310
  @classmethod
2308
2311
  def create(cls, component: Component) -> StatefulComponent | None:
@@ -2362,8 +2365,7 @@ class StatefulComponent(BaseComponent):
2362
2365
  # Look up the tag in the cache
2363
2366
  stateful_component = cls.tag_to_stateful_component.get(tag_name)
2364
2367
  if stateful_component is None:
2365
- # Render the component as a string of javascript code.
2366
- code = cls._render_stateful_code(component, tag_name=tag_name)
2368
+ memo_trigger_hooks = cls._fix_event_triggers(component)
2367
2369
  # Set the stateful component in the cache for the given tag.
2368
2370
  stateful_component = cls.tag_to_stateful_component.setdefault(
2369
2371
  tag_name,
@@ -2371,7 +2373,7 @@ class StatefulComponent(BaseComponent):
2371
2373
  children=component.children,
2372
2374
  component=component,
2373
2375
  tag=tag_name,
2374
- code=code,
2376
+ memo_trigger_hooks=memo_trigger_hooks,
2375
2377
  ),
2376
2378
  )
2377
2379
  # Bump the reference count -- multiple pages referencing the same component
@@ -2435,26 +2437,38 @@ class StatefulComponent(BaseComponent):
2435
2437
  f"{component.tag or 'Comp'}_{code_hash}"
2436
2438
  ).capitalize()
2437
2439
 
2438
- @classmethod
2439
2440
  def _render_stateful_code(
2441
+ self,
2442
+ export: bool = False,
2443
+ ) -> str:
2444
+ if not self.tag:
2445
+ return ""
2446
+ # Render the code for this component and hooks.
2447
+ return STATEFUL_COMPONENT.render(
2448
+ tag_name=self.tag,
2449
+ memo_trigger_hooks=self.memo_trigger_hooks,
2450
+ component=self.component,
2451
+ export=export,
2452
+ )
2453
+
2454
+ @classmethod
2455
+ def _fix_event_triggers(
2440
2456
  cls,
2441
2457
  component: Component,
2442
- tag_name: str,
2443
- ) -> str:
2458
+ ) -> list[str]:
2444
2459
  """Render the code for a stateful component.
2445
2460
 
2446
2461
  Args:
2447
2462
  component: The component to render.
2448
- tag_name: The tag name for the stateful component (see _get_tag_name).
2449
2463
 
2450
2464
  Returns:
2451
- The rendered code.
2465
+ The memoized event trigger hooks for the component.
2452
2466
  """
2453
2467
  # Memoize event triggers useCallback to avoid unnecessary re-renders.
2454
2468
  memo_event_triggers = tuple(cls._get_memoized_event_triggers(component).items())
2455
2469
 
2456
2470
  # Trigger hooks stored separately to write after the normal hooks (see stateful_component.js.jinja2)
2457
- memo_trigger_hooks = []
2471
+ memo_trigger_hooks: list[str] = []
2458
2472
 
2459
2473
  if memo_event_triggers:
2460
2474
  # Copy the component to avoid mutating the original.
@@ -2468,12 +2482,7 @@ class StatefulComponent(BaseComponent):
2468
2482
  memo_trigger_hooks.append(memo_trigger_hook)
2469
2483
  component.event_triggers[event_trigger] = memo_trigger
2470
2484
 
2471
- # Render the code for this component and hooks.
2472
- return STATEFUL_COMPONENT.render(
2473
- tag_name=tag_name,
2474
- memo_trigger_hooks=memo_trigger_hooks,
2475
- component=component,
2476
- )
2485
+ return memo_trigger_hooks
2477
2486
 
2478
2487
  @staticmethod
2479
2488
  def _get_hook_deps(hook: str) -> list[str]:
@@ -2631,15 +2640,20 @@ class StatefulComponent(BaseComponent):
2631
2640
  return set()
2632
2641
  return self.component._get_all_dynamic_imports()
2633
2642
 
2634
- def _get_all_custom_code(self) -> set[str]:
2643
+ def _get_all_custom_code(self, export: bool = False) -> set[str]:
2635
2644
  """Get custom code for the component.
2636
2645
 
2646
+ Args:
2647
+ export: Whether to export the component.
2648
+
2637
2649
  Returns:
2638
2650
  The custom code.
2639
2651
  """
2640
2652
  if self.rendered_as_shared:
2641
2653
  return set()
2642
- return self.component._get_all_custom_code().union({self.code})
2654
+ return self.component._get_all_custom_code().union(
2655
+ {self._render_stateful_code(export=export)}
2656
+ )
2643
2657
 
2644
2658
  def _get_all_refs(self) -> set[str]:
2645
2659
  """Get the refs for the children of the component.
@@ -3,13 +3,12 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import dataclasses
6
- import typing
7
6
  from typing import ClassVar, Literal
8
7
 
9
8
  from reflex.components.component import Component, ComponentNamespace
10
9
  from reflex.components.core.cond import color_mode_cond
11
10
  from reflex.components.lucide.icon import Icon
12
- from reflex.components.markdown.markdown import _LANGUAGE, MarkdownComponentMap
11
+ from reflex.components.markdown.markdown import MarkdownComponentMap
13
12
  from reflex.components.radix.themes.components.button import Button
14
13
  from reflex.components.radix.themes.layout.box import Box
15
14
  from reflex.constants.colors import Color
@@ -315,7 +314,7 @@ def construct_theme_var(theme: str) -> Var[Theme]:
315
314
  theme,
316
315
  _var_data=VarData(
317
316
  imports={
318
- f"react-syntax-highlighter/dist/cjs/styles/prism/{format.to_kebab_case(theme)}": [
317
+ f"react-syntax-highlighter/dist/esm/styles/prism/{format.to_kebab_case(theme)}": [
319
318
  ImportVar(tag=theme, is_default=True, install=False)
320
319
  ]
321
320
  }
@@ -502,75 +501,6 @@ class CodeBlock(Component, MarkdownComponentMap):
502
501
  def _exclude_props(self) -> list[str]:
503
502
  return ["can_copy", "copy_button"]
504
503
 
505
- @classmethod
506
- def _get_language_registration_hook(cls, language_var: Var = _LANGUAGE) -> Var:
507
- """Get the hook to register the language.
508
-
509
- Args:
510
- language_var: The const/literal Var of the language module to import.
511
- For markdown, uses the default placeholder _LANGUAGE. For direct use,
512
- a LiteralStringVar should be passed via the language prop.
513
-
514
- Returns:
515
- The hook to register the language.
516
- """
517
- language_in_there = Var.create(typing.get_args(LiteralCodeLanguage)).contains(
518
- language_var
519
- )
520
- async_load = f"""
521
- (async () => {{
522
- try {{
523
- const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${{{language_var!s}}}`);
524
- SyntaxHighlighter.registerLanguage({language_var!s}, module.default);
525
- }} catch (error) {{
526
- console.error(`Language ${{{language_var!s}}} is not supported for code blocks inside of markdown: `, error);
527
- }}
528
- }})();
529
- """
530
- return Var(
531
- f"""
532
- if ({language_var!s}) {{
533
- if (!{language_in_there!s}) {{
534
- console.warn(`Language \\`${{{language_var!s}}}\\` is not supported for code blocks inside of markdown.`);
535
- {language_var!s} = '';
536
- }} else {{
537
- {async_load!s}
538
- }}
539
- }}
540
- """
541
- if not isinstance(language_var, LiteralVar)
542
- else f"""
543
- if ({language_var!s}) {{
544
- {async_load!s}
545
- }}""",
546
- _var_data=VarData(
547
- imports={
548
- cls.get_fields()["library"].default_value(): [
549
- ImportVar(tag="PrismAsyncLight", alias="SyntaxHighlighter")
550
- ]
551
- },
552
- ),
553
- )
554
-
555
- @classmethod
556
- def get_component_map_custom_code(cls) -> Var:
557
- """Get the custom code for the component.
558
-
559
- Returns:
560
- The custom code for the component.
561
- """
562
- return cls._get_language_registration_hook()
563
-
564
- def add_hooks(self) -> list[str | Var]:
565
- """Add hooks for the component.
566
-
567
- Returns:
568
- The hooks for the component.
569
- """
570
- return [
571
- self._get_language_registration_hook(language_var=self.language),
572
- ]
573
-
574
504
 
575
505
  class CodeblockNamespace(ComponentNamespace):
576
506
  """Namespace for the CodeBlock component."""
@@ -987,9 +987,6 @@ class CodeBlock(Component, MarkdownComponentMap):
987
987
  """
988
988
 
989
989
  def add_style(self): ...
990
- @classmethod
991
- def get_component_map_custom_code(cls) -> Var: ...
992
- def add_hooks(self) -> list[str | Var]: ...
993
990
 
994
991
  class CodeblockNamespace(ComponentNamespace):
995
992
  themes = Theme
@@ -90,6 +90,7 @@ def load_dynamic_serializer():
90
90
  tag_name="MySSRComponent",
91
91
  memo_trigger_hooks=[],
92
92
  component=component,
93
+ export=True,
93
94
  )
94
95
  ] = None
95
96
 
@@ -123,16 +123,18 @@ class Link(RadixThemesComponent, A, MemoizationLeaf, MarkdownComponentMap):
123
123
 
124
124
  if "as_child" not in props:
125
125
  # Extract props for the ReactRouterLink, the rest go to the Link/A element.
126
- next_link_props = {}
126
+ react_router_link_props = {}
127
127
  for prop in props.copy():
128
128
  if prop in _KNOWN_REACT_ROUTER_LINK_PROPS:
129
- next_link_props[prop] = props.pop(prop)
129
+ react_router_link_props[prop] = props.pop(prop)
130
130
 
131
- next_link_props["to"] = next_link_props.pop("href", href)
131
+ react_router_link_props["to"] = react_router_link_props.pop(
132
+ "href", href
133
+ )
132
134
 
133
135
  # If user does not use `as_child`, by default we render using react_router_link to avoid page refresh during internal navigation
134
136
  return super().create(
135
- ReactRouterLink.create(*children, **next_link_props),
137
+ ReactRouterLink.create(*children, **react_router_link_props),
136
138
  as_child=True,
137
139
  **props,
138
140
  )
@@ -16,7 +16,6 @@ from .base import (
16
16
  Dirs,
17
17
  Env,
18
18
  LogLevel,
19
- Next,
20
19
  Ping,
21
20
  ReactRouter,
22
21
  Reflex,
@@ -101,13 +100,13 @@ __all__ = [
101
100
  "LogLevel",
102
101
  "MemoizationDisposition",
103
102
  "MemoizationMode",
104
- "Next",
105
103
  "Node",
106
104
  "PackageJson",
107
105
  "Page404",
108
106
  "PageNames",
109
107
  "Ping",
110
108
  "PyprojectToml",
109
+ "ReactRouter",
111
110
  "Reflex",
112
111
  "RequirementsTxt",
113
112
  "RouteArgType",
reflex/constants/base.py CHANGED
@@ -22,7 +22,7 @@ class Dirs(SimpleNamespace):
22
22
  """Various directories/paths used by Reflex."""
23
23
 
24
24
  # The frontend directories in a project.
25
- # The web folder where the NextJS app is compiled to.
25
+ # The web folder where the frontend app is compiled to.
26
26
  WEB = ".web"
27
27
  # The directory where uploaded files are stored.
28
28
  UPLOADED_FILES = "uploaded_files"
@@ -208,19 +208,6 @@ class Javascript(SimpleNamespace):
208
208
  PACKAGE_LOCK = "package-lock.json"
209
209
 
210
210
 
211
- class Next(Javascript):
212
- """Constants related to NextJS."""
213
-
214
- # The NextJS config file
215
- CONFIG_FILE = "next.config.js"
216
-
217
- # The sitemap config file.
218
- SITEMAP_CONFIG_FILE = "next-sitemap.config.js"
219
-
220
- # Regex to check for message displayed when frontend comes up
221
- FRONTEND_LISTENING_REGEX = "Local:[\\s]+(.*)"
222
-
223
-
224
211
  class ReactRouter(Javascript):
225
212
  """Constants related to React Router."""
226
213
 
@@ -80,7 +80,7 @@ class CompileVars(SimpleNamespace):
80
80
 
81
81
 
82
82
  class PageNames(SimpleNamespace):
83
- """The name of basic pages deployed in NextJS."""
83
+ """The name of basic pages deployed in the frontend."""
84
84
 
85
85
  # The name of the index page.
86
86
  INDEX_ROUTE = "index"
@@ -104,10 +104,9 @@ class PackageJson(SimpleNamespace):
104
104
  class Commands(SimpleNamespace):
105
105
  """The commands to define in package.json."""
106
106
 
107
- DEV = "vite dev"
107
+ DEV = "react-router dev"
108
108
  EXPORT = "react-router build"
109
- # --single rewrites /404.html to /404/index.html
110
- PROD = "serve --single ./build/client"
109
+ PROD = "serve ./build/client"
111
110
 
112
111
  PATH = "package.json"
113
112
 
@@ -131,6 +130,7 @@ class PackageJson(SimpleNamespace):
131
130
  "@react-router/node": cls._react_router_version,
132
131
  "serve": "14.2.4",
133
132
  "react": cls._react_version,
133
+ "react-helmet": "6.1.0",
134
134
  "react-dom": cls._react_version,
135
135
  "isbot": "5.1.26",
136
136
  "socket.io-client": "4.8.1",
reflex/constants/route.py CHANGED
@@ -36,13 +36,25 @@ ROUTER_DATA_INCLUDE = {RouteVar.PATH, RouteVar.ORIGIN, RouteVar.QUERY}
36
36
  class RouteRegex(SimpleNamespace):
37
37
  """Regex used for extracting route args in route."""
38
38
 
39
- ARG = re.compile(r"\[(?!\.)([^\[\]]+)\]")
40
- # group return the catchall pattern (i.e. "[[..slug]]")
41
- CATCHALL = re.compile(r"(\[?\[\.{3}(?![0-9]).*\]?\])")
42
- # group return the arg name (i.e. "slug")
43
- STRICT_CATCHALL = re.compile(r"\[\.{3}([a-zA-Z_][\w]*)\]")
44
- # group return the arg name (i.e. "slug") (optional arg can be empty)
45
- OPT_CATCHALL = re.compile(r"\[\[\.{3}([a-zA-Z_][\w]*)\]\]")
39
+ _DOT_DOT_DOT = r"\.\.\."
40
+ _OPENING_BRACKET = r"\["
41
+ _CLOSING_BRACKET = r"\]"
42
+ _ARG_NAME = r"[a-zA-Z_]\w*"
43
+
44
+ # match a single arg (i.e. "[slug]"), returns the name of the arg
45
+ ARG = re.compile(rf"{_OPENING_BRACKET}({_ARG_NAME}){_CLOSING_BRACKET}")
46
+ # match a single catch-all arg (i.e. "[...slug]" or "[[...slug]]"), returns the name of the arg
47
+ CATCHALL = re.compile(
48
+ rf"({_OPENING_BRACKET}?{_OPENING_BRACKET}{_DOT_DOT_DOT}[^[{_CLOSING_BRACKET}]*{_CLOSING_BRACKET}?{_CLOSING_BRACKET})"
49
+ )
50
+ # match a single non-optional catch-all arg (i.e. "[...slug]"), returns the name of the arg
51
+ STRICT_CATCHALL = re.compile(
52
+ rf"{_OPENING_BRACKET}{_DOT_DOT_DOT}({_ARG_NAME}){_CLOSING_BRACKET}"
53
+ )
54
+ # match a snigle optional catch-all arg (i.e. "[[...slug]]"), returns the name of the arg
55
+ OPT_CATCHALL = re.compile(
56
+ rf"{_OPENING_BRACKET * 2}{_DOT_DOT_DOT}({_ARG_NAME}){_CLOSING_BRACKET * 2}"
57
+ )
46
58
  SINGLE_SEGMENT = "__SINGLE_SEGMENT__"
47
59
  DOUBLE_SEGMENT = "__DOUBLE_SEGMENT__"
48
60
  SINGLE_CATCHALL_SEGMENT = "__SINGLE_CATCHALL_SEGMENT__"
reflex/route.py CHANGED
@@ -17,14 +17,13 @@ def verify_route_validity(route: str) -> None:
17
17
  ValueError: If the route is invalid.
18
18
  """
19
19
  pattern = catchall_in_route(route)
20
- if pattern and not route.endswith(pattern):
21
- msg = f"Catch-all must be the last part of the URL: {route}"
22
- raise ValueError(msg)
23
- if route == "api" or route.startswith("api/"):
24
- msg = (
25
- f"Cannot have a route prefixed with 'api/': {route} (conflicts with NextJS)"
26
- )
27
- raise ValueError(msg)
20
+ if pattern:
21
+ if pattern != "[[...splat]]":
22
+ msg = f"Catchall pattern `{pattern}` is not valid. Only `[[...splat]]` is allowed."
23
+ raise ValueError(msg)
24
+ if not route.endswith(pattern):
25
+ msg = f"Catchall pattern `{pattern}` must be at the end of the route."
26
+ raise ValueError(msg)
28
27
 
29
28
 
30
29
  def get_route_args(route: str) -> dict[str, str]:
@@ -81,6 +80,14 @@ def get_route_args(route: str) -> dict[str, str]:
81
80
  def catchall_in_route(route: str) -> str:
82
81
  """Extract the catchall part from a route.
83
82
 
83
+ Example:
84
+ >>> catchall_in_route("/posts/[...slug]")
85
+ '[...slug]'
86
+ >>> catchall_in_route("/posts/[[...slug]]")
87
+ '[[...slug]]'
88
+ >>> catchall_in_route("/posts/[slug]")
89
+ ''
90
+
84
91
  Args:
85
92
  route: the route from which to extract
86
93
 
@@ -91,19 +98,6 @@ def catchall_in_route(route: str) -> str:
91
98
  return match_.group() if match_ else ""
92
99
 
93
100
 
94
- def catchall_prefix(route: str) -> str:
95
- """Extract the prefix part from a route that contains a catchall.
96
-
97
- Args:
98
- route: the route from which to extract
99
-
100
- Returns:
101
- str: the prefix part of the URI
102
- """
103
- pattern = catchall_in_route(route)
104
- return route.replace(pattern, "") if pattern else ""
105
-
106
-
107
101
  def replace_brackets_with_keywords(input_string: str) -> str:
108
102
  """Replace brackets and everything inside it in a string with a keyword.
109
103
 
reflex/testing.py CHANGED
@@ -22,7 +22,7 @@ import types
22
22
  from collections.abc import AsyncIterator, Callable, Coroutine, Sequence
23
23
  from http.server import SimpleHTTPRequestHandler
24
24
  from pathlib import Path
25
- from typing import TYPE_CHECKING, Any, TypeVar
25
+ from typing import TYPE_CHECKING, Any, Literal, TypeVar
26
26
 
27
27
  import psutil
28
28
  import uvicorn
@@ -241,7 +241,7 @@ class AppHarness:
241
241
  def _initialize_app(self):
242
242
  # disable telemetry reporting for tests
243
243
 
244
- os.environ["TELEMETRY_ENABLED"] = "false"
244
+ os.environ["REFLEX_TELEMETRY_ENABLED"] = "false"
245
245
  CustomComponent.create().get_component.cache_clear()
246
246
  self.app_path.mkdir(parents=True, exist_ok=True)
247
247
  if self.app_source is not None:
@@ -526,7 +526,7 @@ class AppHarness:
526
526
  target: Callable[[], T],
527
527
  timeout: TimeoutType = None,
528
528
  step: TimeoutType = None,
529
- ) -> T | bool:
529
+ ) -> T | Literal[False]:
530
530
  """Generic polling logic.
531
531
 
532
532
  Args:
@@ -544,9 +544,10 @@ class AppHarness:
544
544
  step = POLL_INTERVAL
545
545
  deadline = time.time() + timeout
546
546
  while time.time() < deadline:
547
- success = target()
548
- if success:
549
- return success
547
+ with contextlib.suppress(Exception):
548
+ success = target()
549
+ if success:
550
+ return success
550
551
  time.sleep(step)
551
552
  return False
552
553
 
@@ -850,35 +851,55 @@ class AppHarness:
850
851
  return state_manager.states
851
852
 
852
853
  @staticmethod
853
- def poll_for_result(
854
- f: Callable[[], T],
855
- exception: type[Exception] = Exception,
856
- max_attempts: int = 5,
857
- seconds_between_attempts: int = 1,
854
+ def poll_for_or_raise_timeout(
855
+ target: Callable[[], T],
856
+ timeout: TimeoutType = None,
857
+ step: TimeoutType = None,
858
858
  ) -> T:
859
- """Poll for a result from a function.
859
+ """Poll target callable for a truthy return value.
860
+
861
+ Like `_poll_for`, but raises a `TimeoutError` if the target does not
862
+ return a truthy value within the timeout.
860
863
 
861
864
  Args:
862
- f: function to call
863
- exception: exception to catch
864
- max_attempts: maximum number of attempts
865
- seconds_between_attempts: seconds to wait between
865
+ target: callable that returns truthy if polling condition is met.
866
+ timeout: max polling time
867
+ step: interval between checking target()
866
868
 
867
869
  Returns:
868
- Result of the function
870
+ return value of target() if truthy within timeout
869
871
 
870
872
  Raises:
871
- AssertionError: if the function does not return a value
873
+ TimeoutError: when target does not return a truthy value within timeout
872
874
  """
873
- attempts = 0
874
- while attempts < max_attempts:
875
- try:
876
- return f()
877
- except exception: # noqa: PERF203
878
- attempts += 1
879
- time.sleep(seconds_between_attempts)
880
- msg = "Function did not return a value"
881
- raise AssertionError(msg)
875
+ result = AppHarness._poll_for(
876
+ target=target,
877
+ timeout=timeout,
878
+ step=step,
879
+ )
880
+ if result is False:
881
+ msg = "Target did not return a truthy value while polling."
882
+ raise TimeoutError(msg)
883
+ return result
884
+
885
+ @staticmethod
886
+ def expect(
887
+ target: Callable[[], T],
888
+ timeout: TimeoutType = None,
889
+ step: TimeoutType = None,
890
+ ):
891
+ """Expect a target callable to return a truthy value within the timeout.
892
+
893
+ Args:
894
+ target: callable that returns truthy if polling condition is met.
895
+ timeout: max polling time
896
+ step: interval between checking target()
897
+ """
898
+ AppHarness.poll_for_or_raise_timeout(
899
+ target=target,
900
+ timeout=timeout,
901
+ step=step,
902
+ )
882
903
 
883
904
 
884
905
  class SimpleHTTPRequestHandlerCustomErrors(SimpleHTTPRequestHandler):
reflex/utils/build.py CHANGED
@@ -9,7 +9,6 @@ from pathlib import Path
9
9
  from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
10
10
 
11
11
  from reflex import constants
12
- from reflex.config import get_config
13
12
  from reflex.utils import console, path_ops, prerequisites, processes
14
13
  from reflex.utils.exec import is_in_app_harness
15
14
 
@@ -149,24 +148,36 @@ def zip_app(
149
148
  )
150
149
 
151
150
 
152
- def build(
153
- deploy_url: str | None = None,
154
- for_export: bool = False,
155
- ):
156
- """Build the app for deployment.
151
+ def _duplicate_index_html_to_parent_dir(directory: Path):
152
+ """Duplicate index.html in the child directories to the given directory.
153
+
154
+ This makes accessing /route and /route/ work in production.
157
155
 
158
156
  Args:
159
- deploy_url: The deployment URL.
160
- for_export: Whether the build is for export.
157
+ directory: The directory to duplicate index.html to.
161
158
  """
159
+ for child in directory.iterdir():
160
+ if child.is_dir():
161
+ # If the child directory has an index.html, copy it to the parent directory.
162
+ index_html = child / "index.html"
163
+ if index_html.exists():
164
+ target = directory / (child.name + ".html")
165
+ if not target.exists():
166
+ console.debug(f"Copying {index_html} to {target}")
167
+ path_ops.cp(index_html, target)
168
+ else:
169
+ console.debug(f"Skipping {index_html}, already exists at {target}")
170
+ # Recursively call this function for the child directory.
171
+ _duplicate_index_html_to_parent_dir(child)
172
+
173
+
174
+ def build():
175
+ """Build the app for deployment."""
162
176
  wdir = prerequisites.get_web_dir()
163
177
 
164
178
  # Clean the static directory if it exists.
165
179
  path_ops.rm(str(wdir / constants.Dirs.BUILD_DIR))
166
180
 
167
- # The export command to run.
168
- command = "export"
169
-
170
181
  checkpoints = [
171
182
  "building for production",
172
183
  "building SSR bundle for production",
@@ -175,7 +186,11 @@ def build(
175
186
 
176
187
  # Start the subprocess with the progress bar.
177
188
  process = processes.new_process(
178
- [*prerequisites.get_js_package_executor(raise_on_none=True)[0], "run", command],
189
+ [
190
+ *prerequisites.get_js_package_executor(raise_on_none=True)[0],
191
+ "run",
192
+ "export",
193
+ ],
179
194
  cwd=wdir,
180
195
  shell=constants.IS_WINDOWS,
181
196
  env={
@@ -184,6 +199,7 @@ def build(
184
199
  },
185
200
  )
186
201
  processes.show_progress("Creating Production Build", process, checkpoints)
202
+ _duplicate_index_html_to_parent_dir(wdir / constants.Dirs.STATIC)
187
203
 
188
204
 
189
205
  def setup_frontend(
@@ -210,7 +226,7 @@ def setup_frontend_prod(
210
226
  root: The root path of the project.
211
227
  """
212
228
  setup_frontend(root)
213
- build(deploy_url=get_config().deploy_url)
229
+ build()
214
230
 
215
231
 
216
232
  def _looks_like_venv_dir(dir_to_check: str | Path) -> bool:
reflex/utils/export.py CHANGED
@@ -62,7 +62,7 @@ def export(
62
62
 
63
63
  # Build the static app.
64
64
  if frontend:
65
- build.build(deploy_url=config.deploy_url, for_export=True)
65
+ build.build()
66
66
 
67
67
  # Zip up the app.
68
68
  if zipping:
@@ -611,19 +611,19 @@ def get_redis_sync() -> RedisSync | None:
611
611
 
612
612
 
613
613
  def parse_redis_url() -> str | None:
614
- """Parse the REDIS_URL in config if applicable.
614
+ """Parse the REFLEX_REDIS_URL in config if applicable.
615
615
 
616
616
  Returns:
617
617
  If url is non-empty, return the URL as it is.
618
618
 
619
619
  Raises:
620
- ValueError: If the REDIS_URL is not a supported scheme.
620
+ ValueError: If the REFLEX_REDIS_URL is not a supported scheme.
621
621
  """
622
622
  config = get_config()
623
623
  if not config.redis_url:
624
624
  return None
625
625
  if not config.redis_url.startswith(("redis://", "rediss://", "unix://")):
626
- msg = "REDIS_URL must start with 'redis://', 'rediss://', or 'unix://'."
626
+ msg = "REFLEX_REDIS_URL must start with 'redis://', 'rediss://', or 'unix://'."
627
627
  raise ValueError(msg)
628
628
  return config.redis_url
629
629
 
@@ -1125,19 +1125,19 @@ def update_react_router_config(prerender_routes: bool = False):
1125
1125
  """
1126
1126
  react_router_config_file_path = get_web_dir() / constants.ReactRouter.CONFIG_FILE
1127
1127
 
1128
- react_router_config = _update_react_router_config(
1128
+ new_react_router_config = _update_react_router_config(
1129
1129
  get_config(), prerender_routes=prerender_routes
1130
1130
  )
1131
1131
 
1132
1132
  # Overwriting the config file triggers a full server reload, so make sure
1133
1133
  # there is actually a diff.
1134
- orig_next_config = (
1134
+ old_react_router_config = (
1135
1135
  react_router_config_file_path.read_text()
1136
1136
  if react_router_config_file_path.exists()
1137
1137
  else ""
1138
1138
  )
1139
- if orig_next_config != react_router_config:
1140
- react_router_config_file_path.write_text(react_router_config)
1139
+ if old_react_router_config != new_react_router_config:
1140
+ react_router_config_file_path.write_text(new_react_router_config)
1141
1141
 
1142
1142
 
1143
1143
  def _update_react_router_config(config: Config, prerender_routes: bool = False):
reflex/utils/processes.py CHANGED
@@ -320,7 +320,8 @@ def stream_logs(
320
320
 
321
321
  # Windows uvicorn bug
322
322
  # https://github.com/reflex-dev/reflex/issues/2335
323
- accepted_return_codes = [0, -2, 15] if constants.IS_WINDOWS else [0, -2]
323
+ # 130 is the exit code that react router returns when it is interrupted by a signal.
324
+ accepted_return_codes = [0, -2, 15, 130] if constants.IS_WINDOWS else [0, -2, 130]
324
325
  if process.returncode not in accepted_return_codes and not suppress_errors:
325
326
  console.error(f"{message} failed with exit code {process.returncode}")
326
327
  if "".join(logs).count("CERT_HAS_EXPIRED") > 0:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reflex
3
- Version: 0.8.0a1
3
+ Version: 0.8.0a3
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
@@ -2,7 +2,7 @@ reflex/__init__.py,sha256=S_nJPFaTYwyxSzfOpACFxlSe2Vc8rQGr5ZDeyRSDiYQ,10264
2
2
  reflex/__init__.pyi,sha256=xyNthKRk7NquzHFlg3FgBccSFya-YmmDmlh8SFecTIo,11167
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=-dwH-oDcN-1tzDRsyDeiosbMP9JOtiC2GrJ-HwHiS3w,75077
5
+ reflex/app.py,sha256=_echcweBHf0Ys4V93XdoF5D_6JcnP4Zz2qGQmi-Pkas,75083
6
6
  reflex/assets.py,sha256=l5O_mlrTprC0lF7Rc_McOe3a0OtSLnRdNl_PqCpDCBA,3431
7
7
  reflex/base.py,sha256=Oh664QL3fZEHErhUasFqP7fE4olYf1y-9Oj6uZI2FCU,1173
8
8
  reflex/config.py,sha256=7-OHwPa9UGlz8fxQx6SumT4netp6NgWRHJ3UvUL-opg,16631
@@ -12,10 +12,10 @@ reflex/model.py,sha256=xED7blemoiKxPFaOkCMrSayjjon7AJp8t5g459p7dGs,17646
12
12
  reflex/page.py,sha256=Bn8FTlUtjjKwUtpQA5r5eaWE_ZUb8i4XgrQi8FWbzNA,1880
13
13
  reflex/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  reflex/reflex.py,sha256=eUP0GevShUc1nodfA5_ewB3hgefWU0_x166HGCY8QTs,21606
15
- reflex/route.py,sha256=iO7VQQ9mTWG8LiVYNqZXtbtrD463aWNTS0wn1YolGbw,4192
15
+ reflex/route.py,sha256=7JXP7-dpqdbUS3iSEqQ5UjOHV3pNxD0wKn6BBCRQggQ,4078
16
16
  reflex/state.py,sha256=yM9bvTISBXpZqhP7gNcIBjBtAC9LCyyDSco9rlXDaNc,90976
17
17
  reflex/style.py,sha256=JxbXXA4MTnXrk0XHEoMBoNC7J-M2oL5Hl3W_QmXvmBg,13222
18
- reflex/testing.py,sha256=19F51vysGTOW1QzsfKJz5bxXFIIGqp1tZma8llCbXNU,38671
18
+ reflex/testing.py,sha256=KZ1MEagf3nR_KRug343aH8n5QSRmLQ2p-Pv-huApoZw,39414
19
19
  reflex/.templates/apps/blank/assets/favicon.ico,sha256=baxxgDAQ2V4-G5Q4S2yK5uUJTUGkv-AOWBQ0xd6myUo,4286
20
20
  reflex/.templates/apps/blank/code/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  reflex/.templates/apps/blank/code/blank.py,sha256=UmGz7aDxGULYINwLgotQoj-9qqpy7EOfAEpLmOT7C_k,917
@@ -26,18 +26,18 @@ reflex/.templates/jinja/custom_components/demo_app.py.jinja2,sha256=ipbKtObNqQLc
26
26
  reflex/.templates/jinja/custom_components/pyproject.toml.jinja2,sha256=HG-k3pruUlMy7xYz339hgFkNMTLqB-C_FTLKkOgfBPM,630
27
27
  reflex/.templates/jinja/custom_components/src.py.jinja2,sha256=e80PwMI6NoeQtGJ0NXWhYrkqUe7jvvJTFuztYQe-R5w,2403
28
28
  reflex/.templates/jinja/web/package.json.jinja2,sha256=cS9M6ADYDoYL2Kc8m5dYhV8eDaehOXb6qK_IOijT3N0,673
29
- reflex/.templates/jinja/web/pages/_app.js.jinja2,sha256=SxG94qVWuP8VZrRvGiPIirhvP9HqNopzQg2AdDSaOfg,1451
29
+ reflex/.templates/jinja/web/pages/_app.js.jinja2,sha256=10Wdl_7klhhisXfSY604iK0z10HsW_2E4aY2_MJvPeE,1581
30
30
  reflex/.templates/jinja/web/pages/_document.js.jinja2,sha256=v_r79GPGKnw1g9Bg4lK9o_ow5AzBnvKdz0nv3OgJyzU,166
31
31
  reflex/.templates/jinja/web/pages/base_page.js.jinja2,sha256=nteivFZgOhgwxlPvejgaoxKTPGvDRb7_JAXhsZDZeLM,361
32
32
  reflex/.templates/jinja/web/pages/component.js.jinja2,sha256=1Pui62uSL7LYA7FXZrh9ZmhKH8vHYu663eR134hhsAY,86
33
33
  reflex/.templates/jinja/web/pages/custom_component.js.jinja2,sha256=xUOfUwREPp4h2kozr6mEqSAASnNLzC9b9XHCXVsUQjg,485
34
34
  reflex/.templates/jinja/web/pages/index.js.jinja2,sha256=7sXw99TfjlcLYiLKlWvD3k-3IuFzLO4Y63mvSU97uds,360
35
35
  reflex/.templates/jinja/web/pages/macros.js.jinja2,sha256=RtMZ6eufmMrHghNDMKpueSAhd-znKjgBbJXAAHFc7vU,901
36
- reflex/.templates/jinja/web/pages/stateful_component.js.jinja2,sha256=Gt5gy3Uqe2o3Xp2MVLzM3PGKGlhkttuBNWsXa9P5RnA,321
36
+ reflex/.templates/jinja/web/pages/stateful_component.js.jinja2,sha256=gAipi5MiAVgHntadfThsogunlqpmUwLk5ROLyJJGgUM,387
37
37
  reflex/.templates/jinja/web/pages/stateful_components.js.jinja2,sha256=BfHi7ckH9u5xOliKWxjgmnia6AJbNnII97SC-dt_KSU,101
38
38
  reflex/.templates/jinja/web/pages/utils.js.jinja2,sha256=12v0-q-o-m0Oxr0H35GTtoJqGRpmbZSrRd5js95F5fI,3156
39
39
  reflex/.templates/jinja/web/styles/styles.css.jinja2,sha256=4-CvqGR8-nRzkuCOSp_PdqmhPEmOs_kOhskOlhLMEUg,141
40
- reflex/.templates/jinja/web/utils/context.js.jinja2,sha256=1YfWc690zJd_csL2U6PXpooOEgSDOJSCLN5B67DQUzg,4177
40
+ reflex/.templates/jinja/web/utils/context.js.jinja2,sha256=l1k-H4Y0DmOdeMr8xVEwkSzeBpVc1o94cOjJJ3B2WSY,4103
41
41
  reflex/.templates/jinja/web/utils/theme.js.jinja2,sha256=OSpBMh0Z9tTeqb10js4ZtnE9s1RV4gJfE8629csST8M,26
42
42
  reflex/.templates/web/.gitignore,sha256=3tT0CtVkCL09D_Y3Hd4myUgGcBuESeavCa0WHU5ifJ4,417
43
43
  reflex/.templates/web/jsconfig.json,sha256=rhQZZRBYxBWclFYTeU6UakzbGveM4qyRQZUpEAVhyqY,118
@@ -45,12 +45,12 @@ reflex/.templates/web/postcss.config.js,sha256=6Hf540Ny078yfmJ_-tniZtmgHW6euyEyx
45
45
  reflex/.templates/web/react-router.config.js,sha256=5K1FBryYdPusn1nE513GwB5_5UdPzoyH8ZMANUbpodQ,84
46
46
  reflex/.templates/web/vite.config.js,sha256=rfhF8ttx3q6j6JzEAw_VqwauqvrAP8IMC27FLF47Zm4,755
47
47
  reflex/.templates/web/app/entry.client.js,sha256=2Jv-5SQWHhHmA07BP50f1ew1V-LOsX5VoLtSInnFDj8,264
48
- reflex/.templates/web/app/routes.js,sha256=rEtTjnX4iLzYB-l2Yndi6HmobheTCAPUsOE7cATCTBw,312
49
- reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js,sha256=NorpstVoGRoa-rgjAOL-61DlKxts0knIGcdMjMQx_Qs,959
48
+ reflex/.templates/web/app/routes.js,sha256=GerImlcrkF3qnKxHBS_dgTEHmXOBVtSUHt1BvQWN1KI,315
49
+ reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js,sha256=dnDHW49imtdalZJQqj7J_u7cj2z8Sve7OlhtOO0FbKE,916
50
50
  reflex/.templates/web/components/shiki/code.js,sha256=4Es1pxsr-lX4hTQ5mglrwwC6O_SI-z-O60k03z8VFzQ,1144
51
51
  reflex/.templates/web/utils/client_side_routing.js,sha256=zK_Se1BMjrAHvYqcGH1cu3_UfUhXJ5KxeSt8Xgg2-70,1619
52
- reflex/.templates/web/utils/react-theme.js,sha256=7udgjEbNleNtrsjBT7SMVXLP4PQosWloG8xdeZJl8Kg,2267
53
- reflex/.templates/web/utils/state.js,sha256=63SP5SQWdpJaxYTMb_7uttul4M8T6vJAZAsWnQArFCk,33972
52
+ reflex/.templates/web/utils/react-theme.js,sha256=4CNLOcjLS5tiRp0TIgKK7trf8bSLWtmeDVhFP6BLe80,2349
53
+ reflex/.templates/web/utils/state.js,sha256=Bz_D4G8faACDYY3AFtjyCrr8VniDSpV3dDcXVoIhG3g,34146
54
54
  reflex/.templates/web/utils/helpers/dataeditor.js,sha256=pG6MgsHuStDR7-qPipzfiK32j9bKDBa-4hZ0JSUo4JM,1623
55
55
  reflex/.templates/web/utils/helpers/debounce.js,sha256=xGhtTRtS_xIcaeqnYVvYJNseLgQVk-DW-eFiHJYO9As,528
56
56
  reflex/.templates/web/utils/helpers/paste.js,sha256=ef30HsR83jRzzvZnl8yV79yqFP8TC_u8SlN99cCS_OM,1799
@@ -61,13 +61,13 @@ reflex/app_mixins/lifespan.py,sha256=9rrdVUY0nHyk3gj31WJm7mw6I7KHBNtvSs9tZdwUm4I
61
61
  reflex/app_mixins/middleware.py,sha256=BKhe0jUFO1_TylEC48LUZyaeYyPmAYW-NV4H5Rw221k,2848
62
62
  reflex/app_mixins/mixin.py,sha256=R1YncalqDrbdPZvpKVbm72ZKmQZxYAWfuFq9JknzTqQ,305
63
63
  reflex/compiler/__init__.py,sha256=r8jqmDSFf09iV2lHlNhfc9XrTLjNxfDNwPYlxS4cmHE,27
64
- reflex/compiler/compiler.py,sha256=F1TufK-yzmRCcZl48uJHHb33Ls8HAeNzNc-Gl-xJUeo,29473
65
- reflex/compiler/templates.py,sha256=SKFIJtL_ozEvEHhiMj34tBwBN1CWSYy_-VzlojZMG44,6015
66
- reflex/compiler/utils.py,sha256=hwtpqF9HfFOxoP0uuPBcRLgEcJiDTCezbkTQZLpxKKk,17750
64
+ reflex/compiler/compiler.py,sha256=YRVoN2FqHZEZ8B6wOSYrTXjA-7nIjDJEgP3v2FtGIiU,29307
65
+ reflex/compiler/templates.py,sha256=mQifVgvE7cKyzMFL9F5jxdJb9KFxucWJa7nuOp09ZUo,6002
66
+ reflex/compiler/utils.py,sha256=FrO_nnWab847kwvkKSWUA2tXcd766Q9H0ZEALdH7f2E,18239
67
67
  reflex/components/__init__.py,sha256=55uockd2mEBsaA-fmDO_R4vJewzGPZUk-6UZFNHBSdc,568
68
68
  reflex/components/__init__.pyi,sha256=w3ytnNHEwibvm5Z_7udLu2JdQijWrjU0TVGFkg_jlGs,744
69
- reflex/components/component.py,sha256=Fn5O5f8nlRGOQdu9EgQfPt_KDhcBKNL_p8IHUcfS0Rs,97192
70
- reflex/components/dynamic.py,sha256=UCbFUCZ1JYGhaDBKOsgyiDMi8qw3n_nt_-Xm6NhhLiI,7393
69
+ reflex/components/component.py,sha256=yjVYLZ4LGlfj7uV9O3QpM3-f_OKvHK9lHcNOdJI3mpo,97642
70
+ reflex/components/dynamic.py,sha256=AyOjAW5LQZlDShIFJ7d9P2dFTI-1pr1tZR32E2Rhr9s,7422
71
71
  reflex/components/field.py,sha256=j5JZFzNlET3GAIW91m1L31RypXylMAxJNm0-CJbtykM,5745
72
72
  reflex/components/literals.py,sha256=hogLnwTJxFJODIvqihg-GD9kFZVsEBDoYzaRit56Nuk,501
73
73
  reflex/components/props.py,sha256=BH7RiRu_EI2BRkB1PyBVp6tLeFTTV4FzGEdDIXXQ9Bk,14378
@@ -121,8 +121,8 @@ reflex/components/core/upload.pyi,sha256=znpNNiJdACw825qCiMw_UUNXIIDbBL55wJhHU_8
121
121
  reflex/components/core/layout/__init__.py,sha256=znldZaj_NGt8qCZDG70GMwjMTskcvCf_2N_EjCAHwdc,30
122
122
  reflex/components/datadisplay/__init__.py,sha256=L8pWWKNHWdUD2fbZRoEKjd_8c_hpDdGYO463hwkoIi4,438
123
123
  reflex/components/datadisplay/__init__.pyi,sha256=rYMwO_X4NvUex6IL2MMTnhdFRp8Lz5zweMwXaW_l7nc,588
124
- reflex/components/datadisplay/code.py,sha256=NZFgTohdXnVN_KWb0qJpq3vP_8cPhDtW0fZlzx9JPlw,14666
125
- reflex/components/datadisplay/code.pyi,sha256=NWlMsQP_WwRp1oAq43Gtp5Vs5i3UR6lpHa2Hrpw_8ys,41695
124
+ reflex/components/datadisplay/code.py,sha256=jmt5pCFnHbbx8JzU5LvevwDL6omlIsdf_AbPsW7s5TI,12418
125
+ reflex/components/datadisplay/code.pyi,sha256=fp-__tDq6lfjDetBsKq5KVZv2SXzFuK8Z3E41_FGwsA,41575
126
126
  reflex/components/datadisplay/dataeditor.py,sha256=SxvIDyKl9TILOlx6Zga9jCcu0LBc4E1WhrUFJGO6lKs,13496
127
127
  reflex/components/datadisplay/dataeditor.pyi,sha256=URCSl_0xAj0AF2yObFenNc76kO14YO9Xikq5Ec347lA,12375
128
128
  reflex/components/datadisplay/logo.py,sha256=xvg5TRVRSi2IKn7Kg4oYzWcaFMHfXxUaCp0cQmuKSn0,1993
@@ -298,7 +298,7 @@ reflex/components/radix/themes/typography/code.py,sha256=sSshc5GsSOsJPi_SbR7nkAe
298
298
  reflex/components/radix/themes/typography/code.pyi,sha256=rsDiLZJinQCF1YCGfCVtEASuUluV8kG9WjS2F9QhDW4,11780
299
299
  reflex/components/radix/themes/typography/heading.py,sha256=YVoQu9cLx5SJSNkliNp5UyVhrwu7vgpIMj1X43jifd0,1575
300
300
  reflex/components/radix/themes/typography/heading.pyi,sha256=EVgYHfpGtlq681UQLpPJyog9PxD62ZMrADhQutxScu0,12598
301
- reflex/components/radix/themes/typography/link.py,sha256=cnYYnAb8V-dXmEfKVsQkPPxvNsMoHTBPkiqCaRfU1F8,4935
301
+ reflex/components/radix/themes/typography/link.py,sha256=-wnjg_b2xjGpyfppsqy_53Z_r0Kky2p1gxjDZAmbx84,5013
302
302
  reflex/components/radix/themes/typography/link.pyi,sha256=FBiFOj8ivnaBP46ZgAimkl24rgkR6Z-TB3ewZ3_Q7KE,25956
303
303
  reflex/components/radix/themes/typography/text.py,sha256=qnpZuqyabh-VuKGBtlOmu3dfZloBXehjCtEk4AmZQLg,2843
304
304
  reflex/components/radix/themes/typography/text.pyi,sha256=mqhURMNgLum1rRk9OzGK0v71hfKZ4-tdqE1rMYbZjZw,73469
@@ -330,15 +330,15 @@ reflex/components/tags/iter_tag.py,sha256=R7bOI3Xx3mHcSmr6tz8qUlBoDwXsAh2Q6Esd3n
330
330
  reflex/components/tags/match_tag.py,sha256=3lba1H5pCcKkqxEHzM6DZb5s9s0yJLN4Se3vdhzar24,580
331
331
  reflex/components/tags/tag.py,sha256=BRPODHi1R5g4VwkYLztIUJBMyDgrGPZRqB-QP_u67jk,3665
332
332
  reflex/components/tags/tagless.py,sha256=qO7Gm4V0ITDyymHkyltfz53155ZBt-W_WIPDYy93ca0,587
333
- reflex/constants/__init__.py,sha256=m1lC2qVSEj-i7msyTm4RoSAsi1BRV3ZW4IYCIBqOC0I,2193
334
- reflex/constants/base.py,sha256=c_1YsCwnDBJBZojqDqJf373XGKIfe7VBvOX6L2uBhyc,9393
333
+ reflex/constants/__init__.py,sha256=q2Jf-LBbNcGrOmx5M7QotIAYW_t3m02TsmmdtJ5_IhM,2190
334
+ reflex/constants/base.py,sha256=sFv9DhRZtEM2fix6U8SwTcBn05zl5Z_X8X-bqr3fh2E,9065
335
335
  reflex/constants/colors.py,sha256=n-FN7stNrvk5rCN0TAvE28dqwUeQZHue-b5q1CO0EyQ,2048
336
- reflex/constants/compiler.py,sha256=oyeXsQVSH7cTYt-qjH2OSwLHySPikrk3oLVLs7tspiU,5808
336
+ reflex/constants/compiler.py,sha256=JjUL7myEoRi6-VRo5vxiND2WFaDTBbplfy6iLswazqk,5814
337
337
  reflex/constants/config.py,sha256=8OIjiBdZZJrRVHsNBheMwopE9AwBFFzau0SXqXKcrPg,1715
338
338
  reflex/constants/custom_components.py,sha256=joJt4CEt1yKy7wsBH6vYo7_QRW0O_fWXrrTf0VY2q14,1317
339
339
  reflex/constants/event.py,sha256=tgoynWQi2L0_Kqc3XhXo7XXL76A-OKhJGHRrNjm7gFw,2885
340
- reflex/constants/installer.py,sha256=3yB0t-Gd7xdiW5FafjQfKmXpNpUz0QIaZs3yBw1gXv0,4156
341
- reflex/constants/route.py,sha256=YnLgsp0iYc1lFjQ-cEqTlSE5SEeaNkaWORBoUM0-taI,2079
340
+ reflex/constants/installer.py,sha256=JjZcvXYxiABnnZnBBMMePr8C4279cECzfTRKxe7rHXs,4135
341
+ reflex/constants/route.py,sha256=ZClrIF6Wog7tV3R2gLBrfLyO8Wc_jGZy2b1toy1f1Lk,2619
342
342
  reflex/constants/state.py,sha256=uF_7-M9Gid-P3DjAOq4F1ERplyZhiNccowo_jLrdJrg,323
343
343
  reflex/constants/utils.py,sha256=e1ChEvbHfmE_V2UJvCSUhD_qTVAIhEGPpRJSqdSd6PA,780
344
344
  reflex/custom_components/__init__.py,sha256=R4zsvOi4dfPmHc18KEphohXnQFBPnUCb50cMR5hSLDE,36
@@ -363,22 +363,22 @@ reflex/plugins/sitemap.py,sha256=dbbqIWaim4zl1LEU6Su-gyxd0BgM3E6S8gCExDfkTRo,615
363
363
  reflex/plugins/tailwind_v3.py,sha256=CvRWAEmY8zW9MwkfN7ZDf6Ltci4btF7fq7rgDcX2tPE,4860
364
364
  reflex/plugins/tailwind_v4.py,sha256=BICNY_xTsEfujRo57aR0yHGELZBaN5SEUIT24SYcf0o,5266
365
365
  reflex/utils/__init__.py,sha256=y-AHKiRQAhk2oAkvn7W8cRVTZVK625ff8tTwvZtO7S4,24
366
- reflex/utils/build.py,sha256=wELqxVj3A6lN6Yr3k5XK68lhmVa-E2MhHxFOU-ueDM8,7173
366
+ reflex/utils/build.py,sha256=lk8hE69onG95dv-LxRhjtEugct1g-KcWPUDorzqeGIE,7964
367
367
  reflex/utils/codespaces.py,sha256=kEQ-j-jclTukFpXDlYgNp95kYMGDrQmP3VNEoYGZ1u4,3052
368
368
  reflex/utils/compat.py,sha256=aSJH_M6iomgHPQ4onQ153xh1MWqPi3HSYDzE68N6gZM,2635
369
369
  reflex/utils/console.py,sha256=OFyXqnyhpAgXCDM7m5lokoUMjvXMohc2ftgrmcf62nc,11500
370
370
  reflex/utils/decorator.py,sha256=DVrlVGljV5OchMs-5_y1CbbqnCWlH6lv-dFko8yHxVY,1738
371
371
  reflex/utils/exceptions.py,sha256=Wwu7Ji2xgq521bJKtU2NgjwhmFfnG8erirEVN2h8S-g,8884
372
372
  reflex/utils/exec.py,sha256=q4mk1QezowCQzd07TCH_UNg1hl_upP6W3qhhAA66_HM,20848
373
- reflex/utils/export.py,sha256=lidyOtQpqgb0hTZccZrul8CzyWdipD2zsLYtu1D-8-w,2607
373
+ reflex/utils/export.py,sha256=Z2AHuhkxGQzOi9I90BejQ4qEcD0URr2i-ZU5qTJt7eQ,2562
374
374
  reflex/utils/format.py,sha256=TGPrbqByYMkUjXgFatk_UvMy4tMP8ByzVYvOB7Z6-Xo,21302
375
375
  reflex/utils/imports.py,sha256=tcLE4hLdNolFMe1EyDOOyYIc6FSjjtwx7-n18Pcg6pE,3867
376
376
  reflex/utils/lazy_loader.py,sha256=UREKeql_aSusSFMn6qldyol4n8qD3Sm9Wg7LLICjJgQ,4136
377
377
  reflex/utils/misc.py,sha256=VquggT3rJB5afQtW3oLekLaF7emM96BL6LVJ6sNr-Ek,1504
378
378
  reflex/utils/net.py,sha256=HEHA8L5g7L9s0fFG4dTiZzB9PFO_0WRrlbMgpZr_GAQ,4093
379
379
  reflex/utils/path_ops.py,sha256=_RS17IQDNr5vcoLLGZx2-z1E5WP-JgDHvaRAOgqrZiU,8154
380
- reflex/utils/prerequisites.py,sha256=waii2AFwTrCZ70b6wcFWzPkLf4OOYX0xpl0438zP2MM,66132
381
- reflex/utils/processes.py,sha256=QWzGn2v8LQ5lLkEj7C7UdQFo0HUXbdCKe3vnW_qzHPE,16572
380
+ reflex/utils/prerequisites.py,sha256=JuQg_8d5CqLG8uUsilK_yg100ZyCRFGEWMnmlPekFv0,66179
381
+ reflex/utils/processes.py,sha256=yc64mWPcOYdUWMGHClGpvW1sDkNCOEitztsEOUk1Cp8,16671
382
382
  reflex/utils/pyi_generator.py,sha256=gqwMZNs6SNPooA-4p1E7QFGEjaGDuvYuErrs4Sb2oXA,46296
383
383
  reflex/utils/redir.py,sha256=3JG0cRdfIZnFIBHHN32ynD5cfbUZa7gLRxzrxRGGl5I,1751
384
384
  reflex/utils/registry.py,sha256=DEF7csYQ5VnrZhy6ULVfMlceh7XVH0pks96lIyyThuc,1882
@@ -394,8 +394,8 @@ reflex/vars/number.py,sha256=tO7pnvFaBsedq1HWT4skytnSqHWMluGEhUbjAUMx8XQ,28190
394
394
  reflex/vars/object.py,sha256=BDmeiwG8v97s_BnR1Egq3NxOKVjv9TfnREB3cz0zZtk,17322
395
395
  reflex/vars/sequence.py,sha256=1kBrqihspyjyQ1XDqFPC8OpVGtZs_EVkOdIKBro5ilA,55249
396
396
  scripts/hatch_build.py,sha256=-4pxcLSFmirmujGpQX9UUxjhIC03tQ_fIQwVbHu9kc0,1861
397
- reflex-0.8.0a1.dist-info/METADATA,sha256=T99HBQrDCvW30a2Dfey8lHXdki12sMGrwAUnSoCrXDE,12344
398
- reflex-0.8.0a1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
399
- reflex-0.8.0a1.dist-info/entry_points.txt,sha256=Rxt4dXc7MLBNt5CSHTehVPuSe9Xqow4HLX55nD9tQQ0,45
400
- reflex-0.8.0a1.dist-info/licenses/LICENSE,sha256=dw3zLrp9f5ObD7kqS32vWfhcImfO52PMmRqvtxq_YEE,11358
401
- reflex-0.8.0a1.dist-info/RECORD,,
397
+ reflex-0.8.0a3.dist-info/METADATA,sha256=NHTFLWFi7bzD4Aj8bFxHDlIxqtyQjYw7E2Ilm0c-VFI,12344
398
+ reflex-0.8.0a3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
399
+ reflex-0.8.0a3.dist-info/entry_points.txt,sha256=Rxt4dXc7MLBNt5CSHTehVPuSe9Xqow4HLX55nD9tQQ0,45
400
+ reflex-0.8.0a3.dist-info/licenses/LICENSE,sha256=dw3zLrp9f5ObD7kqS32vWfhcImfO52PMmRqvtxq_YEE,11358
401
+ reflex-0.8.0a3.dist-info/RECORD,,