Skip to content

Commit b639d2f

Browse files
skychxulivz
authored andcommitted
feat: memo ui component
1 parent b35dd32 commit b639d2f

File tree

1 file changed

+82
-74
lines changed

1 file changed

+82
-74
lines changed

multimodal/tarko/ui/src/components/gui-agent/ScreenshotDisplay.tsx

Lines changed: 82 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,86 @@ function getSandboxUrl() {
3434
return sessionMetadata?.sandboxUrl;
3535
}
3636

37+
const Placeholder: React.FC<{ renderBrowserShell: boolean; className?: string }> = React.memo(
38+
({ renderBrowserShell, className }) => {
39+
const placeholderClassName = renderBrowserShell
40+
? 'flex items-center justify-center bg-gray-50 dark:bg-gray-900 min-h-[400px]'
41+
: 'flex items-center justify-center bg-gray-50 dark:bg-gray-900 min-h-[400px] rounded-xl';
42+
43+
return (
44+
<div className={`${placeholderClassName} ${className || ''}`}>
45+
<div className="text-center">
46+
<div className="text-gray-400 dark:text-gray-500 text-sm">
47+
GUI Agent Environment Not Started
48+
</div>
49+
</div>
50+
</div>
51+
);
52+
},
53+
);
54+
55+
const VncViewer: React.FC<{ renderBrowserShell: boolean }> = React.memo(
56+
({ renderBrowserShell }) => {
57+
const vncContainerRef = useRef<HTMLDivElement>(null);
58+
const [scale, setScale] = useState(0.5);
59+
60+
useEffect(() => {
61+
const container = vncContainerRef.current;
62+
if (!container) return;
63+
64+
const updateScale = () => {
65+
const containerWidth = container.offsetWidth;
66+
if (containerWidth && VNC_WIDTH) {
67+
const newScale = containerWidth / VNC_WIDTH;
68+
setScale(newScale > 1 ? 1 : newScale);
69+
}
70+
};
71+
updateScale();
72+
73+
const resizeObserver = new window.ResizeObserver(() => {
74+
updateScale();
75+
});
76+
resizeObserver.observe(container);
77+
78+
return () => {
79+
resizeObserver.disconnect();
80+
};
81+
}, []);
82+
83+
const scaledWidth = VNC_WIDTH * scale;
84+
const scaledHeight = VNC_HEIGHT * scale;
85+
86+
const sandboxBaseUrl = getSandboxUrl();
87+
88+
if (!sandboxBaseUrl) {
89+
return <Placeholder renderBrowserShell={renderBrowserShell} />;
90+
}
91+
92+
return (
93+
<div className="space-y-4" ref={vncContainerRef}>
94+
<div
95+
style={{
96+
width: `${scaledWidth}px`,
97+
height: `${scaledHeight}px`,
98+
}}
99+
>
100+
<iframe
101+
className="w-full"
102+
src={`${sandboxBaseUrl + '/vnc/index.html?autoconnect=true'}`}
103+
frameBorder="0"
104+
style={{
105+
width: `${VNC_WIDTH}px`,
106+
height: `${VNC_HEIGHT}px`,
107+
transform: `scale(${scale})`,
108+
transformOrigin: 'top left',
109+
}}
110+
></iframe>
111+
</div>
112+
</div>
113+
);
114+
},
115+
);
116+
37117
export const ScreenshotDisplay: React.FC<ScreenshotDisplayProps> = ({
38118
strategy,
39119
relatedImage,
@@ -49,32 +129,6 @@ export const ScreenshotDisplay: React.FC<ScreenshotDisplayProps> = ({
49129
renderBrowserShell = true,
50130
}) => {
51131
const imageRef = useRef<HTMLImageElement>(null);
52-
const vncContainerRef = useRef<HTMLDivElement>(null);
53-
const [scale, setScale] = useState(0.5);
54-
55-
useEffect(() => {
56-
if (strategy !== 'vnc') return;
57-
const container = vncContainerRef.current;
58-
if (!container) return;
59-
60-
const updateScale = () => {
61-
const containerWidth = container.offsetWidth;
62-
if (containerWidth && VNC_WIDTH) {
63-
const newScale = containerWidth / VNC_WIDTH;
64-
setScale(newScale > 1 ? 1 : newScale);
65-
}
66-
};
67-
updateScale();
68-
69-
const resizeObserver = new window.ResizeObserver(() => {
70-
updateScale();
71-
});
72-
resizeObserver.observe(container);
73-
74-
return () => {
75-
resizeObserver.disconnect();
76-
};
77-
}, [strategy]);
78132

79133
const shouldShowMouseCursor = (imageType: 'before' | 'after' | 'single') => {
80134
if (!mousePosition || !showCoordinates) return false;
@@ -93,22 +147,6 @@ export const ScreenshotDisplay: React.FC<ScreenshotDisplayProps> = ({
93147
return content;
94148
};
95149

96-
const renderPlaceholder = () => {
97-
const placeholderClassName = renderBrowserShell
98-
? 'flex items-center justify-center bg-gray-50 dark:bg-gray-900 min-h-[400px]'
99-
: 'flex items-center justify-center bg-gray-50 dark:bg-gray-900 min-h-[400px] rounded-xl';
100-
101-
return (
102-
<div className={placeholderClassName}>
103-
<div className="text-center">
104-
<div className="text-gray-400 dark:text-gray-500 text-sm">
105-
GUI Agent Environment Not Started
106-
</div>
107-
</div>
108-
</div>
109-
);
110-
};
111-
112150
const renderImageContent = (
113151
image: string | null,
114152
alt: string,
@@ -156,7 +194,7 @@ export const ScreenshotDisplay: React.FC<ScreenshotDisplayProps> = ({
156194
);
157195
}
158196

159-
return renderPlaceholder();
197+
return <Placeholder renderBrowserShell={renderBrowserShell} />;
160198
};
161199

162200
if (strategy === 'both') {
@@ -201,37 +239,7 @@ export const ScreenshotDisplay: React.FC<ScreenshotDisplayProps> = ({
201239
}
202240

203241
if (strategy === 'vnc') {
204-
const scaledWidth = VNC_WIDTH * scale;
205-
const scaledHeight = VNC_HEIGHT * scale;
206-
207-
const sandboxBaseUrl = getSandboxUrl();
208-
209-
if (!sandboxBaseUrl) {
210-
return renderPlaceholder();
211-
}
212-
213-
return (
214-
<div className="space-y-4" ref={vncContainerRef}>
215-
<div
216-
style={{
217-
width: `${scaledWidth}px`,
218-
height: `${scaledHeight}px`,
219-
}}
220-
>
221-
<iframe
222-
className="w-full"
223-
src={`${sandboxBaseUrl + '/vnc/index.html?autoconnect=true'}`}
224-
frameBorder="0"
225-
style={{
226-
width: `${VNC_WIDTH}px`,
227-
height: `${VNC_HEIGHT}px`,
228-
transform: `scale(${scale})`,
229-
transformOrigin: 'top left',
230-
}}
231-
></iframe>
232-
</div>
233-
</div>
234-
);
242+
return <VncViewer renderBrowserShell={renderBrowserShell} />;
235243
}
236244

237245
return wrapWithBrowserShell(

0 commit comments

Comments
 (0)