feat(workspace): gate Echo Studio scaffold behind off-by-default Labs toggle

Echo Studio (integrated from #457 in cycle 2b) was always-visible in the
chat sidebar and mobile hamburger nav. It's a scaffold/prototype, so surface
it only when opted in.

- add experimentalEchoStudio: false to StudioSettings + defaults
- new Settings > Labs (experimental) section with a Switch
- chat-sidebar + mobile-hamburger filter the Echo Studio nav item by the flag
This commit is contained in:
Aurora
2026-06-05 17:25:10 -04:00
parent 9c31f52623
commit 8eec98f257
4 changed files with 44 additions and 8 deletions

View File

@@ -26,6 +26,7 @@ import {
selectChatProfileDisplayName,
useChatSettingsStore,
} from '@/hooks/use-chat-settings'
import { useSettingsStore } from '@/hooks/use-settings'
export const MOBILE_HAMBURGER_NAV_ITEMS = [
{
@@ -166,6 +167,12 @@ export function MobileHamburgerMenu() {
const navigate = useNavigate()
const pathname = useRouterState({ select: (s) => s.location.pathname })
const profileDisplayName = useChatSettingsStore(selectChatProfileDisplayName)
const echoStudioEnabled = useSettingsStore(
(state) => state.settings.experimentalEchoStudio,
)
const visibleNavItems = MOBILE_HAMBURGER_NAV_ITEMS.filter(
(item) => item.id !== 'echo-studio' || echoStudioEnabled,
)
const isChatRoute =
pathname.startsWith('/chat') || pathname === '/new' || pathname === '/'
@@ -253,7 +260,7 @@ export function MobileHamburgerMenu() {
{/* Nav items */}
<nav className="flex flex-col gap-1 px-3 pt-4 flex-1">
{MOBILE_HAMBURGER_NAV_ITEMS.map((item) => {
{visibleNavItems.map((item) => {
const isActive = item.match(pathname)
return (
<button

View File

@@ -28,6 +28,8 @@ export type StudioSettings = {
interfaceDensity: InterfaceDensity
/** Mobile chat nav mode: 'dock' = iMessage (no nav in chat), 'integrated' = chat input in nav pill, 'scroll-hide' = nav shows on scroll up */
mobileChatNavMode: 'dock' | 'integrated' | 'scroll-hide'
/** Hidden experimental: show Echo Studio (dashboard builder scaffold) in nav. Off by default. */
experimentalEchoStudio: boolean
}
type SettingsState = {
@@ -54,6 +56,7 @@ export const defaultStudioSettings: StudioSettings = {
interfaceFont: 'system',
interfaceDensity: 'comfortable',
mobileChatNavMode: 'dock',
experimentalEchoStudio: false,
}
export const useSettingsStore = create<SettingsState>()(

View File

@@ -427,6 +427,25 @@ function SettingsRoute() {
</div>
</div>
</SettingsSection>
<SettingsSection
title="Labs (experimental)"
description="Early/unfinished features. May change or be removed. Off by default."
icon={Settings02Icon}
>
<SettingsRow
label="Echo Studio"
description="Show the Echo Studio dashboard builder (scaffold) in the nav. Experimental."
>
<Switch
checked={settings.experimentalEchoStudio}
onCheckedChange={(checked) =>
updateSettings({ experimentalEchoStudio: checked })
}
aria-label="Enable Echo Studio (experimental)"
/>
</SettingsRow>
</SettingsSection>
</>
)}

View File

@@ -587,6 +587,9 @@ function ChatSidebarComponent({
const isConductorActive = pathname === '/conductor'
const isOperationsActive = pathname === '/operations'
const isSwarmActive = pathname === '/swarm' || pathname === '/swarm2'
const echoStudioEnabled = useSettingsStore(
(state) => state.settings.experimentalEchoStudio,
)
const mainRoutes = ['/chat', '/new', '/files', '/terminal']
const knowledgeRoutes = ['/memory', '/skills']
const systemRoutes = ['/settings', '/logs']
@@ -845,13 +848,17 @@ function ChatSidebarComponent({
label: 'Swarm',
active: isSwarmActive,
},
{
kind: 'link',
to: '/echo-studio',
icon: DashboardSquare01Icon,
label: 'Echo Studio',
active: pathname.startsWith('/echo-studio'),
},
...(echoStudioEnabled
? [
{
kind: 'link' as const,
to: '/echo-studio',
icon: DashboardSquare01Icon,
label: 'Echo Studio',
active: pathname.startsWith('/echo-studio'),
},
]
: []),
]