@wandelbots/wandelbots-js-react-components 2.32.0-pr.feature-robot-precondition-list.372.8ed54a6 → 2.32.0-pr.feature-robot-precondition-list.372.5bce944

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,6 @@
1
1
  import { Box, Button, Card, Divider, Typography, useTheme } from "@mui/material"
2
2
  import { Bounds, useBounds } from "@react-three/drei"
3
3
  import { Canvas } from "@react-three/fiber"
4
- import type { DHParameter } from "@wandelbots/nova-api/v1"
5
4
  import type {
6
5
  ConnectedMotionGroup,
7
6
  RobotControllerStateOperationModeEnum,
@@ -16,27 +15,23 @@ import { PresetEnvironment } from "./3d-viewport/PresetEnvironment"
16
15
  import { CycleTimer } from "./CycleTimer"
17
16
  import type { ProgramState } from "./ProgramControl"
18
17
  import { ProgramStateIndicator } from "./ProgramStateIndicator"
19
- import { SupportedRobot } from "./robots/SupportedRobot"
18
+ import { Robot } from "./robots/Robot"
20
19
 
21
20
  // Component to refresh bounds when model renders
22
21
  function BoundsRefresher({
23
22
  modelRenderTrigger,
24
23
  children,
25
24
  }: {
26
- modelRenderTrigger?: number
25
+ modelRenderTrigger: number
27
26
  children: React.ReactNode
28
27
  }) {
29
- const api = useBounds()
28
+ const bounds = useBounds()
30
29
 
31
30
  useEffect(() => {
32
- if (modelRenderTrigger && modelRenderTrigger > 0) {
33
- // Small delay to ensure the model is fully rendered
34
- const timer = setTimeout(() => {
35
- api.refresh().fit()
36
- }, 100)
37
- return () => clearTimeout(timer)
31
+ if (modelRenderTrigger > 0) {
32
+ bounds.refresh().clip().fit()
38
33
  }
39
- }, [modelRenderTrigger, api])
34
+ }, [modelRenderTrigger, bounds])
40
35
 
41
36
  return <>{children}</>
42
37
  }
@@ -58,11 +53,9 @@ export interface RobotCardProps {
58
53
  onDriveToHomeRelease?: () => void
59
54
  /** Connected motion group for the robot */
60
55
  connectedMotionGroup: ConnectedMotionGroup
61
- /** Custom robot component to render (optional, defaults to SupportedRobot) */
56
+ /** Custom robot component to render (optional, defaults to Robot) */
62
57
  robotComponent?: React.ComponentType<{
63
- rapidlyChangingMotionState: ConnectedMotionGroup["rapidlyChangingMotionState"]
64
- modelFromController: string
65
- dhParameters: DHParameter[]
58
+ connectedMotionGroup: ConnectedMotionGroup
66
59
  flangeRef?: React.Ref<Group>
67
60
  postModelRender?: () => void
68
61
  transparentColor?: string
@@ -82,6 +75,17 @@ export interface RobotCardProps {
82
75
  autoStart?: boolean
83
76
  className?: string
84
77
  }>
78
+ /** Callback to receive cycle timer controls for external timer management */
79
+ onCycleTimerReady?: (controls: {
80
+ startNewCycle: (maxTimeSeconds: number, elapsedSeconds?: number) => void
81
+ pause: () => void
82
+ resume: () => void
83
+ isPaused: () => boolean
84
+ }) => void
85
+ /** Callback fired when a cycle completes (reaches zero) */
86
+ onCycleEnd?: () => void
87
+ /** Whether the cycle timer should auto-start when a new cycle is set */
88
+ cycleTimerAutoStart?: boolean
85
89
  /** Additional CSS class name */
86
90
  className?: string
87
91
  }
@@ -95,7 +99,12 @@ export interface RobotCardProps {
95
99
  * - Automatic layout switching based on aspect ratio:
96
100
  * - Portrait mode: Vertical layout with robot in center
97
101
  * - Landscape mode: Horizontal layout with robot on left, content on right (left-aligned)
98
- * - Minimum size constraints (300px width, 400px height in portrait, 250px height in landscape) for usability
102
+ * - Responsive 3D robot rendering:
103
+ * - Scales dynamically with container size
104
+ * - Hides at very small sizes to preserve usability
105
+ * - Adaptive margin based on available space
106
+ * - Smart spacing and padding that reduces at smaller sizes
107
+ * - Minimum size constraints for usability while maximizing content density
99
108
  * - Robot name displayed in Typography h6 at top-left
100
109
  * - Program state indicator below the name
101
110
  * - Auto-fitting 3D robot model that scales with container size
@@ -116,8 +125,11 @@ export const RobotCard = externalizeComponent(
116
125
  onDriveToHomePress,
117
126
  onDriveToHomeRelease,
118
127
  connectedMotionGroup,
119
- robotComponent: RobotComponent = SupportedRobot,
128
+ robotComponent: RobotComponent = Robot,
120
129
  cycleTimerComponent: CycleTimerComponent = CycleTimer,
130
+ onCycleTimerReady,
131
+ onCycleEnd,
132
+ cycleTimerAutoStart = true,
121
133
  className,
122
134
  }: RobotCardProps) => {
123
135
  const theme = useTheme()
@@ -127,22 +139,35 @@ export const RobotCard = externalizeComponent(
127
139
  const robotRef = useRef<Group>(null)
128
140
  const cardRef = useRef<HTMLDivElement>(null)
129
141
  const [isLandscape, setIsLandscape] = useState(false)
142
+ const [cardSize, setCardSize] = useState<{
143
+ width: number
144
+ height: number
145
+ }>({ width: 400, height: 600 })
130
146
  const [modelRenderTrigger, setModelRenderTrigger] = useState(0)
131
147
 
132
- // Hook to detect aspect ratio changes
148
+ // Store cycle timer controls for external control
149
+ const cycleControlsRef = useRef<{
150
+ startNewCycle: (maxTimeSeconds: number, elapsedSeconds?: number) => void
151
+ pause: () => void
152
+ resume: () => void
153
+ isPaused: () => boolean
154
+ } | null>(null)
155
+
156
+ // Hook to detect aspect ratio and size changes
133
157
  useEffect(() => {
134
- const checkAspectRatio = () => {
158
+ const checkDimensions = () => {
135
159
  if (cardRef.current) {
136
160
  const { offsetWidth, offsetHeight } = cardRef.current
137
161
  setIsLandscape(offsetWidth > offsetHeight)
162
+ setCardSize({ width: offsetWidth, height: offsetHeight })
138
163
  }
139
164
  }
140
165
 
141
166
  // Initial check
142
- checkAspectRatio()
167
+ checkDimensions()
143
168
 
144
169
  // Set up ResizeObserver to watch for size changes
145
- const resizeObserver = new ResizeObserver(checkAspectRatio)
170
+ const resizeObserver = new ResizeObserver(checkDimensions)
146
171
  if (cardRef.current) {
147
172
  resizeObserver.observe(cardRef.current)
148
173
  }
@@ -176,9 +201,9 @@ export const RobotCard = externalizeComponent(
176
201
  }
177
202
  }, [isDriveToHomePressed, onDriveToHomeRelease])
178
203
 
179
- // Mock cycle timer controls for now
204
+ // Store and provide cycle timer controls for external use
180
205
  const handleCycleComplete = useCallback(
181
- (_controls: {
206
+ (controls: {
182
207
  startNewCycle: (
183
208
  maxTimeSeconds: number,
184
209
  elapsedSeconds?: number,
@@ -187,16 +212,47 @@ export const RobotCard = externalizeComponent(
187
212
  resume: () => void
188
213
  isPaused: () => boolean
189
214
  }) => {
190
- // TODO: Implement cycle timer integration if needed
191
- // Controls are available here for future integration
215
+ // Store the controls for potential future use
216
+ cycleControlsRef.current = controls
217
+
218
+ // Notify parent component that timer controls are ready
219
+ if (onCycleTimerReady) {
220
+ onCycleTimerReady(controls)
221
+ }
192
222
  },
193
- [],
223
+ [onCycleTimerReady],
194
224
  )
195
225
 
226
+ // Determine if robot should be hidden at small sizes to save space
227
+ // Less aggressive hiding for portrait mode since robot is in center
228
+ const shouldHideRobot = isLandscape
229
+ ? cardSize.width < 350 || cardSize.height < 250 // Aggressive for landscape
230
+ : cardSize.width < 250 || cardSize.height < 180 // Less aggressive for portrait
231
+
232
+ // Calculate responsive robot scale based on card size and orientation
233
+ const getRobotScale = () => {
234
+ if (shouldHideRobot) return 0
235
+
236
+ if (isLandscape) {
237
+ // More aggressive scaling for landscape since robot is on side
238
+ if (cardSize.width < 450) return 0.8
239
+ if (cardSize.width < 550) return 0.9
240
+ return 1
241
+ } else {
242
+ // Less aggressive scaling for portrait since robot is central
243
+ if (cardSize.width < 300) return 0.8
244
+ if (cardSize.width < 400) return 0.9
245
+ return 1
246
+ }
247
+ }
248
+
249
+ const robotScale = getRobotScale()
250
+
196
251
  return (
197
252
  <Card
198
253
  ref={cardRef}
199
254
  className={className}
255
+ elevation={5}
200
256
  sx={{
201
257
  width: "100%",
202
258
  height: "100%",
@@ -204,9 +260,10 @@ export const RobotCard = externalizeComponent(
204
260
  flexDirection: isLandscape ? "row" : "column",
205
261
  position: "relative",
206
262
  overflow: "hidden",
207
- minWidth: 300,
208
- minHeight: isLandscape ? 300 : 400,
209
- background: "var(--background-paper-elevation-8, #292B3F)",
263
+ minWidth: { xs: 180, sm: 220, md: 250 },
264
+ minHeight: isLandscape
265
+ ? { xs: 160, sm: 200, md: 250 }
266
+ : { xs: 200, sm: 280, md: 350 },
210
267
  border:
211
268
  "1px solid var(--secondary-_states-outlinedBorder, #FFFFFF1F)",
212
269
  borderRadius: "18px",
@@ -224,63 +281,62 @@ export const RobotCard = externalizeComponent(
224
281
  minHeight: "100%",
225
282
  maxHeight: "100%",
226
283
  borderRadius: 1,
227
- m: 2,
228
- mr: 1,
284
+ m: { xs: 1.5, sm: 2, md: 3 },
285
+ mr: { xs: 0.75, sm: 1, md: 1.5 },
229
286
  overflow: "hidden", // Prevent content from affecting container size
287
+ display: shouldHideRobot ? "none" : "block",
230
288
  }}
231
289
  >
232
- <Canvas
233
- camera={{
234
- position: [2, 2, 2],
235
- fov: 45,
236
- }}
237
- shadows
238
- style={{
239
- borderRadius: theme.shape.borderRadius,
240
- width: "100%",
241
- height: "100%",
242
- background: "transparent",
243
- position: "absolute",
244
- top: 0,
245
- left: 0,
246
- }}
247
- dpr={[1, 2]}
248
- gl={{ alpha: true, antialias: true }}
249
- >
250
- <PresetEnvironment />
251
- <Bounds fit clip observe margin={1}>
252
- <BoundsRefresher modelRenderTrigger={modelRenderTrigger}>
253
- <group ref={robotRef}>
254
- <RobotComponent
255
- rapidlyChangingMotionState={
256
- connectedMotionGroup.rapidlyChangingMotionState
257
- }
258
- modelFromController={
259
- connectedMotionGroup.modelFromController || ""
260
- }
261
- dhParameters={connectedMotionGroup.dhParameters || []}
262
- postModelRender={handleModelRender}
263
- />
264
- </group>
265
- </BoundsRefresher>
266
- </Bounds>
267
- </Canvas>
290
+ {!shouldHideRobot && (
291
+ <Canvas
292
+ camera={{
293
+ position: [4, 4, 4], // Move camera further back for orthographic view
294
+ fov: 15, // Low FOV for near-orthographic projection
295
+ }}
296
+ shadows
297
+ frameloop="demand"
298
+ style={{
299
+ borderRadius: theme.shape.borderRadius,
300
+ width: "100%",
301
+ height: "100%",
302
+ background: "transparent",
303
+ position: "absolute",
304
+ top: 0,
305
+ left: 0,
306
+ }}
307
+ dpr={[1, 2]}
308
+ gl={{ alpha: true, antialias: true }}
309
+ >
310
+ <PresetEnvironment />
311
+ <Bounds fit clip observe margin={1}>
312
+ <BoundsRefresher modelRenderTrigger={modelRenderTrigger}>
313
+ <group ref={robotRef} scale={robotScale}>
314
+ <RobotComponent
315
+ connectedMotionGroup={connectedMotionGroup}
316
+ postModelRender={handleModelRender}
317
+ />
318
+ </group>
319
+ </BoundsRefresher>
320
+ </Bounds>
321
+ </Canvas>
322
+ )}
268
323
  </Box>
269
324
 
270
325
  {/* Content container on right */}
271
326
  <Box
272
327
  sx={{
273
- flex: "1",
328
+ flex: shouldHideRobot ? "1" : "1",
274
329
  display: "flex",
275
330
  flexDirection: "column",
276
331
  justifyContent: "flex-start",
332
+ width: shouldHideRobot ? "100%" : "50%",
277
333
  }}
278
334
  >
279
335
  {/* Header section with robot name and program state */}
280
336
  <Box
281
337
  sx={{
282
- p: 3,
283
- pb: 2,
338
+ p: { xs: 1.5, sm: 2, md: 3 },
339
+ pb: { xs: 1, sm: 1.5, md: 2 },
284
340
  textAlign: "left",
285
341
  }}
286
342
  >
@@ -297,7 +353,7 @@ export const RobotCard = externalizeComponent(
297
353
  {/* Bottom section with runtime, cycle time, and button */}
298
354
  <Box
299
355
  sx={{
300
- p: 3,
356
+ p: { xs: 1.5, sm: 2, md: 3 },
301
357
  pt: 0,
302
358
  flex: "1",
303
359
  display: "flex",
@@ -324,14 +380,16 @@ export const RobotCard = externalizeComponent(
324
380
  variant="small"
325
381
  compact
326
382
  onCycleComplete={handleCycleComplete}
383
+ onCycleEnd={onCycleEnd}
384
+ autoStart={cycleTimerAutoStart}
327
385
  />
328
386
  </Box>
329
387
 
330
388
  {/* Divider */}
331
389
  <Divider
332
390
  sx={{
333
- mt: 2,
334
- mb: 2,
391
+ mt: 1,
392
+ mb: 0,
335
393
  borderColor: theme.palette.divider,
336
394
  opacity: 0.5,
337
395
  }}
@@ -344,8 +402,8 @@ export const RobotCard = externalizeComponent(
344
402
  sx={{
345
403
  display: "flex",
346
404
  justifyContent: "flex-start",
347
- mt: 2,
348
- mb: 2,
405
+ mt: { xs: 1, sm: 1.5, md: 2 },
406
+ mb: { xs: 0.5, sm: 0.75, md: 1 },
349
407
  }}
350
408
  >
351
409
  <Button
@@ -353,7 +411,7 @@ export const RobotCard = externalizeComponent(
353
411
  variant="contained"
354
412
  color="secondary"
355
413
  size="small"
356
- disabled={true}
414
+ disabled={!driveToHomeEnabled}
357
415
  onMouseDown={handleDriveToHomeMouseDown}
358
416
  onMouseUp={handleDriveToHomeMouseUp}
359
417
  onMouseLeave={handleDriveToHomeMouseLeave}
@@ -375,127 +433,138 @@ export const RobotCard = externalizeComponent(
375
433
  ) : (
376
434
  <>
377
435
  {/* Portrait Layout: Header, Robot, Footer */}
378
-
379
- {/* Header section with robot name and program state */}
380
- <Box sx={{ p: 3, pb: 1 }}>
381
- <Typography variant="h6" component="h2" sx={{ mb: 1 }}>
382
- {robotName}
383
- </Typography>
384
- <ProgramStateIndicator
385
- programState={programState}
386
- safetyState={safetyState}
387
- operationMode={operationMode}
388
- />
389
- </Box>
390
-
391
- {/* 3D Robot viewport in center */}
392
436
  <Box
393
437
  sx={{
394
- flex: 1,
395
- position: "relative",
396
- minHeight: 200,
397
- borderRadius: 1,
398
- mx: 3,
399
- mb: 1,
400
- overflow: "hidden", // Prevent content from affecting container size
438
+ p: 3,
439
+ height: "100%",
440
+ display: "flex",
441
+ flexDirection: "column",
401
442
  }}
402
443
  >
403
- <Canvas
404
- camera={{
405
- position: [2, 2, 2],
406
- fov: 45,
407
- }}
408
- shadows
409
- style={{
410
- borderRadius: theme.shape.borderRadius,
411
- width: "100%",
412
- height: "100%",
413
- background: "transparent",
414
- position: "absolute",
415
- top: 0,
416
- left: 0,
417
- }}
418
- dpr={[1, 2]}
419
- gl={{ alpha: true, antialias: true }}
420
- >
421
- <PresetEnvironment />
422
- <Bounds fit clip observe margin={1.2}>
423
- <BoundsRefresher modelRenderTrigger={modelRenderTrigger}>
424
- <group ref={robotRef}>
425
- <RobotComponent
426
- rapidlyChangingMotionState={
427
- connectedMotionGroup.rapidlyChangingMotionState
428
- }
429
- modelFromController={
430
- connectedMotionGroup.modelFromController || ""
431
- }
432
- dhParameters={connectedMotionGroup.dhParameters || []}
433
- postModelRender={handleModelRender}
434
- />
435
- </group>
436
- </BoundsRefresher>
437
- </Bounds>
438
- </Canvas>
439
- </Box>
444
+ {/* Header section with robot name and program state */}
445
+ <Box>
446
+ <Typography variant="h6" component="h2" sx={{ mb: 1 }}>
447
+ {robotName}
448
+ </Typography>
449
+ <ProgramStateIndicator
450
+ programState={programState}
451
+ safetyState={safetyState}
452
+ operationMode={operationMode}
453
+ />
454
+ </Box>
440
455
 
441
- {/* Bottom section with runtime, cycle time, and button */}
442
- <Box sx={{ p: 3, pt: 0 }}>
443
- {/* Runtime display */}
444
- <Typography
445
- variant="body1"
456
+ {/* 3D Robot viewport in center */}
457
+ <Box
446
458
  sx={{
447
- mb: 0,
448
- color: "var(--text-secondary, #FFFFFFB2)",
459
+ flex: shouldHideRobot ? 0 : 1,
460
+ position: "relative",
461
+ minHeight: shouldHideRobot
462
+ ? 0
463
+ : { xs: 120, sm: 150, md: 200 },
464
+ height: shouldHideRobot ? 0 : "auto",
465
+ borderRadius: 1,
466
+ overflow: "hidden",
467
+ display: shouldHideRobot ? "none" : "block",
449
468
  }}
450
469
  >
451
- {t("RobotCard.Runtime.lb")}
452
- </Typography>
470
+ {!shouldHideRobot && (
471
+ <Canvas
472
+ camera={{
473
+ position: [3, 3, 3], // Closer camera position for portrait to make robot larger
474
+ fov: 20, // Slightly higher FOV for portrait to fill better
475
+ }}
476
+ shadows
477
+ frameloop="demand"
478
+ style={{
479
+ borderRadius: theme.shape.borderRadius,
480
+ width: "100%",
481
+ height: "100%",
482
+ background: "transparent",
483
+ position: "absolute",
484
+ top: 0,
485
+ left: 0,
486
+ }}
487
+ dpr={[1, 2]}
488
+ gl={{ alpha: true, antialias: true }}
489
+ >
490
+ <PresetEnvironment />
491
+ <Bounds fit observe margin={1}>
492
+ <BoundsRefresher
493
+ modelRenderTrigger={modelRenderTrigger}
494
+ >
495
+ <group ref={robotRef} scale={robotScale}>
496
+ <RobotComponent
497
+ connectedMotionGroup={connectedMotionGroup}
498
+ postModelRender={handleModelRender}
499
+ />
500
+ </group>
501
+ </BoundsRefresher>
502
+ </Bounds>
503
+ </Canvas>
504
+ )}
505
+ </Box>
506
+
507
+ {/* Bottom section with runtime, cycle time, and button */}
508
+ <Box>
509
+ {/* Runtime display */}
510
+ <Typography
511
+ variant="body1"
512
+ sx={{
513
+ mb: 0,
514
+ color: "var(--text-secondary, #FFFFFFB2)",
515
+ }}
516
+ >
517
+ {t("RobotCard.Runtime.lb")}
518
+ </Typography>
453
519
 
454
- {/* Compact cycle time component directly below runtime */}
455
- <CycleTimerComponent
456
- variant="small"
457
- compact
458
- onCycleComplete={handleCycleComplete}
459
- />
520
+ {/* Compact cycle time component directly below runtime */}
521
+ <CycleTimerComponent
522
+ variant="small"
523
+ compact
524
+ onCycleComplete={handleCycleComplete}
525
+ onCycleEnd={onCycleEnd}
526
+ autoStart={cycleTimerAutoStart}
527
+ />
460
528
 
461
- {/* Divider */}
462
- <Divider
463
- sx={{
464
- mt: 2,
465
- mb: 2,
466
- borderColor: theme.palette.divider,
467
- opacity: 0.5,
468
- }}
469
- />
529
+ {/* Divider */}
530
+ <Divider
531
+ sx={{
532
+ mt: 1,
533
+ mb: 0,
534
+ borderColor: theme.palette.divider,
535
+ opacity: 0.5,
536
+ }}
537
+ />
470
538
 
471
- {/* Drive to Home button with some space */}
472
- <Box
473
- sx={{
474
- display: "flex",
475
- justifyContent: "flex-start",
476
- mt: 5,
477
- mb: 2,
478
- }}
479
- >
480
- <Button
481
- ref={driveButtonRef}
482
- variant="contained"
483
- color="secondary"
484
- size="small"
485
- disabled={true}
486
- onMouseDown={handleDriveToHomeMouseDown}
487
- onMouseUp={handleDriveToHomeMouseUp}
488
- onMouseLeave={handleDriveToHomeMouseLeave}
489
- onTouchStart={handleDriveToHomeMouseDown}
490
- onTouchEnd={handleDriveToHomeMouseUp}
539
+ {/* Drive to Home button with some space */}
540
+ <Box
491
541
  sx={{
492
- textTransform: "none",
493
- px: 1.5,
494
- py: 0.5,
542
+ display: "flex",
543
+ justifyContent: "flex-start",
544
+ mt: { xs: 1, sm: 2, md: 5 },
545
+ mb: { xs: 0.5, sm: 0.75, md: 1 },
495
546
  }}
496
547
  >
497
- {t("RobotCard.DriveToHome.bt")}
498
- </Button>
548
+ <Button
549
+ ref={driveButtonRef}
550
+ variant="contained"
551
+ color="secondary"
552
+ size="small"
553
+ disabled={!driveToHomeEnabled}
554
+ onMouseDown={handleDriveToHomeMouseDown}
555
+ onMouseUp={handleDriveToHomeMouseUp}
556
+ onMouseLeave={handleDriveToHomeMouseLeave}
557
+ onTouchStart={handleDriveToHomeMouseDown}
558
+ onTouchEnd={handleDriveToHomeMouseUp}
559
+ sx={{
560
+ textTransform: "none",
561
+ px: 1.5,
562
+ py: 0.5,
563
+ }}
564
+ >
565
+ {t("RobotCard.DriveToHome.bt")}
566
+ </Button>
567
+ </Box>
499
568
  </Box>
500
569
  </Box>
501
570
  </>
@@ -1,15 +1,16 @@
1
- import { type ThreeElements } from "@react-three/fiber"
1
+ import type { ThreeElements } from "@react-three/fiber"
2
2
 
3
3
  import type { ConnectedMotionGroup } from "@wandelbots/nova-js/v1"
4
4
  import type { Group } from "three"
5
- import { SupportedRobot } from "./SupportedRobot"
6
5
  import { defaultGetModel } from "./robotModelLogic"
6
+ import { SupportedRobot } from "./SupportedRobot"
7
7
 
8
8
  export type RobotProps = {
9
9
  connectedMotionGroup: ConnectedMotionGroup
10
10
  getModel?: (modelFromController: string) => string
11
11
  flangeRef?: React.Ref<Group>
12
12
  transparentColor?: string
13
+ postModelRender?: () => void
13
14
  } & ThreeElements["group"]
14
15
 
15
16
  /**
@@ -28,6 +29,7 @@ export function Robot({
28
29
  getModel = defaultGetModel,
29
30
  flangeRef,
30
31
  transparentColor,
32
+ postModelRender,
31
33
  ...props
32
34
  }: RobotProps) {
33
35
  if (!connectedMotionGroup.dhParameters) {
@@ -44,6 +46,7 @@ export function Robot({
44
46
  getModel={getModel}
45
47
  flangeRef={flangeRef}
46
48
  transparentColor={transparentColor}
49
+ postModelRender={postModelRender}
47
50
  {...props}
48
51
  />
49
52
  )