-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat: faster screenshot output preview data #659
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
09064e9
1a6dc32
6768dda
d5f5440
9512433
22c043c
a4897d0
390c5f2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1866,6 +1866,63 @@ export class RemoteBrowser { | |
| ) as Array<Record<string, string>>; | ||
| } | ||
|
|
||
| /** | ||
| * Captures a screenshot directly without running the workflow interpreter | ||
| * @param settings Screenshot settings containing fullPage, type, etc. | ||
| * @returns Promise<void> | ||
| */ | ||
| public captureDirectScreenshot = async (settings: { | ||
| fullPage: boolean; | ||
| type: 'png' | 'jpeg'; | ||
| timeout?: number; | ||
| animations?: 'disabled' | 'allow'; | ||
| caret?: 'hide' | 'initial'; | ||
| scale?: 'css' | 'device'; | ||
| }): Promise<void> => { | ||
| if (!this.currentPage) { | ||
| logger.error("No current page available for screenshot"); | ||
| this.socket.emit('screenshotError', { | ||
| userId: this.userId, | ||
| error: 'No active page available' | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| try { | ||
| this.socket.emit('screenshotCaptureStarted', { | ||
| userId: this.userId, | ||
| fullPage: settings.fullPage | ||
| }); | ||
|
|
||
| const screenshotBuffer = await this.currentPage.screenshot({ | ||
| fullPage: settings.fullPage, | ||
| type: settings.type || 'png', | ||
| timeout: settings.timeout || 30000, | ||
| animations: settings.animations || 'allow', | ||
| caret: settings.caret || 'hide', | ||
| scale: settings.scale || 'device' | ||
| }); | ||
|
|
||
| const base64Data = screenshotBuffer.toString('base64'); | ||
| const mimeType = `image/${settings.type || 'png'}`; | ||
| const dataUrl = `data:${mimeType};base64,${base64Data}`; | ||
|
|
||
| this.socket.emit('directScreenshotCaptured', { | ||
| userId: this.userId, | ||
| screenshot: dataUrl, | ||
| mimeType: mimeType, | ||
| fullPage: settings.fullPage, | ||
| timestamp: Date.now() | ||
| }); | ||
| } catch (error) { | ||
| logger.error('Failed to capture direct screenshot:', error); | ||
| this.socket.emit('screenshotError', { | ||
| userId: this.userId, | ||
| error: error instanceof Error ? error.message : 'Unknown error occurred' | ||
| }); | ||
| } | ||
| }; | ||
|
|
||
| /** | ||
| * Registers all event listeners needed for the recording editor session. | ||
| * Should be called only once after the full initialization of the remote browser. | ||
|
|
@@ -1874,6 +1931,16 @@ export class RemoteBrowser { | |
| public registerEditorEvents = (): void => { | ||
| // For each event, include userId to make sure events are handled for the correct browser | ||
| logger.log('debug', `Registering editor events for user: ${this.userId}`); | ||
|
|
||
| this.socket.on(`captureDirectScreenshot:${this.userId}`, async (settings) => { | ||
| logger.debug(`Direct screenshot capture requested for user ${this.userId}`); | ||
| await this.captureDirectScreenshot(settings); | ||
| }); | ||
|
|
||
| // For backward compatibility | ||
| this.socket.on('captureDirectScreenshot', async (settings) => { | ||
| await this.captureDirectScreenshot(settings); | ||
| }); | ||
|
Comment on lines
+1935
to
+1943
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add error handling and rate limiting for screenshot events. The event handlers lack explicit error handling and rate limiting, which could lead to resource abuse or unhandled errors. +// Add rate limiting for screenshot captures
+private lastScreenshotTime = 0;
+private readonly SCREENSHOT_RATE_LIMIT = 1000; // 1 second
this.socket.on(`captureDirectScreenshot:${this.userId}`, async (settings) => {
- logger.debug(`Direct screenshot capture requested for user ${this.userId}`);
- await this.captureDirectScreenshot(settings);
+ try {
+ const now = Date.now();
+ if (now - this.lastScreenshotTime < this.SCREENSHOT_RATE_LIMIT) {
+ logger.warn(`Screenshot rate limit exceeded for user ${this.userId}`);
+ this.socket.emit('screenshotError', {
+ userId: this.userId,
+ error: 'Rate limit exceeded. Please wait before requesting another screenshot.'
+ });
+ return;
+ }
+ this.lastScreenshotTime = now;
+
+ logger.debug(`Direct screenshot capture requested for user ${this.userId}`);
+ await this.captureDirectScreenshot(settings);
+ } catch (error) {
+ logger.error(`Error handling screenshot request for user ${this.userId}:`, error);
+ this.socket.emit('screenshotError', {
+ userId: this.userId,
+ error: 'Failed to process screenshot request'
+ });
+ }
});
// For backward compatibility
this.socket.on('captureDirectScreenshot', async (settings) => {
- await this.captureDirectScreenshot(settings);
+ try {
+ const now = Date.now();
+ if (now - this.lastScreenshotTime < this.SCREENSHOT_RATE_LIMIT) {
+ logger.warn(`Screenshot rate limit exceeded`);
+ this.socket.emit('screenshotError', {
+ userId: this.userId,
+ error: 'Rate limit exceeded. Please wait before requesting another screenshot.'
+ });
+ return;
+ }
+ this.lastScreenshotTime = now;
+
+ await this.captureDirectScreenshot(settings);
+ } catch (error) {
+ logger.error(`Error handling backward compatibility screenshot request:`, error);
+ this.socket.emit('screenshotError', {
+ userId: this.userId,
+ error: 'Failed to process screenshot request'
+ });
+ }
});
🤖 Prompt for AI Agents |
||
|
|
||
| // Listen for specific events for this user | ||
| this.socket.on(`rerender:${this.userId}`, async () => { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -856,7 +856,7 @@ export const DOMBrowserRenderer: React.FC<RRWebDOMBrowserRendererProps> = ({ | |||||
|
|
||||||
| /* Make everything interactive */ | ||||||
| * { | ||||||
| cursor: ${isInCaptureMode ? "crosshair" : "pointer"} !important; | ||||||
| cursor: "pointer" !important; | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider restoring conditional cursor styling for better UX. The hardcoded "pointer" cursor removes the visual distinction between normal navigation and capture mode. Users typically expect a crosshair cursor when selecting elements ( Consider reverting to conditional cursor styling: - cursor: "pointer" !important;
+ cursor: ${isInCaptureMode ? '"crosshair"' : '"pointer"'} !important;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| } | ||||||
|
|
||||||
| /* Additional CSS from resources */ | ||||||
|
|
@@ -1127,7 +1127,7 @@ export const DOMBrowserRenderer: React.FC<RRWebDOMBrowserRendererProps> = ({ | |||||
| left: 0, | ||||||
| right: 0, | ||||||
| bottom: 0, | ||||||
| cursor: "pointer", | ||||||
| cursor: "pointer !important", | ||||||
| pointerEvents: "none", | ||||||
| zIndex: 999, | ||||||
| borderRadius: "0px 0px 5px 5px", | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -72,7 +72,7 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| startAction, finishAction | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } = useActionContext(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { browserSteps, updateBrowserTextStepLabel, deleteBrowserStep, addScreenshotStep, updateListTextFieldLabel, removeListTextField, updateListStepLimit, deleteStepsByActionId, updateListStepData } = useBrowserSteps(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { browserSteps, updateBrowserTextStepLabel, deleteBrowserStep, addScreenshotStep, updateListTextFieldLabel, removeListTextField, updateListStepLimit, deleteStepsByActionId, updateListStepData, updateScreenshotStepData } = useBrowserSteps(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { id, socket } = useSocketStore(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { t } = useTranslation(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -183,6 +183,29 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [socket, updateListStepData, isDOMMode]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (socket) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleDirectScreenshot = (data: any) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const screenshotSteps = browserSteps.filter(step => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| step.type === 'screenshot' && step.actionId === currentScreenshotActionId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (screenshotSteps.length > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const latestStep = screenshotSteps[screenshotSteps.length - 1]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateScreenshotStepData(latestStep.id, data.screenshot); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setCurrentScreenshotActionId(''); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| socket.on('directScreenshotCaptured', handleDirectScreenshot); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| socket.off('directScreenshotCaptured', handleDirectScreenshot); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [socket, id, notify, t, currentScreenshotActionId, updateScreenshotStepData, setCurrentScreenshotActionId]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+186
to
+208
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Optimize dependencies and add error handling. The socket event handling logic is correct, but consider these improvements:
Apply this diff to optimize the effect: useEffect(() => {
if (socket) {
const handleDirectScreenshot = (data: any) => {
+ try {
+ if (!data?.screenshot) {
+ console.warn('Invalid screenshot data received:', data);
+ return;
+ }
+
const screenshotSteps = browserSteps.filter(step =>
step.type === 'screenshot' && step.actionId === currentScreenshotActionId
);
if (screenshotSteps.length > 0) {
const latestStep = screenshotSteps[screenshotSteps.length - 1];
updateScreenshotStepData(latestStep.id, data.screenshot);
}
setCurrentScreenshotActionId('');
+ } catch (error) {
+ console.error('Error handling direct screenshot:', error);
+ }
};
socket.on('directScreenshotCaptured', handleDirectScreenshot);
return () => {
socket.off('directScreenshotCaptured', handleDirectScreenshot);
};
}
- }, [socket, id, notify, t, currentScreenshotActionId, updateScreenshotStepData, setCurrentScreenshotActionId]);
+ }, [socket, currentScreenshotActionId, updateScreenshotStepData, setCurrentScreenshotActionId, browserSteps]);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const extractDataClientSide = useCallback( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| listSelector: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -649,14 +672,15 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [currentListActionId, browserSteps, stopGetList, deleteStepsByActionId, resetListState, setShowPaginationOptions, setShowLimitOptions, setCaptureStage, notify, t]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const captureScreenshot = (fullPage: boolean) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const screenshotSettings: ScreenshotSettings = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const screenshotSettings = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fullPage, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'png', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'png' as const, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timeout: 30000, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| animations: 'allow', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| caret: 'hide', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| scale: 'device', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| animations: 'allow' as const, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| caret: 'hide' as const, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| scale: 'device' as const, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| socket?.emit('captureDirectScreenshot', screenshotSettings); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| socket?.emit('action', { action: 'screenshot', settings: screenshotSettings }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| addScreenshotStep(fullPage, currentScreenshotActionId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stopGetScreenshot(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,7 +2,11 @@ import React, { FC, useState } from 'react'; | |||||||||||||||||
| import { InterpretationButtons } from "../run/InterpretationButtons"; | ||||||||||||||||||
| import { useSocketStore } from "../../context/socket"; | ||||||||||||||||||
|
|
||||||||||||||||||
| export const SidePanelHeader = () => { | ||||||||||||||||||
| interface SidePanelHeaderProps { | ||||||||||||||||||
| onPreviewClick?: () => void; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| export const SidePanelHeader = ({ onPreviewClick }: SidePanelHeaderProps) => { | ||||||||||||||||||
|
|
||||||||||||||||||
| const [steppingIsDisabled, setSteppingIsDisabled] = useState(true); | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -14,7 +18,10 @@ export const SidePanelHeader = () => { | |||||||||||||||||
|
|
||||||||||||||||||
| return ( | ||||||||||||||||||
| <div style={{ width: 'inherit' }}> | ||||||||||||||||||
| <InterpretationButtons enableStepping={(isPaused) => setSteppingIsDisabled(!isPaused)} /> | ||||||||||||||||||
| <InterpretationButtons | ||||||||||||||||||
| enableStepping={(isPaused) => setSteppingIsDisabled(!isPaused)} | ||||||||||||||||||
| onPreviewComplete={onPreviewClick} | ||||||||||||||||||
| /> | ||||||||||||||||||
|
Comment on lines
+21
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainVerify the prop name mapping consistency. The prop 🏁 Script executed: #!/bin/bash
# Description: Check if onPreviewComplete is consistently used across the codebase
# Expected: Find usages to verify naming consistency
rg -A 3 -B 3 "onPreviewComplete|onPreviewClick"Length of output: 3638 Align prop naming between SidePanelHeader and InterpretationButtons To avoid semantic confusion, the “preview” callback should use the same name in both components. Right now:
Please consolidate on one name—preferably Changes needed:
With these updates, both components will share the same prop name and intent. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| {/* <Button | ||||||||||||||||||
| variant='outlined' | ||||||||||||||||||
| disabled={steppingIsDisabled} | ||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -10,6 +10,7 @@ import { useTranslation } from "react-i18next"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface InterpretationButtonsProps { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| enableStepping: (isPaused: boolean) => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onPreviewComplete?: () => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface InterpretationInfo { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -22,7 +23,7 @@ const interpretationInfo: InterpretationInfo = { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isPaused: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const InterpretationButtons = ({ enableStepping }: InterpretationButtonsProps) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const InterpretationButtons = ({ enableStepping, onPreviewComplete }: InterpretationButtonsProps) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { t } = useTranslation(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [info, setInfo] = useState<InterpretationInfo>(interpretationInfo); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [decisionModal, setDecisionModal] = useState<{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -102,16 +103,20 @@ export const InterpretationButtons = ({ enableStepping }: InterpretationButtonsP | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [socket, finishedHandler, breakpointHitHandler]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handlePlay = async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!info.running) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setInfo({ ...info, running: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const finished = await interpretCurrentRecording(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setInfo({ ...info, running: false }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (finished) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| notify('info', t('interpretation_buttons.messages.run_finished')); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| notify('error', t('interpretation_buttons.messages.run_failed')); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onPreviewComplete?.(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| notify('info', t('interpretation_buttons.messages.run_finished')); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Update notification message for accuracy. The notification message references "run_finished" but the operation is now a preview. This could confuse users about what actually happened. Consider adding a more appropriate translation key like 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Legacy code for running the interpretation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // if (!info.running) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // setInfo({ ...info, running: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // // const finished = await interpretCurrentRecording(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // setInfo({ ...info, running: false }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // if (finished) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // notify('error', t('interpretation_buttons.messages.run_failed')); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
105
to
+119
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical issues with the simplified handlePlay logic. The refactored
Consider this improved implementation: const handlePlay = async () => {
+ if (info.running) return;
+
+ try {
+ setInfo({ ...info, running: true });
onPreviewComplete?.();
- notify('info', t('interpretation_buttons.messages.run_finished'));
+ notify('info', t('interpretation_buttons.messages.preview_ready'));
+ } catch (error) {
+ notify('error', t('interpretation_buttons.messages.preview_failed'));
+ } finally {
+ setInfo({ ...info, running: false });
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // pause and stop logic (do not delete - we wil bring this back!) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add input validation for screenshot settings.
The method accepts settings from the client without validation. Malicious or invalid values could cause issues.
public captureDirectScreenshot = async (settings: { fullPage: boolean; type: 'png' | 'jpeg'; timeout?: number; animations?: 'disabled' | 'allow'; caret?: 'hide' | 'initial'; scale?: 'css' | 'device'; }): Promise<void> => { + // Validate settings + if (settings.timeout && (settings.timeout < 0 || settings.timeout > 60000)) { + logger.error("Invalid timeout value"); + this.socket.emit('screenshotError', { + userId: this.userId, + error: 'Invalid timeout value (must be 0-60000ms)' + }); + return; + } + + if (!['png', 'jpeg'].includes(settings.type)) { + logger.error("Invalid image type"); + this.socket.emit('screenshotError', { + userId: this.userId, + error: 'Invalid image type (must be png or jpeg)' + }); + return; + }📝 Committable suggestion
🤖 Prompt for AI Agents