Skip to content

refactor(recorder): optimize event handling and improve UI element rendering #849

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

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions apps/chrome-extension/src/extension/popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ export function PlaygroundPopup() {
</div>
),
},
// {
// key: 'recorder',
// label: 'Recorder (Beta)',
// icon: <VideoCameraOutlined />,
// children: <Recorder />,
// },
{
key: 'recorder',
label: 'Recorder (Beta)',
icon: <VideoCameraOutlined />,
children: <Recorder />,
},
{
key: 'bridge',
label: 'Bridge Mode',
Expand Down
56 changes: 31 additions & 25 deletions apps/chrome-extension/src/extension/recorder/ExportControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Button, Dropdown, Modal, Space, Typography, message } from 'antd';
import type { MenuProps } from 'antd';
import type React from 'react';
import { useEffect, useState } from 'react';
import { useRecordingSessionStore } from '../../store';
import { useRecordStore, useRecordingSessionStore } from '../../store';
import { generateAIDescription } from '../../utils/eventOptimizer';
import { ProgressModal, type ProgressStep } from './components/ProgressModal';
import { generatePlaywrightTest, generateYamlTest } from './generators';
Expand Down Expand Up @@ -73,7 +73,7 @@ export const ExportControls: React.FC<{

// Create a function to get the latest events with AI descriptions
const getCurrentEvents = (): ChromeRecordedEvent[] => {
return getLatestEvents(events, sessionId);
return getLatestEvents(sessionId);
};

// Generate session title and description using AI
Expand All @@ -92,10 +92,6 @@ export const ExportControls: React.FC<{
session &&
(!session.name || session.name.includes('-') || !session.description)
) {
recordLogger.info(
'Generating session title and description before export',
);

// Update progress: Step 1 in progress
updateProgressStep(stepIndex, {
status: 'loading',
Expand Down Expand Up @@ -158,11 +154,7 @@ export const ExportControls: React.FC<{
stepIndex: number,
): Promise<ChromeRecordedEvent[]> => {
const eventsNeedingDescriptions = events.filter(
(event: ChromeRecordedEvent) =>
(event.type === 'click' ||
event.type === 'input' ||
event.type === 'scroll') &&
event.descriptionLoading !== false,
(event: ChromeRecordedEvent) => event.type !== 'navigation',
);

if (eventsNeedingDescriptions.length === 0) {
Expand All @@ -180,32 +172,30 @@ export const ExportControls: React.FC<{
});

let completedCount = 0;
const updatedEvents = [...events];
const finalEvents = [...events];

// Process events in parallel with progress tracking
const optimizePromises = eventsNeedingDescriptions.map(
async (event, index) => {
try {
const description = await generateAIDescription(event, event.hashId);

updatedEvents[index] = {
finalEvents[index] = {
...event,
elementDescription: description,
descriptionLoading: false,
};
completedCount++;

const progress = Math.round(
(completedCount / eventsNeedingDescriptions.length) * 100,
);
const progress = Math.round((completedCount / eventsNeedingDescriptions.length) * 100);
updateProgressStep(stepIndex, {
status: 'loading',
progress,
details: `Generated ${completedCount}/${eventsNeedingDescriptions.length} element descriptions`,
});
} catch (error) {
console.error('Failed to optimize event:', error);
updatedEvents[index] = {
finalEvents[index] = {
...event,
elementDescription: 'failed to generate element description',
descriptionLoading: false,
Expand All @@ -220,23 +210,25 @@ export const ExportControls: React.FC<{
// Update session with new event descriptions if sessionId exists
if (sessionId) {
updateSession(sessionId, {
events: updatedEvents,
events: finalEvents,
updatedAt: Date.now(),
});
useRecordStore.getState().setEvents(finalEvents);
recordLogger.info('Updated session with AI descriptions', {
sessionId,
eventsCount: updatedEvents.length,
finalEvents,
eventsCount: finalEvents.length,
descriptionsGenerated: eventsNeedingDescriptions.length,
});
}

updateProgressStep(stepIndex, {
status: 'completed',
progress: 100,
details: `Generated descriptions for ${eventsNeedingDescriptions.length} elements`,
details: `Generated descriptions for ${events.length} elements`,
});

return updatedEvents;
return finalEvents;
};

// Common function to handle code generation
Expand Down Expand Up @@ -289,10 +281,19 @@ export const ExportControls: React.FC<{

// After stopping recording, get the latest events from session
let finalEvents = getCurrentEvents();
recordLogger.info('start generating code', {
finalEvents,
sessionId,
});

// Step 1: Generate element descriptions
updateProgressStep(0, { status: 'loading' });
finalEvents = await generateElementDescriptions(finalEvents, 0);
// generate again to ensure all descriptions are generated
recordLogger.info('Generated element descriptions', {
finalEvents,
sessionId,
});

// Step 2: Generate session title and description if not already generated
updateProgressStep(1, { status: 'loading' });
Expand All @@ -302,6 +303,11 @@ export const ExportControls: React.FC<{
finalEvents,
1,
);
recordLogger.info('Generated session title and description', {
finalEvents,
sessionId,
currentSessionName,
});

// Step 3: Generate code
updateProgressStep(2, {
Expand All @@ -317,10 +323,10 @@ export const ExportControls: React.FC<{
type === 'playwright'
? await generatePlaywrightTest(finalEvents)
: await generateYamlTest(finalEvents, {
testName: currentSessionName,
description: `Test session recorded on ${new Date().toLocaleDateString()}`,
includeTimestamps: true,
});
testName: currentSessionName,
description: `Test session recorded on ${new Date().toLocaleDateString()}`,
includeTimestamps: true,
});

// Update session with generated code if sessionId exists
if (sessionId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,15 @@ export const RecordDetail: React.FC<RecordDetailProps> = ({
<Text>{new Date(session.createdAt).toLocaleString()}</Text>
</div>
{session.url && (
<div>
<div
style={{
display: '-webkit-box',
WebkitLineClamp: 2,
WebkitBoxOrient: 'vertical',
overflow: 'hidden',
wordBreak: 'break-all',
}}
>
<Text strong>URL: </Text>
<Text>{session.url}</Text>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@ export const generateYamlTest = async (
options: YamlGenerationOptions = {},
): Promise<string> => {
try {
recordLogger.info('Starting AI-powered YAML test generation', {
eventsCount: events.length,
});

// Extract navigation and viewport information
const navigationInfo = extractNavigationAndViewportInfo(events);

recordLogger.info('Navigation and viewport info extracted', {
recordLogger.info('Starting AI-powered YAML test generation', {
eventsCount: events.length,
events,
navigationInfo,
});

// Merge navigation and viewport info into options
Expand All @@ -35,6 +33,8 @@ export const generateYamlTest = async (

recordLogger.success('AI-powered YAML test generated successfully', {
eventsCount: events.length,
events,
yamlContent,
});

return yamlContent;
Expand Down
Loading
Loading