import decamelize from 'decamelize' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { Setting } from 'lib/settings/initialSettings' import { SetEventTypes, SettingsLevel } from 'lib/settings/settingsTypes' import { shouldHideSetting, shouldShowSettingInput, } from 'lib/settings/settingsUtils' import { Fragment } from 'react/jsx-runtime' import { SettingsSection } from './SettingsSection' import { useLocation, useNavigate } from 'react-router-dom' import { isDesktop } from 'lib/isDesktop' import { ActionButton } from 'components/ActionButton' import { SettingsFieldInput } from './SettingsFieldInput' import toast from 'react-hot-toast' import { APP_VERSION, IS_NIGHTLY, getReleaseUrl } from 'routes/Settings' import { PATHS } from 'lib/paths' import { createAndOpenNewTutorialProject, getSettingsFolderPaths, } from 'lib/desktopFS' import { useDotDotSlash } from 'hooks/useDotDotSlash' import { ForwardedRef, forwardRef, useEffect } from 'react' import { useLspContext } from 'components/LspProvider' import { toSync } from 'lib/utils' import { reportRejection } from 'lib/trap' import { openExternalBrowserIfDesktop } from 'lib/openWindow' interface AllSettingsFieldsProps { searchParamTab: SettingsLevel isFileSettings: boolean } export const AllSettingsFields = forwardRef( ( { searchParamTab, isFileSettings }: AllSettingsFieldsProps, scrollRef: ForwardedRef ) => { const location = useLocation() const navigate = useNavigate() const { onProjectOpen } = useLspContext() const dotDotSlash = useDotDotSlash() const { settings: { send, context, state }, } = useSettingsAuthContext() const projectPath = isFileSettings && isDesktop() ? decodeURI( location.pathname .replace(PATHS.FILE + '/', '') .replace(PATHS.SETTINGS, '') .slice( 0, decodeURI(location.pathname).lastIndexOf( window.electron.path.sep ) ) ) : undefined function restartOnboarding() { send({ type: `set.app.onboardingStatus`, data: { level: 'user', value: '' }, }) } /** * A "listener" for the XState to return to "idle" state * when the user resets the onboarding, using the callback above */ useEffect(() => { async function navigateToOnboardingStart() { if ( state.context.app.onboardingStatus.user === '' && state.matches('idle') ) { if (isFileSettings) { // If we're in a project, first navigate to the onboarding start here // so we can trigger the warning screen if necessary navigate(dotDotSlash(1) + PATHS.ONBOARDING.INDEX) } else { // If we're in the global settings, create a new project and navigate // to the onboarding start in that project await createAndOpenNewTutorialProject({ onProjectOpen, navigate }) } } } // eslint-disable-next-line @typescript-eslint/no-floating-promises navigateToOnboardingStart() }, [isFileSettings, navigate, state]) 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 ( send({ type: `set.${category}.${settingName}`, data: { level: searchParamTab, value: parentValue !== undefined ? parentValue : setting.getFallback(searchParamTab), }, } as SetEventTypes) } > ) })}
))}

Resets

Replay Onboarding
{isDesktop() && ( { const paths = await getSettingsFolderPaths( projectPath ? decodeURIComponent(projectPath) : undefined ) 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 )} { 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 Modeling App

{/* 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

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 Modeling App (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 🤖.

)}
) } )