diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index eb55e8549..7a4b4f5ae 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2626,6 +2626,7 @@ dependencies = [ "tower-lsp", "ts-rs", "url", + "urlencoding", "uuid", "validator", "wasm-bindgen", @@ -6258,6 +6259,12 @@ dependencies = [ "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "urlpattern" version = "0.2.0" diff --git a/src/components/HelpMenu.tsx b/src/components/HelpMenu.tsx index 3de0f1eb0..ebd780dbc 100644 --- a/src/components/HelpMenu.tsx +++ b/src/components/HelpMenu.tsx @@ -6,6 +6,7 @@ import { useLocation, useNavigate } from 'react-router-dom' import { createAndOpenNewProject } from 'lib/tauriFS' import { paths } from 'lib/paths' import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath' +import { useLspContext } from './LspProvider' const HelpMenuDivider = () => (
@@ -13,6 +14,7 @@ const HelpMenuDivider = () => ( export function HelpMenu(props: React.PropsWithChildren) { const location = useLocation() + const { onProjectOpen } = useLspContext() const filePath = useAbsoluteFilePath() const isInProject = location.pathname.includes(paths.FILE) const navigate = useNavigate() @@ -106,9 +108,9 @@ export function HelpMenu(props: React.PropsWithChildren) { }, }) if (isInProject) { - navigate('onboarding') + navigate(filePath + paths.ONBOARDING.INDEX) } else { - createAndOpenNewProject(navigate) + createAndOpenNewProject({ onProjectOpen, navigate }) } }} > diff --git a/src/components/ModelingSidebar/ModelingPanes/index.ts b/src/components/ModelingSidebar/ModelingPanes/index.ts index 878c4a9e6..8c3d27cc1 100644 --- a/src/components/ModelingSidebar/ModelingPanes/index.ts +++ b/src/components/ModelingSidebar/ModelingPanes/index.ts @@ -3,7 +3,6 @@ import { faBugSlash, faCode, faCodeCommit, - faExclamationCircle, faSquareRootVariable, } from '@fortawesome/free-solid-svg-icons' import { KclEditorMenu } from 'components/ModelingSidebar/ModelingPanes/KclEditorMenu' diff --git a/src/components/Settings/AllSettingsFields.tsx b/src/components/Settings/AllSettingsFields.tsx index b855a2dbb..4eaf7d04e 100644 --- a/src/components/Settings/AllSettingsFields.tsx +++ b/src/components/Settings/AllSettingsFields.tsx @@ -19,7 +19,8 @@ import { createAndOpenNewProject, getSettingsFolderPaths } from 'lib/tauriFS' import { paths } from 'lib/paths' import { useDotDotSlash } from 'hooks/useDotDotSlash' import { sep } from '@tauri-apps/api/path' -import { ForwardedRef, forwardRef } from 'react' +import { ForwardedRef, forwardRef, useEffect } from 'react' +import { useLspContext } from 'components/LspProvider' interface AllSettingsFieldsProps { searchParamTab: SettingsLevel @@ -33,9 +34,10 @@ export const AllSettingsFields = forwardRef( ) => { const location = useLocation() const navigate = useNavigate() + const { onProjectOpen } = useLspContext() const dotDotSlash = useDotDotSlash() const { - settings: { send, context }, + settings: { send, context, state }, } = useSettingsAuthContext() const projectPath = @@ -48,19 +50,37 @@ export const AllSettingsFields = forwardRef( ) : undefined - function restartOnboarding() { + async function restartOnboarding() { send({ type: `set.app.onboardingStatus`, data: { level: 'user', value: '' }, }) - - if (isFileSettings) { - navigate(dotDotSlash(1) + paths.ONBOARDING.INDEX) - } else { - createAndOpenNewProject(navigate) - } } + /** + * 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 createAndOpenNewProject({ onProjectOpen, navigate }) + } + } + } + navigateToOnboardingStart() + }, [isFileSettings, navigate, state]) + return (
diff --git a/src/lib/tauriFS.ts b/src/lib/tauriFS.ts index cc79e2517..c9cf838f2 100644 --- a/src/lib/tauriFS.ts +++ b/src/lib/tauriFS.ts @@ -14,6 +14,7 @@ import { listProjects, readAppSettingsFile, } from './tauri' +import { engineCommandManager } from './singletons' export const isHidden = (fileOrDir: FileEntry) => !!fileOrDir.name?.startsWith('.') @@ -116,9 +117,23 @@ export async function getSettingsFolderPaths(projectPath?: string) { } } -export async function createAndOpenNewProject( +export async function createAndOpenNewProject({ + onProjectOpen, + navigate, +}: { + onProjectOpen: ( + project: { + name: string | null + path: string | null + } | null, + file: FileEntry | null + ) => void navigate: (path: string) => void -) { +}) { + // Clear the scene and end the session. + engineCommandManager.endSession() + + // Create a new project with the onboarding project name const configuration = await readAppSettingsFile() const projects = await listProjects(configuration) const nextIndex = getNextProjectIndex(ONBOARDING_PROJECT_NAME, projects) @@ -126,6 +141,24 @@ export async function createAndOpenNewProject( ONBOARDING_PROJECT_NAME, nextIndex ) - const newFile = await createNewProjectDirectory(name, bracket, configuration) - navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`) + const newProject = await createNewProjectDirectory( + name, + bracket, + configuration + ) + + // Prep the LSP and navigate to the onboarding start + onProjectOpen( + { + name: newProject.name, + path: newProject.path, + }, + null + ) + navigate( + `${paths.FILE}/${encodeURIComponent(newProject.default_file)}${ + paths.ONBOARDING.INDEX + }` + ) + return newProject } diff --git a/src/routes/Onboarding/Introduction.tsx b/src/routes/Onboarding/Introduction.tsx index 9078f7107..7dc5ae1ca 100644 --- a/src/routes/Onboarding/Introduction.tsx +++ b/src/routes/Onboarding/Introduction.tsx @@ -3,22 +3,16 @@ import { onboardingPaths } from 'routes/Onboarding/paths' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { Themes, getSystemTheme } from 'lib/theme' import { bracket } from 'lib/exampleKcl' -import { - getNextProjectIndex, - interpolateProjectNameWithIndex, -} from 'lib/tauriFS' +import { createAndOpenNewProject } from 'lib/tauriFS' import { isTauri } from 'lib/isTauri' -import { useNavigate } from 'react-router-dom' -import { paths } from 'lib/paths' +import { useNavigate, useRouteLoaderData } from 'react-router-dom' import { codeManager, kclManager } from 'lib/singletons' -import { join } from '@tauri-apps/api/path' -import { - APP_NAME, - ONBOARDING_PROJECT_NAME, - PROJECT_ENTRYPOINT, -} from 'lib/constants' -import { createNewProjectDirectory, listProjects } from 'lib/tauri' +import { APP_NAME } from 'lib/constants' import { useState } from 'react' +import { useLspContext } from 'components/LspProvider' +import { IndexLoaderData } from 'lib/types' +import { paths } from 'lib/paths' +import { useFileContext } from 'hooks/useFileContext' /** * Show either a welcome screen or a warning screen @@ -47,30 +41,28 @@ function OnboardingResetWarning(props: OnboardingResetWarningProps) { {!isTauri() ? ( ) : ( - + )}
) } -function OnboardingWarningDesktop() { +function OnboardingWarningDesktop(props: OnboardingResetWarningProps) { const navigate = useNavigate() const dismiss = useDismiss() + const loaderData = useRouteLoaderData(paths.FILE) as IndexLoaderData + const { context: fileContext } = useFileContext() + const { onProjectClose, onProjectOpen } = useLspContext() - async function createAndOpenNewProject() { - const projects = await listProjects() - const nextIndex = getNextProjectIndex(ONBOARDING_PROJECT_NAME, projects) - const name = interpolateProjectNameWithIndex( - ONBOARDING_PROJECT_NAME, - nextIndex - ) - const newFile = await createNewProjectDirectory(name, bracket) - navigate( - `${paths.FILE}/${encodeURIComponent( - await join(newFile.path, PROJECT_ENTRYPOINT) - )}${paths.ONBOARDING.INDEX}` + async function onAccept() { + onProjectClose( + loaderData.file || null, + fileContext.project.path || null, + false ) + await createAndOpenNewProject({ onProjectOpen, navigate }) + props.setShouldShowWarning(false) } return ( @@ -88,11 +80,7 @@ function OnboardingWarningDesktop() { { - void createAndOpenNewProject() - codeManager.updateCodeEditor(bracket) - dismiss() - }} + next={onAccept} nextText="Make a new project" /> diff --git a/src/routes/Onboarding/index.tsx b/src/routes/Onboarding/index.tsx index a0a0e8454..ea9f12988 100644 --- a/src/routes/Onboarding/index.tsx +++ b/src/routes/Onboarding/index.tsx @@ -79,7 +79,7 @@ export const onboardingRoutes = [ export function useDemoCode() { useEffect(() => { - if (!editorManager.editorView) return + if (!editorManager.editorView || codeManager.code === bracket) return setTimeout(async () => { codeManager.updateCodeStateEditor(bracket) kclManager.isFirstRender = true