diff --git a/app/gui/src/dashboard/App.tsx b/app/gui/src/dashboard/App.tsx
index 7b43c605ff9..0cd3820ce00 100644
--- a/app/gui/src/dashboard/App.tsx
+++ b/app/gui/src/dashboard/App.tsx
@@ -519,13 +519,11 @@ function AppRouter(props: AppRouterProps) {
{routes}
- {detect.IS_DEV_MODE && (
-
-
-
-
-
- )}
+
+
+
+
+
diff --git a/app/gui/src/dashboard/components/Devtools/EnsoDevtools.tsx b/app/gui/src/dashboard/components/Devtools/EnsoDevtools.tsx
index fbe1a7f1e65..93148def8d5 100644
--- a/app/gui/src/dashboard/components/Devtools/EnsoDevtools.tsx
+++ b/app/gui/src/dashboard/components/Devtools/EnsoDevtools.tsx
@@ -24,6 +24,7 @@ import {
useEnableVersionChecker,
usePaywallDevtools,
useSetEnableVersionChecker,
+ useShowDevtools,
} from './EnsoDevtoolsProvider'
import * as ariaComponents from '#/components/AriaComponents'
@@ -54,6 +55,9 @@ export function EnsoDevtools() {
const { authQueryKey, session } = authProvider.useAuth()
const queryClient = reactQuery.useQueryClient()
const { getFeature } = billing.usePaywallFeatures()
+
+ const showDevtools = useShowDevtools()
+
const { features, setFeature } = usePaywallDevtools()
const enableVersionChecker = useEnableVersionChecker()
const setEnableVersionChecker = useSetEnableVersionChecker()
@@ -66,6 +70,10 @@ export function EnsoDevtools() {
const featureFlags = useFeatureFlags()
const setFeatureFlags = useSetFeatureFlags()
+ if (!showDevtools) {
+ return null
+ }
+
return (
diff --git a/app/gui/src/dashboard/components/Devtools/EnsoDevtoolsProvider.tsx b/app/gui/src/dashboard/components/Devtools/EnsoDevtoolsProvider.tsx
index 213db13bb8e..4aa70641030 100644
--- a/app/gui/src/dashboard/components/Devtools/EnsoDevtoolsProvider.tsx
+++ b/app/gui/src/dashboard/components/Devtools/EnsoDevtoolsProvider.tsx
@@ -3,6 +3,8 @@
* This file provides a zustand store that contains the state of the Enso devtools.
*/
import type { PaywallFeatureName } from '#/hooks/billing'
+import { IS_DEV_MODE } from 'enso-common/src/detect'
+import * as React from 'react'
import * as zustand from 'zustand'
/** Configuration for a paywall feature. */
@@ -16,13 +18,23 @@ export interface PaywallDevtoolsFeatureConfiguration {
/** The state of this zustand store. */
interface EnsoDevtoolsStore {
+ readonly showDevtools: boolean
+ readonly setShowDevtools: (showDevtools: boolean) => void
+ readonly toggleDevtools: () => void
readonly showVersionChecker: boolean | null
readonly paywallFeatures: Record
readonly setPaywallFeature: (feature: PaywallFeatureName, isForceEnabled: boolean | null) => void
readonly setEnableVersionChecker: (showVersionChecker: boolean | null) => void
}
-const ensoDevtoolsStore = zustand.createStore((set) => ({
+export const ensoDevtoolsStore = zustand.createStore((set) => ({
+ showDevtools: IS_DEV_MODE,
+ setShowDevtools: (showDevtools) => {
+ set({ showDevtools })
+ },
+ toggleDevtools: () => {
+ set(({ showDevtools }) => ({ showDevtools: !showDevtools }))
+ },
showVersionChecker: false,
paywallFeatures: {
share: { isForceEnabled: null },
@@ -67,3 +79,23 @@ export function usePaywallDevtools() {
setFeature: state.setPaywallFeature,
}))
}
+
+/** A hook that provides access to the show devtools state. */
+export function useShowDevtools() {
+ return zustand.useStore(ensoDevtoolsStore, (state) => state.showDevtools)
+}
+
+// =================================
+// === DevtoolsProvider ===
+// =================================
+
+/**
+ * Provide the Enso devtools to the app.
+ */
+export function DevtoolsProvider(props: { children: React.ReactNode }) {
+ React.useEffect(() => {
+ window.toggleDevtools = ensoDevtoolsStore.getState().toggleDevtools
+ }, [])
+
+ return <>{props.children}>
+}
diff --git a/app/gui/src/dashboard/components/Devtools/ReactQueryDevtools.tsx b/app/gui/src/dashboard/components/Devtools/ReactQueryDevtools.tsx
index e786fec979a..6ddb0664d51 100644
--- a/app/gui/src/dashboard/components/Devtools/ReactQueryDevtools.tsx
+++ b/app/gui/src/dashboard/components/Devtools/ReactQueryDevtools.tsx
@@ -4,6 +4,7 @@ import * as React from 'react'
import * as reactQuery from '@tanstack/react-query'
import * as reactQueryDevtools from '@tanstack/react-query-devtools'
import * as errorBoundary from 'react-error-boundary'
+import { useShowDevtools } from './EnsoDevtoolsProvider'
const ReactQueryDevtoolsProduction = React.lazy(() =>
import('@tanstack/react-query-devtools/build/modern/production.js').then((d) => ({
@@ -13,19 +14,13 @@ const ReactQueryDevtoolsProduction = React.lazy(() =>
/** Show the React Query Devtools and provide the ability to show them in production. */
export function ReactQueryDevtools() {
- const [showDevtools, setShowDevtools] = React.useState(false)
+ const showDevtools = useShowDevtools()
// It is safer to pass the client directly to the devtools
// since there might be a chance that we have multiple versions of `react-query`,
// in case we forget to update the devtools, npm messes up the versions,
// or there are hoisting issues.
const client = reactQuery.useQueryClient()
- React.useEffect(() => {
- window.toggleDevtools = () => {
- setShowDevtools((old) => !old)
- }
- }, [])
-
return (
{
diff --git a/app/gui/src/dashboard/index.tsx b/app/gui/src/dashboard/index.tsx
index 943408afe52..3dfdcb99318 100644
--- a/app/gui/src/dashboard/index.tsx
+++ b/app/gui/src/dashboard/index.tsx
@@ -21,7 +21,7 @@ import LoggerProvider, { type Logger } from '#/providers/LoggerProvider'
import LoadingScreen from '#/pages/authentication/LoadingScreen'
-import { ReactQueryDevtools } from '#/components/Devtools'
+import { DevtoolsProvider, ReactQueryDevtools } from '#/components/Devtools'
import { ErrorBoundary } from '#/components/ErrorBoundary'
import { OfflineNotificationManager } from '#/components/OfflineNotificationManager'
import { Suspense } from '#/components/Suspense'
@@ -113,21 +113,23 @@ export function run(props: DashboardProps) {
reactDOM.createRoot(root).render(
-
- }>
-
-
-
-
-
-
-
-
-
-
-
+
+
+ }>
+
+
+
+
+
+
+
+
+
+
+
-
+
+
,
)