diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx
index 397c4ed4e62..f753391ed28 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx
+++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx
@@ -13,7 +13,6 @@ import { SearchBarTrigger } from "./components/SearchBarTrigger";
import { SidebarToggle } from "./components/SidebarToggle";
import { V2WorkspaceOpenInButton } from "./components/V2WorkspaceOpenInButton";
import { V2WorkspaceSearchBarTrigger } from "./components/V2WorkspaceSearchBarTrigger";
-import { VersionToggle } from "./components/VersionToggle";
import { WindowControls } from "./components/WindowControls";
export function TopBar() {
@@ -31,7 +30,7 @@ export function TopBar() {
{ enabled: !!workspaceId && !isV2WorkspaceRoute },
);
const isOnline = useOnlineStatus();
- const { isV2CloudEnabled, isRemoteV2Enabled } = useIsV2CloudEnabled();
+ const { isV2CloudEnabled } = useIsV2CloudEnabled();
// Default to Mac layout while loading to avoid overlap with traffic lights
const isMac = platform === undefined || platform === "darwin";
@@ -46,7 +45,6 @@ export function TopBar() {
- {isRemoteV2Enabled && }
{isV2WorkspaceRoute ? (
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/VersionToggle/VersionToggle.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/VersionToggle/VersionToggle.tsx
deleted file mode 100644
index 33462108871..00000000000
--- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/VersionToggle/VersionToggle.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
-import { cn } from "@superset/ui/utils";
-import { useV2LocalOverrideStore } from "renderer/stores/v2-local-override";
-
-export function VersionToggle() {
- const { forceV1, toggle } = useV2LocalOverrideStore();
- const activeVersion = forceV1 ? "v1" : "v2";
-
- return (
-
-
-
-
-
- {forceV1
- ? "Early Access: Switch to Superset V2"
- : "Switch to Superset V1"}
-
-
- );
-}
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/VersionToggle/index.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/VersionToggle/index.ts
deleted file mode 100644
index 3e06c734ddd..00000000000
--- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/VersionToggle/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { VersionToggle } from "./VersionToggle";
diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/components/SettingsSidebar/GeneralSettings.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/components/SettingsSidebar/GeneralSettings.tsx
index 0905de1ef99..74378ce1a31 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/settings/components/SettingsSidebar/GeneralSettings.tsx
+++ b/apps/desktop/src/renderer/routes/_authenticated/settings/components/SettingsSidebar/GeneralSettings.tsx
@@ -1,6 +1,7 @@
import { cn } from "@superset/ui/utils";
import { Link, useMatchRoute } from "@tanstack/react-router";
import {
+ HiOutlineBeaker,
HiOutlineBell,
HiOutlineBuildingOffice2,
HiOutlineCommandLine,
@@ -35,6 +36,7 @@ type SettingsRoute =
| "/settings/terminal"
| "/settings/links"
| "/settings/models"
+ | "/settings/experimental"
| "/settings/integrations"
| "/settings/billing"
| "/settings/api-keys"
@@ -170,6 +172,12 @@ const SECTION_GROUPS: SectionGroup[] = [
icon: ,
macOnly: true,
},
+ {
+ id: "/settings/experimental",
+ section: "experimental",
+ label: "Experimental",
+ icon: ,
+ },
],
},
];
diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/components/ExperimentalSettings/ExperimentalSettings.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/components/ExperimentalSettings/ExperimentalSettings.tsx
new file mode 100644
index 00000000000..90c4da1646d
--- /dev/null
+++ b/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/components/ExperimentalSettings/ExperimentalSettings.tsx
@@ -0,0 +1,61 @@
+import { Label } from "@superset/ui/label";
+import { Switch } from "@superset/ui/switch";
+import { useIsV2CloudEnabled } from "renderer/hooks/useIsV2CloudEnabled";
+import { useV2LocalOverrideStore } from "renderer/stores/v2-local-override";
+import {
+ isItemVisible,
+ SETTING_ITEM_ID,
+ type SettingItemId,
+} from "../../../utils/settings-search";
+
+interface ExperimentalSettingsProps {
+ visibleItems?: SettingItemId[] | null;
+}
+
+export function ExperimentalSettings({
+ visibleItems,
+}: ExperimentalSettingsProps) {
+ const showSupersetV2 = isItemVisible(
+ SETTING_ITEM_ID.EXPERIMENTAL_SUPERSET_V2,
+ visibleItems,
+ );
+ const { isV2CloudEnabled, isRemoteV2Enabled } = useIsV2CloudEnabled();
+ const setForceV1 = useV2LocalOverrideStore((state) => state.setForceV1);
+
+ return (
+
+
+
Experimental
+
+ Try early access features and previews
+
+
+
+
+ {showSupersetV2 && (
+
+
+
+
+ Use the new workspace experience when early access is available
+
+ {!isRemoteV2Enabled && (
+
+ Early access is not enabled for this account.
+
+ )}
+
+
setForceV1(!enabled)}
+ disabled={!isRemoteV2Enabled}
+ />
+
+ )}
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/components/ExperimentalSettings/index.ts b/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/components/ExperimentalSettings/index.ts
new file mode 100644
index 00000000000..7f3499d9e21
--- /dev/null
+++ b/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/components/ExperimentalSettings/index.ts
@@ -0,0 +1 @@
+export { ExperimentalSettings } from "./ExperimentalSettings";
diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/page.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/page.tsx
new file mode 100644
index 00000000000..f03700a391b
--- /dev/null
+++ b/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/page.tsx
@@ -0,0 +1,22 @@
+import { createFileRoute } from "@tanstack/react-router";
+import { useMemo } from "react";
+import { useSettingsSearchQuery } from "renderer/stores/settings-state";
+import { getMatchingItemsForSection } from "../utils/settings-search";
+import { ExperimentalSettings } from "./components/ExperimentalSettings";
+
+export const Route = createFileRoute("/_authenticated/settings/experimental/")({
+ component: ExperimentalSettingsPage,
+});
+
+function ExperimentalSettingsPage() {
+ const searchQuery = useSettingsSearchQuery();
+
+ const visibleItems = useMemo(() => {
+ if (!searchQuery) return null;
+ return getMatchingItemsForSection(searchQuery, "experimental").map(
+ (item) => item.id,
+ );
+ }, [searchQuery]);
+
+ return ;
+}
diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/layout.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/layout.tsx
index 5b1597aad31..2bff0a9e57c 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/settings/layout.tsx
+++ b/apps/desktop/src/renderer/routes/_authenticated/settings/layout.tsx
@@ -39,6 +39,7 @@ const SECTION_ORDER: SettingsSection[] = [
"billing",
"apikeys",
"permissions",
+ "experimental",
];
function getSectionFromPath(pathname: string): SettingsSection | null {
@@ -52,6 +53,7 @@ function getSectionFromPath(pathname: string): SettingsSection | null {
if (pathname.includes("/settings/terminal")) return "terminal";
if (pathname.includes("/settings/links")) return "links";
if (pathname.includes("/settings/models")) return "models";
+ if (pathname.includes("/settings/experimental")) return "experimental";
if (pathname.includes("/settings/integrations")) return "integrations";
if (pathname.includes("/settings/permissions")) return "permissions";
if (pathname.includes("/settings/project")) return "project";
@@ -80,6 +82,8 @@ function getPathFromSection(section: SettingsSection): string {
return "/settings/links";
case "models":
return "/settings/models";
+ case "experimental":
+ return "/settings/experimental";
case "integrations":
return "/settings/integrations";
case "permissions":
diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts b/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts
index eaf25734214..a332c1ad67c 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts
+++ b/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts
@@ -46,6 +46,8 @@ export const SETTING_ITEM_ID = {
MODELS_ANTHROPIC: "models-anthropic",
MODELS_OPENAI: "models-openai",
+ EXPERIMENTAL_SUPERSET_V2: "experimental-superset-v2",
+
INTEGRATIONS_LINEAR: "integrations-linear",
INTEGRATIONS_GITHUB: "integrations-github",
INTEGRATIONS_SLACK: "integrations-slack",
@@ -704,6 +706,26 @@ export const SETTINGS_ITEMS: SettingsItem[] = [
"auto name",
],
},
+ {
+ id: SETTING_ITEM_ID.EXPERIMENTAL_SUPERSET_V2,
+ section: "experimental",
+ title: "Try Superset Version 2 (Early Access)",
+ description: "Switch between Superset V1 and the new V2 experience",
+ keywords: [
+ "experimental",
+ "experiments",
+ "v2",
+ "v1",
+ "version",
+ "early access",
+ "beta",
+ "preview",
+ "workspace",
+ "workspaces",
+ "toggle",
+ "switch",
+ ],
+ },
{
id: SETTING_ITEM_ID.INTEGRATIONS_LINEAR,
section: "integrations",
diff --git a/apps/desktop/src/renderer/stores/settings-state.ts b/apps/desktop/src/renderer/stores/settings-state.ts
index 98d18fe2599..e3a744e3151 100644
--- a/apps/desktop/src/renderer/stores/settings-state.ts
+++ b/apps/desktop/src/renderer/stores/settings-state.ts
@@ -13,6 +13,7 @@ export type SettingsSection =
| "terminal"
| "links"
| "models"
+ | "experimental"
| "integrations"
| "billing"
| "apikeys"
diff --git a/apps/desktop/src/renderer/stores/v2-local-override.ts b/apps/desktop/src/renderer/stores/v2-local-override.ts
index 01b66fb9885..c815a605aa2 100644
--- a/apps/desktop/src/renderer/stores/v2-local-override.ts
+++ b/apps/desktop/src/renderer/stores/v2-local-override.ts
@@ -4,15 +4,15 @@ import { devtools, persist } from "zustand/middleware";
interface V2LocalOverrideState {
/** When true, forces v1 mode locally even though v2 is enabled remotely. */
forceV1: boolean;
- toggle: () => void;
+ setForceV1: (forceV1: boolean) => void;
}
export const useV2LocalOverrideStore = create()(
devtools(
persist(
- (set, get) => ({
+ (set) => ({
forceV1: false,
- toggle: () => set({ forceV1: !get().forceV1 }),
+ setForceV1: (forceV1) => set({ forceV1 }),
}),
{ name: "v2-local-override" },
),