diff --git a/rust/kcl-lib/src/std/args.rs b/rust/kcl-lib/src/std/args.rs index e8b05a130..0afe72cec 100644 --- a/rust/kcl-lib/src/std/args.rs +++ b/rust/kcl-lib/src/std/args.rs @@ -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 diff --git a/src/components/EngineStream.tsx b/src/components/EngineStream.tsx index b391b7e8a..8fd579127 100644 --- a/src/components/EngineStream.tsx +++ b/src/components/EngineStream.tsx @@ -54,7 +54,8 @@ export const EngineStream = (props: { const { file, project } = useRouteLoaderData(PATHS.FILE) as IndexLoaderData const last = useRef(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 | 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) && ( - - Connecting to engine... + + Connecting and setting up scene )} diff --git a/src/components/Loading.tsx b/src/components/Loading.tsx index 152a4b5ca..53a9483d5 100644 --- a/src/components/Loading.tsx +++ b/src/components/Loading.tsx @@ -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({ error: ConnectionError.Unset, }) + const [countdown, setCountdown] = useState( + isRetrying && retryAttemptCountdown !== undefined + ? Math.trunc(retryAttemptCountdown / 1000) + : undefined + ) + const [timeoutIdCountdown, setTimeoutIdCountdown] = useState< + ReturnType | 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 = ({

{isUnrecoverableError ? '' : children || 'Loading'}

+
+ {countdown !== undefined && countdown > 0 && isRetrying && ( + Connecting in {countdown}s + )} +
{CONNECTION_ERROR_TEXT[error.error] && (