* Remove isFirstRender because isExecuting means the same and remove building scene * add small makefile util * Remove waiting for building scene prompt * Add a new model state indicator * fmt lint tsc --------- Co-authored-by: Frank Noirot <frank@kittycad.io>
213 lines
5.7 KiB
TypeScript
213 lines
5.7 KiB
TypeScript
import { useHotkeys } from 'react-hotkeys-hook'
|
|
import { Outlet, useNavigate } from 'react-router-dom'
|
|
import Introduction from './Introduction'
|
|
import Camera from './Camera'
|
|
import Sketching from './Sketching'
|
|
import { useCallback, useEffect } from 'react'
|
|
import makeUrlPathRelative from '../../lib/makeUrlPathRelative'
|
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
|
import Streaming from './Streaming'
|
|
import CodeEditor from './CodeEditor'
|
|
import ParametricModeling from './ParametricModeling'
|
|
import InteractiveNumbers from './InteractiveNumbers'
|
|
import CmdK from './CmdK'
|
|
import UserMenu from './UserMenu'
|
|
import ProjectMenu from './ProjectMenu'
|
|
import Export from './Export'
|
|
import FutureWork from './FutureWork'
|
|
import { PATHS } from 'lib/paths'
|
|
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
|
import { ActionButton } from 'components/ActionButton'
|
|
import { onboardingPaths } from 'routes/Onboarding/paths'
|
|
import { codeManager, editorManager, kclManager } from 'lib/singletons'
|
|
import { bracket } from 'lib/exampleKcl'
|
|
|
|
export const kbdClasses =
|
|
'py-0.5 px-1 text-sm rounded bg-chalkboard-10 dark:bg-chalkboard-100 border border-chalkboard-50 border-b-2'
|
|
|
|
export const onboardingRoutes = [
|
|
{
|
|
index: true,
|
|
element: <Introduction />,
|
|
},
|
|
{
|
|
path: makeUrlPathRelative(onboardingPaths.CAMERA),
|
|
element: <Camera />,
|
|
},
|
|
{
|
|
path: makeUrlPathRelative(onboardingPaths.STREAMING),
|
|
element: <Streaming />,
|
|
},
|
|
{
|
|
path: makeUrlPathRelative(onboardingPaths.EDITOR),
|
|
element: <CodeEditor />,
|
|
},
|
|
{
|
|
path: makeUrlPathRelative(onboardingPaths.PARAMETRIC_MODELING),
|
|
element: <ParametricModeling />,
|
|
},
|
|
{
|
|
path: makeUrlPathRelative(onboardingPaths.INTERACTIVE_NUMBERS),
|
|
element: <InteractiveNumbers />,
|
|
},
|
|
{
|
|
path: makeUrlPathRelative(onboardingPaths.COMMAND_K),
|
|
element: <CmdK />,
|
|
},
|
|
{
|
|
path: makeUrlPathRelative(onboardingPaths.USER_MENU),
|
|
element: <UserMenu />,
|
|
},
|
|
{
|
|
path: makeUrlPathRelative(onboardingPaths.PROJECT_MENU),
|
|
element: <ProjectMenu />,
|
|
},
|
|
{
|
|
path: makeUrlPathRelative(onboardingPaths.EXPORT),
|
|
element: <Export />,
|
|
},
|
|
// Export / conversion API
|
|
{
|
|
path: makeUrlPathRelative(onboardingPaths.SKETCHING),
|
|
element: <Sketching />,
|
|
},
|
|
{
|
|
path: makeUrlPathRelative(onboardingPaths.FUTURE_WORK),
|
|
element: <FutureWork />,
|
|
},
|
|
]
|
|
|
|
export function useDemoCode() {
|
|
useEffect(() => {
|
|
if (!editorManager.editorView || codeManager.code === bracket) return
|
|
setTimeout(async () => {
|
|
codeManager.updateCodeStateEditor(bracket)
|
|
await kclManager.executeCode(true)
|
|
await codeManager.writeToFile()
|
|
})
|
|
}, [editorManager.editorView])
|
|
}
|
|
|
|
export function useNextClick(newStatus: string) {
|
|
const filePath = useAbsoluteFilePath()
|
|
const {
|
|
settings: { send },
|
|
} = useSettingsAuthContext()
|
|
const navigate = useNavigate()
|
|
|
|
return useCallback(() => {
|
|
send({
|
|
type: 'set.app.onboardingStatus',
|
|
data: { level: 'user', value: newStatus },
|
|
})
|
|
navigate(filePath + PATHS.ONBOARDING.INDEX.slice(0, -1) + newStatus)
|
|
}, [filePath, newStatus, send, navigate])
|
|
}
|
|
|
|
export function useDismiss() {
|
|
const filePath = useAbsoluteFilePath()
|
|
const {
|
|
settings: { state, send },
|
|
} = useSettingsAuthContext()
|
|
const navigate = useNavigate()
|
|
|
|
const settingsCallback = useCallback(() => {
|
|
send({
|
|
type: 'set.app.onboardingStatus',
|
|
data: { level: 'user', value: 'dismissed' },
|
|
})
|
|
}, [send])
|
|
|
|
/**
|
|
* A "listener" for the XState to return to "idle" state
|
|
* when the user dismisses the onboarding, using the callback above
|
|
*/
|
|
useEffect(() => {
|
|
if (
|
|
state.context.app.onboardingStatus.user === 'dismissed' &&
|
|
state.matches('idle')
|
|
) {
|
|
navigate(filePath)
|
|
}
|
|
}, [filePath, navigate, state])
|
|
|
|
return settingsCallback
|
|
}
|
|
|
|
// Get the 1-indexed step number of the current onboarding step
|
|
export function useStepNumber(
|
|
slug?: (typeof onboardingPaths)[keyof typeof onboardingPaths]
|
|
) {
|
|
return slug
|
|
? slug === onboardingPaths.INDEX
|
|
? 1
|
|
: onboardingRoutes.findIndex(
|
|
(r) => r.path === makeUrlPathRelative(slug)
|
|
) + 1
|
|
: undefined
|
|
}
|
|
|
|
export function OnboardingButtons({
|
|
next,
|
|
nextText,
|
|
dismiss,
|
|
currentSlug,
|
|
className,
|
|
...props
|
|
}: {
|
|
next: () => void
|
|
nextText?: string
|
|
dismiss: () => void
|
|
currentSlug?: (typeof onboardingPaths)[keyof typeof onboardingPaths]
|
|
className?: string
|
|
} & React.HTMLAttributes<HTMLDivElement>) {
|
|
const stepNumber = useStepNumber(currentSlug)
|
|
|
|
return (
|
|
<div
|
|
className={'flex items-center justify-between ' + (className ?? '')}
|
|
{...props}
|
|
>
|
|
<ActionButton
|
|
Element="button"
|
|
onClick={dismiss}
|
|
iconStart={{
|
|
icon: 'close',
|
|
className: 'text-chalkboard-10',
|
|
bgClassName: 'bg-destroy-80 group-hover:bg-destroy-80',
|
|
}}
|
|
className="hover:border-destroy-40 hover:bg-destroy-10/50 dark:hover:bg-destroy-80/50"
|
|
>
|
|
Dismiss
|
|
</ActionButton>
|
|
{stepNumber !== undefined && (
|
|
<p className="font-mono text-xs text-center m-0">
|
|
{stepNumber} / {onboardingRoutes.length}
|
|
</p>
|
|
)}
|
|
<ActionButton
|
|
Element="button"
|
|
onClick={next}
|
|
iconStart={{ icon: 'arrowRight', bgClassName: 'dark:bg-chalkboard-80' }}
|
|
className="dark:hover:bg-chalkboard-80/50"
|
|
data-testid="onboarding-next"
|
|
>
|
|
{nextText ?? 'Next'}
|
|
</ActionButton>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const Onboarding = () => {
|
|
const dismiss = useDismiss()
|
|
useHotkeys('esc', dismiss)
|
|
|
|
return (
|
|
<div className="content" data-testid="onboarding-content">
|
|
<Outlet />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default Onboarding
|