Merge branch 'main' into nicboone8-patch-3

This commit is contained in:
Nicholas Boone
2025-06-13 12:02:48 -07:00
committed by GitHub
3 changed files with 69 additions and 12 deletions

View File

@ -166,8 +166,12 @@ impl Args {
KclError::new_semantic(KclErrorDetails::new(message, arg.source_ranges()))
})?;
// TODO unnecessary cloning
Ok(T::from_kcl_val(&arg).unwrap())
T::from_kcl_val(&arg).ok_or_else(|| {
KclError::new_internal(KclErrorDetails::new(
format!("Mismatch between type coercion and value extraction (this isn't your fault).\nTo assist in bug-reporting, expected type: {ty:?}; actual value: {arg:?}"),
vec![self.source_range],
))
})
}
/// Get a labelled keyword arg, check it's an array, and return all items in the array

View File

@ -54,7 +54,8 @@ export const EngineStream = (props: {
const { file, project } = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
const last = useRef<number>(Date.now())
const [firstPlay, setFirstPlay] = useState(true)
const [firstRun, setFirstRun] = useState(true)
const [needsExecution, setNeedsExecution] = useState(true)
const [goRestart, setGoRestart] = useState(false)
const [timeoutId, setTimeoutId] = useState<
ReturnType<typeof setTimeout> | undefined
@ -183,13 +184,14 @@ export const EngineStream = (props: {
// Reset the restart timeouts
setAttemptTimes([0, TIME_1_SECOND])
console.log(firstPlay)
if (!firstPlay) return
console.log(needsExecution)
if (!needsExecution) return
setFirstPlay(false)
console.log('firstPlay true, zoom to fit')
setNeedsExecution(false)
console.log('needsExecution true, zoom to fit')
kmp
.then(async () => {
setFirstRun(false)
await resetCameraPosition()
if (project && project.path) {
@ -212,7 +214,7 @@ export const EngineStream = (props: {
executeKcl
)
}
}, [firstPlay])
}, [needsExecution])
useEffect(() => {
// We do a back-off restart, using a fibonacci sequence, since it
@ -226,7 +228,7 @@ export const EngineStream = (props: {
engineStreamState.context.videoRef.current?.pause()
engineCommandManager.tearDown()
startOrReconfigureEngine()
setFirstPlay(true)
setNeedsExecution(true)
setTimeoutId(undefined)
setGoRestart(false)
@ -598,8 +600,13 @@ export const EngineStream = (props: {
EngineStreamState.Paused,
EngineStreamState.Resuming,
].some((s) => s === engineStreamState.value) && (
<Loading dataTestId="loading-engine" className="fixed inset-0 h-screen">
Connecting to engine...
<Loading
isRetrying={timeoutId !== undefined && !firstRun}
retryAttemptCountdown={attemptTimes[1]}
dataTestId="loading-engine"
className="fixed inset-0 h-screen"
>
Connecting and setting up scene
</Loading>
)}
</div>

View File

@ -21,6 +21,8 @@ interface LoadingProps extends React.PropsWithChildren {
isDummy?: boolean
className?: string
dataTestId?: string
retryAttemptCountdown?: number
isRetrying?: boolean
}
const markedOptions: MarkedOptions = {
@ -47,7 +49,7 @@ export const CONNECTION_ERROR_CALL_TO_ACTION_TEXT: Record<
[ConnectionError.DataChannelError]:
'A modeling session was created, but there was an issue creating a modeling commands channel.',
[ConnectionError.WebSocketError]:
"An unexpected issue regarding the connection to Zoo's KittyCAD API happened. We suggest re-opening Zoo Design Studio to try again.",
"An unexpected issue regarding the connection to Zoo's KittyCAD API happened. Design Studio will try to reconnect.",
[ConnectionError.LocalDescriptionInvalid]:
'The modeling session was created, but there is an issue connecting to the stream.',
[ConnectionError.MissingAuthToken]:
@ -73,10 +75,21 @@ const Loading = ({
children,
className,
dataTestId,
retryAttemptCountdown,
isRetrying,
}: LoadingProps) => {
const [error, setError] = useState<ErrorType>({
error: ConnectionError.Unset,
})
const [countdown, setCountdown] = useState<undefined | number>(
isRetrying && retryAttemptCountdown !== undefined
? Math.trunc(retryAttemptCountdown / 1000)
: undefined
)
const [timeoutIdCountdown, setTimeoutIdCountdown] = useState<
ReturnType<typeof setTimeout> | undefined
>(undefined)
const isUnrecoverableError = error.error > ConnectionError.VeryLongLoadingTime
const colorClass =
error.error === ConnectionError.Unset
@ -84,6 +97,27 @@ const Loading = ({
: !isUnrecoverableError
? 'text-warn-60'
: 'text-chalkboard-60 dark:text-chalkboard-40'
useEffect(() => {
if (retryAttemptCountdown === undefined) return
setCountdown(Math.trunc(retryAttemptCountdown / 1000))
}, [retryAttemptCountdown])
useEffect(() => {
if (countdown === undefined || countdown < 0) return
clearTimeout(timeoutIdCountdown)
setTimeoutIdCountdown(
setTimeout(() => {
setCountdown(countdown - 1)
}, 1000)
)
return () => {
clearTimeout(timeoutIdCountdown)
}
}, [countdown])
useEffect(() => {
const onConnectionStateChange = ({ detail: state }: CustomEvent) => {
if (
@ -183,6 +217,18 @@ const Loading = ({
<p className={`text-base mt-4`}>
{isUnrecoverableError ? '' : children || 'Loading'}
</p>
<div
className={
`text-base h-[1em] ` +
(countdown !== undefined &&
countdown <= 1 &&
'transition-opacity duration-1000 opacity-0')
}
>
{countdown !== undefined && countdown > 0 && isRetrying && (
<span>Connecting in {countdown}s</span>
)}
</div>
{CONNECTION_ERROR_TEXT[error.error] && (
<div>
<div className="max-w-3xl text-base flex flex-col gap-2 px-2 pt-2 mt-2 pb-6 mb-6 border-b border-chalkboard-30">