reflex 0.7.14a6__py3-none-any.whl → 0.8.0a1__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.

Files changed (206) hide show
  1. reflex/.templates/jinja/app/rxconfig.py.jinja2 +4 -1
  2. reflex/.templates/jinja/web/package.json.jinja2 +1 -1
  3. reflex/.templates/jinja/web/pages/_app.js.jinja2 +16 -10
  4. reflex/.templates/jinja/web/pages/_document.js.jinja2 +1 -1
  5. reflex/.templates/jinja/web/pages/base_page.js.jinja2 +0 -1
  6. reflex/.templates/jinja/web/utils/context.js.jinja2 +25 -6
  7. reflex/.templates/web/app/entry.client.js +8 -0
  8. reflex/.templates/web/app/routes.js +10 -0
  9. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +12 -32
  10. reflex/.templates/web/postcss.config.js +1 -1
  11. reflex/.templates/web/react-router.config.js +6 -0
  12. reflex/.templates/web/utils/client_side_routing.js +21 -19
  13. reflex/.templates/web/utils/react-theme.js +89 -0
  14. reflex/.templates/web/utils/state.js +155 -67
  15. reflex/.templates/web/vite.config.js +32 -0
  16. reflex/__init__.py +1 -6
  17. reflex/__init__.pyi +0 -4
  18. reflex/app.py +52 -115
  19. reflex/base.py +1 -87
  20. reflex/compiler/compiler.py +40 -3
  21. reflex/compiler/utils.py +54 -28
  22. reflex/components/__init__.py +0 -2
  23. reflex/components/__init__.pyi +0 -3
  24. reflex/components/base/__init__.py +1 -5
  25. reflex/components/base/__init__.pyi +4 -6
  26. reflex/components/base/app_wrap.pyi +5 -4
  27. reflex/components/base/body.pyi +5 -4
  28. reflex/components/base/document.py +18 -14
  29. reflex/components/base/document.pyi +83 -27
  30. reflex/components/base/error_boundary.pyi +5 -4
  31. reflex/components/base/fragment.pyi +5 -4
  32. reflex/components/base/link.pyi +9 -7
  33. reflex/components/base/meta.pyi +17 -13
  34. reflex/components/base/script.py +60 -58
  35. reflex/components/base/script.pyi +246 -31
  36. reflex/components/base/strict_mode.pyi +5 -4
  37. reflex/components/component.py +109 -194
  38. reflex/components/core/__init__.py +1 -0
  39. reflex/components/core/__init__.pyi +1 -0
  40. reflex/components/core/auto_scroll.pyi +5 -4
  41. reflex/components/core/banner.pyi +25 -19
  42. reflex/components/core/client_side_routing.py +7 -6
  43. reflex/components/core/client_side_routing.pyi +6 -56
  44. reflex/components/core/clipboard.pyi +5 -4
  45. reflex/components/core/debounce.py +1 -0
  46. reflex/components/core/debounce.pyi +5 -4
  47. reflex/components/core/foreach.py +3 -2
  48. reflex/components/core/helmet.py +14 -0
  49. reflex/components/{next/base.pyi → core/helmet.pyi} +10 -7
  50. reflex/components/core/html.pyi +5 -4
  51. reflex/components/core/sticky.pyi +17 -13
  52. reflex/components/core/upload.py +2 -1
  53. reflex/components/core/upload.pyi +21 -16
  54. reflex/components/datadisplay/code.pyi +9 -7
  55. reflex/components/datadisplay/dataeditor.pyi +5 -4
  56. reflex/components/datadisplay/shiki_code_block.pyi +13 -10
  57. reflex/components/dynamic.py +4 -5
  58. reflex/components/el/element.pyi +5 -4
  59. reflex/components/el/elements/base.pyi +5 -4
  60. reflex/components/el/elements/forms.pyi +69 -52
  61. reflex/components/el/elements/inline.pyi +113 -85
  62. reflex/components/el/elements/media.pyi +105 -79
  63. reflex/components/el/elements/metadata.pyi +25 -19
  64. reflex/components/el/elements/other.pyi +29 -22
  65. reflex/components/el/elements/scripts.pyi +13 -10
  66. reflex/components/el/elements/sectioning.pyi +61 -46
  67. reflex/components/el/elements/tables.pyi +41 -31
  68. reflex/components/el/elements/typography.pyi +61 -46
  69. reflex/components/field.py +175 -0
  70. reflex/components/gridjs/datatable.py +2 -2
  71. reflex/components/gridjs/datatable.pyi +11 -9
  72. reflex/components/lucide/icon.py +6 -2
  73. reflex/components/lucide/icon.pyi +15 -10
  74. reflex/components/markdown/markdown.pyi +5 -4
  75. reflex/components/moment/moment.pyi +5 -4
  76. reflex/components/plotly/plotly.pyi +19 -10
  77. reflex/components/props.py +376 -27
  78. reflex/components/radix/primitives/accordion.py +8 -1
  79. reflex/components/radix/primitives/accordion.pyi +29 -22
  80. reflex/components/radix/primitives/base.pyi +9 -7
  81. reflex/components/radix/primitives/drawer.pyi +45 -34
  82. reflex/components/radix/primitives/form.pyi +41 -31
  83. reflex/components/radix/primitives/progress.pyi +21 -16
  84. reflex/components/radix/primitives/slider.pyi +21 -16
  85. reflex/components/radix/themes/base.py +3 -3
  86. reflex/components/radix/themes/base.pyi +33 -25
  87. reflex/components/radix/themes/color_mode.pyi +13 -10
  88. reflex/components/radix/themes/components/alert_dialog.pyi +29 -22
  89. reflex/components/radix/themes/components/aspect_ratio.pyi +5 -4
  90. reflex/components/radix/themes/components/avatar.pyi +5 -4
  91. reflex/components/radix/themes/components/badge.pyi +5 -4
  92. reflex/components/radix/themes/components/button.pyi +5 -4
  93. reflex/components/radix/themes/components/callout.pyi +21 -16
  94. reflex/components/radix/themes/components/card.pyi +5 -4
  95. reflex/components/radix/themes/components/checkbox.pyi +13 -10
  96. reflex/components/radix/themes/components/checkbox_cards.pyi +9 -7
  97. reflex/components/radix/themes/components/checkbox_group.pyi +9 -7
  98. reflex/components/radix/themes/components/context_menu.pyi +53 -40
  99. reflex/components/radix/themes/components/data_list.pyi +17 -13
  100. reflex/components/radix/themes/components/dialog.pyi +29 -22
  101. reflex/components/radix/themes/components/dropdown_menu.pyi +33 -25
  102. reflex/components/radix/themes/components/hover_card.pyi +17 -13
  103. reflex/components/radix/themes/components/icon_button.pyi +5 -4
  104. reflex/components/radix/themes/components/inset.pyi +5 -4
  105. reflex/components/radix/themes/components/popover.pyi +17 -13
  106. reflex/components/radix/themes/components/progress.pyi +5 -4
  107. reflex/components/radix/themes/components/radio.pyi +5 -4
  108. reflex/components/radix/themes/components/radio_cards.pyi +9 -7
  109. reflex/components/radix/themes/components/radio_group.pyi +17 -13
  110. reflex/components/radix/themes/components/scroll_area.pyi +5 -4
  111. reflex/components/radix/themes/components/segmented_control.pyi +9 -7
  112. reflex/components/radix/themes/components/select.pyi +37 -28
  113. reflex/components/radix/themes/components/separator.pyi +5 -4
  114. reflex/components/radix/themes/components/skeleton.pyi +5 -4
  115. reflex/components/radix/themes/components/slider.pyi +5 -4
  116. reflex/components/radix/themes/components/spinner.pyi +5 -4
  117. reflex/components/radix/themes/components/switch.pyi +5 -4
  118. reflex/components/radix/themes/components/table.pyi +29 -22
  119. reflex/components/radix/themes/components/tabs.pyi +21 -16
  120. reflex/components/radix/themes/components/text_area.pyi +5 -4
  121. reflex/components/radix/themes/components/text_field.pyi +13 -10
  122. reflex/components/radix/themes/components/tooltip.pyi +5 -4
  123. reflex/components/radix/themes/layout/base.pyi +5 -4
  124. reflex/components/radix/themes/layout/box.pyi +5 -4
  125. reflex/components/radix/themes/layout/center.pyi +5 -4
  126. reflex/components/radix/themes/layout/container.pyi +5 -4
  127. reflex/components/radix/themes/layout/flex.pyi +5 -4
  128. reflex/components/radix/themes/layout/grid.pyi +5 -4
  129. reflex/components/radix/themes/layout/list.pyi +21 -16
  130. reflex/components/radix/themes/layout/section.pyi +5 -4
  131. reflex/components/radix/themes/layout/spacer.pyi +5 -4
  132. reflex/components/radix/themes/layout/stack.pyi +13 -10
  133. reflex/components/radix/themes/typography/blockquote.pyi +5 -4
  134. reflex/components/radix/themes/typography/code.pyi +5 -4
  135. reflex/components/radix/themes/typography/heading.pyi +5 -4
  136. reflex/components/radix/themes/typography/link.py +42 -9
  137. reflex/components/radix/themes/typography/link.pyi +311 -6
  138. reflex/components/radix/themes/typography/text.pyi +29 -22
  139. reflex/components/react_player/audio.pyi +5 -4
  140. reflex/components/react_player/react_player.pyi +5 -4
  141. reflex/components/react_player/video.pyi +5 -4
  142. reflex/components/recharts/cartesian.py +2 -1
  143. reflex/components/recharts/cartesian.pyi +65 -46
  144. reflex/components/recharts/charts.py +4 -2
  145. reflex/components/recharts/charts.pyi +36 -24
  146. reflex/components/recharts/general.pyi +24 -18
  147. reflex/components/recharts/polar.py +8 -4
  148. reflex/components/recharts/polar.pyi +16 -10
  149. reflex/components/recharts/recharts.pyi +9 -7
  150. reflex/components/sonner/toast.py +2 -2
  151. reflex/components/sonner/toast.pyi +7 -6
  152. reflex/config.py +3 -77
  153. reflex/constants/__init__.py +1 -0
  154. reflex/constants/base.py +38 -8
  155. reflex/constants/compiler.py +4 -2
  156. reflex/constants/event.py +1 -0
  157. reflex/constants/installer.py +23 -16
  158. reflex/constants/state.py +2 -0
  159. reflex/custom_components/custom_components.py +0 -14
  160. reflex/environment.py +1 -1
  161. reflex/event.py +178 -81
  162. reflex/experimental/__init__.py +0 -30
  163. reflex/istate/proxy.py +5 -3
  164. reflex/page.py +0 -27
  165. reflex/plugins/__init__.py +3 -2
  166. reflex/plugins/base.py +5 -1
  167. reflex/plugins/shared_tailwind.py +158 -0
  168. reflex/plugins/sitemap.py +206 -0
  169. reflex/plugins/tailwind_v3.py +13 -106
  170. reflex/plugins/tailwind_v4.py +15 -108
  171. reflex/reflex.py +1 -0
  172. reflex/state.py +134 -140
  173. reflex/testing.py +57 -9
  174. reflex/utils/build.py +9 -69
  175. reflex/utils/exec.py +59 -161
  176. reflex/utils/export.py +1 -1
  177. reflex/utils/imports.py +0 -4
  178. reflex/utils/misc.py +28 -0
  179. reflex/utils/prerequisites.py +62 -59
  180. reflex/utils/processes.py +6 -6
  181. reflex/utils/pyi_generator.py +21 -9
  182. reflex/utils/serializers.py +14 -1
  183. reflex/utils/types.py +196 -61
  184. reflex/vars/__init__.py +2 -0
  185. reflex/vars/base.py +367 -134
  186. {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/METADATA +12 -5
  187. {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/RECORD +190 -197
  188. reflex/.templates/web/next.config.js +0 -7
  189. reflex/components/base/head.py +0 -20
  190. reflex/components/base/head.pyi +0 -116
  191. reflex/components/next/__init__.py +0 -10
  192. reflex/components/next/base.py +0 -7
  193. reflex/components/next/image.py +0 -117
  194. reflex/components/next/image.pyi +0 -94
  195. reflex/components/next/link.py +0 -20
  196. reflex/components/next/link.pyi +0 -67
  197. reflex/components/next/video.py +0 -38
  198. reflex/components/next/video.pyi +0 -68
  199. reflex/components/suneditor/__init__.py +0 -5
  200. reflex/components/suneditor/editor.py +0 -269
  201. reflex/components/suneditor/editor.pyi +0 -199
  202. reflex/experimental/layout.py +0 -254
  203. reflex/experimental/layout.pyi +0 -814
  204. {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/WHEEL +0 -0
  205. {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/entry_points.txt +0 -0
  206. {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/licenses/LICENSE +0 -0
@@ -2,5 +2,8 @@ import reflex as rx
2
2
 
3
3
  config = rx.Config(
4
4
  app_name="{{ app_name }}",
5
- plugins=[rx.plugins.TailwindV3Plugin()],
5
+ plugins=[
6
+ rx.plugins.SitemapPlugin(),
7
+ rx.plugins.TailwindV4Plugin(),
8
+ ],
6
9
  )
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "reflex",
3
+ "type": "module",
3
4
  "scripts": {
4
5
  "dev": "{{ scripts.dev }}",
5
6
  "export": "{{ scripts.export }}",
6
- "export-sitemap": "{{ scripts.export_sitemap }}",
7
7
  "prod": "{{ scripts.prod }}"
8
8
  },
9
9
  "dependencies": {
@@ -6,8 +6,10 @@ import '$/styles/__reflex_global_styles.css'
6
6
  {% endblock %}
7
7
 
8
8
  {% block declaration %}
9
- import { EventLoopProvider, StateProvider, defaultColorMode } from "$/utils/context.js";
10
- import { ThemeProvider } from 'next-themes'
9
+ import { EventLoopProvider, StateProvider, defaultColorMode } from "$/utils/context";
10
+ import { ThemeProvider } from '$/utils/react-theme';
11
+ import { Layout as AppLayout } from './_document';
12
+ import { Outlet } from 'react-router';
11
13
  {% for library_alias, library_path in window_libraries %}
12
14
  import * as {{library_alias}} from "{{library_path}}";
13
15
  {% endfor %}
@@ -26,8 +28,9 @@ function AppWrap({children}) {
26
28
  )
27
29
  }
28
30
 
29
- export default function MyApp({ Component, pageProps }) {
30
- React.useEffect(() => {
31
+
32
+ export function Layout({children}) {
33
+ React.useEffect(() => {
31
34
  // Make contexts and state objects available globally for dynamic eval'd components
32
35
  let windowImports = {
33
36
  {% for library_alias, library_path in window_libraries %}
@@ -36,17 +39,20 @@ export default function MyApp({ Component, pageProps }) {
36
39
  };
37
40
  window["__reflex"] = windowImports;
38
41
  }, []);
39
- return (
40
- jsx(ThemeProvider, {defaultTheme:defaultColorMode,attribute:"class"},
42
+
43
+ return jsx(AppLayout, {},
44
+ jsx(ThemeProvider, {defaultTheme: defaultColorMode, attribute: "class"},
41
45
  jsx(StateProvider, {},
42
- jsx(EventLoopProvider, {},
43
- jsx(AppWrap, {},
44
- jsx(Component, pageProps)
45
- )
46
+ jsx(EventLoopProvider, {},
47
+ jsx(AppWrap, {}, children)
46
48
  )
47
49
  )
48
50
  )
49
51
  );
50
52
  }
51
53
 
54
+ export default function App() {
55
+ return jsx(Outlet, {});
56
+ }
57
+
52
58
  {% endblock %}
@@ -1,7 +1,7 @@
1
1
  {% extends "web/pages/base_page.js.jinja2" %}
2
2
 
3
3
  {% block export %}
4
- export default function Document() {
4
+ export function Layout({children}) {
5
5
  return (
6
6
  {{utils.render(document)}}
7
7
  )
@@ -1,5 +1,4 @@
1
1
  {% import 'web/pages/utils.js.jinja2' as utils %}
2
- /** @jsxImportSource @emotion/react */
3
2
 
4
3
  {% block early_imports %}
5
4
  {% endblock %}
@@ -1,5 +1,6 @@
1
- import { createContext, useContext, useMemo, useReducer, useState, createElement } from "react"
2
- import { applyDelta, Event, hydrateClientStorage, useEventLoop, refs } from "$/utils/state.js"
1
+ import { createContext, useContext, useMemo, useReducer, useState, createElement, useEffect } from "react"
2
+ import { applyDelta, Event, hydrateClientStorage, useEventLoop, refs } from "$/utils/state"
3
+ import { jsx } from "@emotion/react";
3
4
 
4
5
  {% if initial_state %}
5
6
  export const initialState = {{ initial_state|json_dumps }}
@@ -77,7 +78,21 @@ export function UploadFilesProvider({ children }) {
77
78
  delete newFilesById[id]
78
79
  return newFilesById
79
80
  })
80
- return createElement(UploadFilesContext, {value:[filesById, setFilesById]}, children);
81
+ return createElement(
82
+ UploadFilesContext.Provider,
83
+ { value: [filesById, setFilesById] },
84
+ children
85
+ );
86
+ }
87
+
88
+ export function ClientSide(component) {
89
+ return ({ children, ...props }) => {
90
+ const [Component, setComponent] = useState(null);
91
+ useEffect(() => {
92
+ setComponent(component);
93
+ }, []);
94
+ return Component ? jsx(Component, props, children) : null;
95
+ };
81
96
  }
82
97
 
83
98
  export function EventLoopProvider({ children }) {
@@ -87,7 +102,11 @@ export function EventLoopProvider({ children }) {
87
102
  initialEvents,
88
103
  clientStorage,
89
104
  )
90
- return createElement(EventLoopContext, {value:[addEvents, connectErrors]}, children);
105
+ return createElement(
106
+ EventLoopContext.Provider,
107
+ { value: [addEvents, connectErrors] },
108
+ children
109
+ );
91
110
  }
92
111
 
93
112
  export function StateProvider({ children }) {
@@ -106,7 +125,7 @@ export function StateProvider({ children }) {
106
125
  {% for state_name in initial_state %}
107
126
  createElement(StateContexts.{{state_name|var_name}},{value: {{state_name|var_name}}},
108
127
  {% endfor %}
109
- createElement(DispatchContext.Provider, {value: dispatchers}, children),
110
- {% for state_name in initial_state|reverse %}){% endfor %}
128
+ createElement(DispatchContext, {value: dispatchers}, children)
129
+ {% for state_name in initial_state %}){% endfor %}
111
130
  )
112
131
  }
@@ -0,0 +1,8 @@
1
+ import { startTransition } from "react";
2
+ import { hydrateRoot } from "react-dom/client";
3
+ import { HydratedRouter } from "react-router/dom";
4
+ import { createElement } from "react";
5
+
6
+ startTransition(() => {
7
+ hydrateRoot(document, createElement(HydratedRouter));
8
+ });
@@ -0,0 +1,10 @@
1
+ import { route } from "@react-router/dev/routes";
2
+ import { flatRoutes } from "@react-router/fs-routes";
3
+
4
+ export default [
5
+ route("*", "routes/[404]_._index.js"),
6
+ route("404", "routes/[404]_._index.js", { id: "404" }),
7
+ ...(await flatRoutes({
8
+ ignoredRouteFiles: ["routes/\\[404\\]_._index.js"],
9
+ })),
10
+ ];
@@ -1,45 +1,19 @@
1
- import { useTheme } from "next-themes";
2
- import { useRef, useEffect, useState, createElement } from "react";
1
+ import { useTheme } from "$/utils/react-theme";
2
+ import { createElement } from "react";
3
3
  import {
4
4
  ColorModeContext,
5
5
  defaultColorMode,
6
6
  isDevMode,
7
7
  lastCompiledTimeStamp,
8
- } from "$/utils/context.js";
8
+ } from "$/utils/context";
9
9
 
10
10
  export default function RadixThemesColorModeProvider({ children }) {
11
11
  const { theme, resolvedTheme, setTheme } = useTheme();
12
- const [rawColorMode, setRawColorMode] = useState(defaultColorMode);
13
- const [resolvedColorMode, setResolvedColorMode] = useState(
14
- defaultColorMode === "dark" ? "dark" : "light",
15
- );
16
- const firstUpdate = useRef(true);
17
- useEffect(() => {
18
- if (firstUpdate.current) {
19
- firstUpdate.current = false;
20
- setRawColorMode(theme);
21
- setResolvedColorMode(resolvedTheme);
22
- }
23
- });
24
-
25
- useEffect(() => {
26
- if (isDevMode) {
27
- const lastCompiledTimeInLocalStorage =
28
- localStorage.getItem("last_compiled_time");
29
- if (lastCompiledTimeInLocalStorage !== lastCompiledTimeStamp) {
30
- // on app startup, make sure the application color mode is persisted correctly.
31
- setTheme(defaultColorMode);
32
- localStorage.setItem("last_compiled_time", lastCompiledTimeStamp);
33
- return;
34
- }
35
- }
36
- setRawColorMode(theme);
37
- setResolvedColorMode(resolvedTheme);
38
- }, [theme, resolvedTheme]);
39
12
 
40
13
  const toggleColorMode = () => {
41
14
  setTheme(resolvedTheme === "light" ? "dark" : "light");
42
15
  };
16
+
43
17
  const setColorMode = (mode) => {
44
18
  const allowedModes = ["light", "dark", "system"];
45
19
  if (!allowedModes.includes(mode)) {
@@ -50,10 +24,16 @@ export default function RadixThemesColorModeProvider({ children }) {
50
24
  }
51
25
  setTheme(mode);
52
26
  };
27
+
53
28
  return createElement(
54
- ColorModeContext,
29
+ ColorModeContext.Provider,
55
30
  {
56
- value: { rawColorMode, resolvedColorMode, toggleColorMode, setColorMode },
31
+ value: {
32
+ rawColorMode: theme,
33
+ resolvedColorMode: resolvedTheme,
34
+ toggleColorMode,
35
+ setColorMode,
36
+ },
57
37
  },
58
38
  children,
59
39
  );
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  plugins: {
3
3
  "postcss-import": {},
4
4
  autoprefixer: {},
@@ -0,0 +1,6 @@
1
+ export default {
2
+ future: {
3
+ unstable_optimizeDeps: true,
4
+ },
5
+ ssr: false,
6
+ };
@@ -1,41 +1,43 @@
1
1
  import { useEffect, useRef, useState } from "react";
2
- import { useRouter } from "next/router";
2
+ import { useLocation, useNavigate } from "react-router-dom";
3
3
 
4
4
  /**
5
- * React hook for use in /404 page to enable client-side routing.
5
+ * React hook for use in NotFound page to enable client-side routing.
6
6
  *
7
- * Uses the next/router to redirect to the provided URL when loading
8
- * the 404 page (for example as a fallback in static hosting situations).
7
+ * Uses React Router to redirect to the provided URL when loading
8
+ * the NotFound page (for example as a fallback in static hosting situations).
9
9
  *
10
10
  * @returns {boolean} routeNotFound - true if the current route is an actual 404
11
11
  */
12
12
  export const useClientSideRouting = () => {
13
13
  const [routeNotFound, setRouteNotFound] = useState(false);
14
14
  const didRedirect = useRef(false);
15
- const router = useRouter();
15
+ const location = useLocation();
16
+ const navigate = useNavigate();
17
+
16
18
  useEffect(() => {
17
- if (
18
- router.isReady &&
19
- !didRedirect.current // have not tried redirecting yet
20
- ) {
21
- didRedirect.current = true; // never redirect twice to avoid "Hard Navigate" error
19
+ if (!didRedirect.current) {
20
+ // have not tried redirecting yet
21
+ didRedirect.current = true; // never redirect twice to avoid navigation loops
22
+
22
23
  // attempt to redirect to the route in the browser address bar once
23
- router
24
- .replace({
25
- pathname: window.location.pathname,
26
- query: window.location.search.slice(1),
27
- })
24
+ const path = window.location.pathname;
25
+ const search = window.location.search;
26
+
27
+ // Use navigate instead of replace
28
+ navigate(path + search, { replace: true })
28
29
  .then(() => {
29
- // Check if the current route is /404
30
- if (router.pathname === "/404") {
30
+ // Check if we're still on a NotFound route
31
+ // Note: This depends on how your routes are set up
32
+ if (location.pathname === path) {
31
33
  setRouteNotFound(true); // Mark as an actual 404
32
34
  }
33
35
  })
34
- .catch((e) => {
36
+ .catch(() => {
35
37
  setRouteNotFound(true); // navigation failed, so this is a real 404
36
38
  });
37
39
  }
38
- }, [router.isReady]);
40
+ }, [location, navigate]);
39
41
 
40
42
  // Return the reactive bool, to avoid flashing 404 page until we know for sure
41
43
  // the route is not found.
@@ -0,0 +1,89 @@
1
+ import {
2
+ createContext,
3
+ useContext,
4
+ useState,
5
+ useEffect,
6
+ createElement,
7
+ useRef,
8
+ useMemo,
9
+ } from "react";
10
+
11
+ import { isDevMode, lastCompiledTimeStamp } from "$/utils/context";
12
+
13
+ const ThemeContext = createContext();
14
+
15
+ export function ThemeProvider({ children, defaultTheme = "system" }) {
16
+ const [theme, setTheme] = useState(defaultTheme);
17
+ const [systemTheme, setSystemTheme] = useState(
18
+ defaultTheme !== "system" ? defaultTheme : "light",
19
+ );
20
+
21
+ const firstRender = useRef(true);
22
+
23
+ useEffect(() => {
24
+ if (!firstRender.current) {
25
+ return;
26
+ }
27
+
28
+ firstRender.current = false;
29
+
30
+ if (isDevMode) {
31
+ const lastCompiledTimeInLocalStorage =
32
+ localStorage.getItem("last_compiled_time");
33
+ if (lastCompiledTimeInLocalStorage !== lastCompiledTimeStamp) {
34
+ // on app startup, make sure the application color mode is persisted correctly.
35
+ localStorage.setItem("last_compiled_time", lastCompiledTimeStamp);
36
+ return;
37
+ }
38
+ }
39
+
40
+ // Load saved theme from localStorage
41
+ const savedTheme = localStorage.getItem("theme") || defaultTheme;
42
+ setTheme(savedTheme);
43
+ });
44
+
45
+ const resolvedTheme = useMemo(
46
+ () => (theme === "system" ? systemTheme : theme),
47
+ [theme, systemTheme],
48
+ );
49
+
50
+ useEffect(() => {
51
+ // Set up media query for system preference detection
52
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
53
+
54
+ // Listen for system preference changes
55
+ const handleChange = () => {
56
+ setSystemTheme(mediaQuery.matches ? "dark" : "light");
57
+ };
58
+
59
+ handleChange();
60
+
61
+ mediaQuery.addEventListener("change", handleChange);
62
+
63
+ return () => {
64
+ mediaQuery.removeEventListener("change", handleChange);
65
+ };
66
+ });
67
+
68
+ // Save theme to localStorage whenever it changes
69
+ useEffect(() => {
70
+ localStorage.setItem("theme", theme);
71
+ }, [theme]);
72
+
73
+ useEffect(() => {
74
+ const root = window.document.documentElement;
75
+ root.classList.remove("light", "dark");
76
+ root.classList.add(resolvedTheme);
77
+ root.style.colorScheme = resolvedTheme;
78
+ }, [resolvedTheme]);
79
+
80
+ return createElement(
81
+ ThemeContext.Provider,
82
+ { value: { theme, resolvedTheme, setTheme } },
83
+ children,
84
+ );
85
+ }
86
+
87
+ export function useTheme() {
88
+ return useContext(ThemeContext);
89
+ }