import decamelize from 'decamelize' import type { ForwardedRef } from 'react' import { forwardRef, useMemo } from 'react' import toast from 'react-hot-toast' import { useLocation, useNavigate } from 'react-router-dom' import { Fragment } from 'react/jsx-runtime' import { ActionButton } from '@src/components/ActionButton' import { SettingsFieldInput } from '@src/components/Settings/SettingsFieldInput' import { SettingsSection } from '@src/components/Settings/SettingsSection' import { getSettingsFolderPaths } from '@src/lib/desktopFS' import { isDesktop } from '@src/lib/isDesktop' import { openExternalBrowserIfDesktop } from '@src/lib/openWindow' import { ONBOARDING_SUBPATHS } from '@src/lib/onboardingPaths' import { PATHS } from '@src/lib/paths' import type { Setting } from '@src/lib/settings/initialSettings' import type { SetEventTypes, SettingsLevel, } from '@src/lib/settings/settingsTypes' import { shouldHideSetting, shouldShowSettingInput, } from '@src/lib/settings/settingsUtils' import { reportRejection } from '@src/lib/trap' import { toSync } from '@src/lib/utils' import { codeManager, kclManager, settingsActor, useSettings, } from '@src/lib/singletons' import { APP_VERSION, IS_NIGHTLY, getReleaseUrl } from '@src/routes/utils' import { acceptOnboarding, catchOnboardingWarnError, } from '@src/routes/Onboarding/utils' interface AllSettingsFieldsProps { searchParamTab: SettingsLevel isFileSettings: boolean } export const AllSettingsFields = forwardRef( ( { searchParamTab, isFileSettings }: AllSettingsFieldsProps, scrollRef: ForwardedRef ) => { const location = useLocation() const navigate = useNavigate() const context = useSettings() const projectPath = useMemo(() => { const filteredPathname = location.pathname .replace(PATHS.FILE, '') .replace(PATHS.SETTINGS, '') const lastSlashIndex = filteredPathname.lastIndexOf( // This is slicing off any remaining browser path segments, // so we don't use window.electron.sep here '/' ) const projectPath = isFileSettings && isDesktop() ? decodeURIComponent(filteredPathname.slice(lastSlashIndex + 1)) : undefined return projectPath }, [location.pathname, isFileSettings]) async function restartOnboarding() { const props = { onboardingStatus: ONBOARDING_SUBPATHS.INDEX, navigate, codeManager, kclManager, } acceptOnboarding(props).catch((reason) => catchOnboardingWarnError(reason, props) ) } return (
{Object.entries(context) .filter(([_, categorySettings]) => // Filter out categories that don't have any non-hidden settings Object.values(categorySettings).some( (setting) => !shouldHideSetting(setting, searchParamTab) ) ) .map(([category, categorySettings]) => (

{decamelize(category, { separator: ' ' })}

{Object.entries(categorySettings) .filter( // Filter out settings that don't have a Component or inputType // or are hidden on the current level or the current platform (item: [string, Setting]) => shouldShowSettingInput(item[1], searchParamTab) ) .map(([settingName, s]) => { const setting = s as Setting const parentValue = setting[setting.getParentLevel(searchParamTab)] return ( settingsActor.send({ type: `set.${category}.${settingName}`, data: { level: searchParamTab, value: parentValue !== undefined ? parentValue : setting.getFallback(searchParamTab), }, } as SetEventTypes) } > ) })}
))}

Resets

{ restartOnboarding().catch(reportRejection) }} iconStart={{ icon: 'refresh', size: 'sm', className: 'p-1', }} > Replay Onboarding
{isDesktop() && ( { const paths = await getSettingsFolderPaths(projectPath) const finalPath = paths[searchParamTab] if (!finalPath) { return new Error('finalPath undefined') } window.electron.showInFolder(finalPath) }, reportRejection)} iconStart={{ icon: 'folder', size: 'sm', className: 'p-1', }} > Show in folder )} { settingsActor.send({ type: 'Reset settings', level: searchParamTab, }) toast.success( `Your ${searchParamTab}-level settings were reset` ) }} iconStart={{ icon: 'refresh', size: 'sm', className: 'p-1 text-chalkboard-10', bgClassName: 'bg-destroy-70', }} > Reset {searchParamTab}-level settings

About Design Studio

{/* This uses a Vite plugin, set in vite.config.ts to inject the version from package.json */} App version {APP_VERSION}.{' '}

View release on GitHub { window.electron.appCheckForUpdates().catch(reportRejection) }} iconStart={{ icon: 'refresh', size: 'sm', className: 'p-1', }} > Check for updates

Don't see the feature you want? Check to see if it's on{' '} our roadmap , and start a discussion if you don't see it! Your feedback will help us prioritize what to build next.

{!IS_NIGHTLY && (

Want to experience the latest and (hopefully) greatest from our main development branch?{' '} Click here to grab Zoo Design Studio (Nightly) . It can be installed side-by-side with the stable version you're running now. But careful there, a lot less testing is involved in their release 🤖.

)}
) } )