CodeBlock Test Page
This page tests CodeBlocks with large amounts of content to verify the shrinking issue is fixed.
Small Code Block
import { Button } from '@vuer-ai/vuer-uikit';
function App() {
return <Button>Click me</Button>;
}
Large Code Block
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
import {
Button,
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
Input,
Label,
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
Textarea,
Checkbox,
RadioGroup,
RadioGroupItem,
Switch,
Slider,
Progress,
Avatar,
AvatarFallback,
AvatarImage,
Badge,
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
Sheet,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
Tabs,
TabsContent,
TabsList,
TabsTrigger,
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
Toast,
ToastAction,
ToastDescription,
ToastProvider,
ToastTitle,
ToastViewport,
useToast,
Toaster,
} from '@vuer-ai/vuer-uikit/src';
interface UserProfile {
id: string;
name: string;
email: string;
avatar?: string;
bio?: string;
preferences: {
theme: 'light' | 'dark' | 'auto';
notifications: boolean;
marketing: boolean;
analytics: boolean;
};
settings: {
language: string;
timezone: string;
dateFormat: string;
currency: string;
};
metadata: {
createdAt: Date;
updatedAt: Date;
lastLoginAt?: Date;
loginCount: number;
};
}
interface FormData {
personalInfo: {
firstName: string;
lastName: string;
email: string;
phone?: string;
dateOfBirth?: Date;
};
address: {
street: string;
city: string;
state: string;
zipCode: string;
country: string;
};
preferences: UserProfile['preferences'];
settings: UserProfile['settings'];
}
interface ComponentProps {
user: UserProfile;
onSave: (data: FormData) => Promise<void>;
onCancel: () => void;
isLoading?: boolean;
error?: string | null;
className?: string;
}
export const ComplexUserProfileForm: React.FC<ComponentProps> = ({
user,
onSave,
onCancel,
isLoading = false,
error = null,
className = '',
}) => {
// State management for form data
const [formData, setFormData] = useState<FormData>({
personalInfo: {
firstName: user.name.split(' ')[0] || '',
lastName: user.name.split(' ').slice(1).join(' ') || '',
email: user.email,
phone: '',
dateOfBirth: undefined,
},
address: {
street: '',
city: '',
state: '',
zipCode: '',
country: 'US',
},
preferences: user.preferences,
settings: user.settings,
});
// Form validation state
const [errors, setErrors] = useState<Record<string, string>>({});
const [touched, setTouched] = useState<Record<string, boolean>>({});
const [isSubmitting, setIsSubmitting] = useState(false);
// Refs for form elements
const formRef = useRef<HTMLFormElement>(null);
const firstInputRef = useRef<HTMLInputElement>(null);
// Toast hook for notifications
const { toast } = useToast();
// Memoized validation rules
const validationRules = useMemo(() => ({
'personalInfo.firstName': (value: string) =>
!value ? 'First name is required' :
value.length < 2 ? 'First name must be at least 2 characters' : null,
'personalInfo.lastName': (value: string) =>
!value ? 'Last name is required' :
value.length < 2 ? 'Last name must be at least 2 characters' : null,
'personalInfo.email': (value: string) => {
if (!value) return 'Email is required';
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return !emailRegex.test(value) ? 'Invalid email format' : null;
},
'address.street': (value: string) =>
!value ? 'Street address is required' : null,
'address.city': (value: string) =>
!value ? 'City is required' : null,
'address.state': (value: string) =>
!value ? 'State is required' : null,
'address.zipCode': (value: string) => {
if (!value) return 'ZIP code is required';
const zipRegex = /^\d{5}(-\d{4})?$/;
return !zipRegex.test(value) ? 'Invalid ZIP code format' : null;
},
}), []);
// Validation function
const validateField = useCallback((fieldPath: string, value: any) => {
const validator = validationRules[fieldPath as keyof typeof validationRules];
return validator ? validator(value) : null;
}, [validationRules]);
// Handle field changes
const handleFieldChange = useCallback((fieldPath: string, value: any) => {
setFormData(prev => {
const keys = fieldPath.split('.');
const newData = { ...prev };
let current: any = newData;
for (let i = 0; i < keys.length - 1; i++) {
current[keys[i]] = { ...current[keys[i]] };
current = current[keys[i]];
}
current[keys[keys.length - 1]] = value;
return newData;
});
// Validate field if it's been touched
if (touched[fieldPath]) {
const error = validateField(fieldPath, value);
setErrors(prev => ({
...prev,
[fieldPath]: error,
}));
}
}, [touched, validateField]);
// Handle field blur (mark as touched and validate)
const handleFieldBlur = useCallback((fieldPath: string, value: any) => {
setTouched(prev => ({ ...prev, [fieldPath]: true }));
const error = validateField(fieldPath, value);
setErrors(prev => ({
...prev,
[fieldPath]: error,
}));
}, [validateField]);
// Validate entire form
const validateForm = useCallback(() => {
const newErrors: Record<string, string> = {};
Object.keys(validationRules).forEach(fieldPath => {
const keys = fieldPath.split('.');
let value = formData as any;
for (const key of keys) {
value = value?.[key];
}
const error = validateField(fieldPath, value);
if (error) {
newErrors[fieldPath] = error;
}
});
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
}, [formData, validationRules, validateField]);
// Handle form submission
const handleSubmit = useCallback(async (e: React.FormEvent) => {
e.preventDefault();
if (!validateForm()) {
toast({
title: 'Validation Error',
description: 'Please fix the errors in the form before submitting.',
variant: 'destructive',
});
return;
}
setIsSubmitting(true);
try {
await onSave(formData);
toast({
title: 'Success',
description: 'Profile updated successfully!',
variant: 'default',
});
} catch (err) {
toast({
title: 'Error',
description: 'Failed to update profile. Please try again.',
variant: 'destructive',
});
} finally {
setIsSubmitting(false);
}
}, [formData, validateForm, onSave, toast]);
// Focus first input on mount
useEffect(() => {
if (firstInputRef.current) {
firstInputRef.current.focus();
}
}, []);
// Keyboard shortcuts
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.metaKey || e.ctrlKey) {
switch (e.key) {
case 's':
e.preventDefault();
if (!isSubmitting && !isLoading) {
handleSubmit(e as any);
}
break;
case 'Escape':
e.preventDefault();
onCancel();
break;
}
}
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [handleSubmit, isSubmitting, isLoading, onCancel]);
return (
<ToastProvider>
<div className={`max-w-4xl mx-auto p-6 space-y-6 ${className}`}>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Avatar className="h-8 w-8">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback>
{user.name.split(' ').map(n => n[0]).join('')}
</AvatarFallback>
</Avatar>
Edit Profile
</CardTitle>
<CardDescription>
Update your personal information and preferences
</CardDescription>
</CardHeader>
<form ref={formRef} onSubmit={handleSubmit}>
<CardContent className="space-y-8">
{error && (
<div className="p-4 bg-destructive/10 border border-destructive/20 rounded-md">
<p className="text-destructive text-sm">{error}</p>
</div>
)}
{/* Personal Information Section */}
<div className="space-y-4">
<h3 className="text-lg font-semibold">Personal Information</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="firstName">First Name *</Label>
<Input
ref={firstInputRef}
id="firstName"
value={formData.personalInfo.firstName}
onChange={(e) => handleFieldChange('personalInfo.firstName', e.target.value)}
onBlur={(e) => handleFieldBlur('personalInfo.firstName', e.target.value)}
className={errors['personalInfo.firstName'] ? 'border-destructive' : ''}
placeholder="Enter your first name"
/>
{errors['personalInfo.firstName'] && (
<p className="text-destructive text-sm">{errors['personalInfo.firstName']}</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="lastName">Last Name *</Label>
<Input
id="lastName"
value={formData.personalInfo.lastName}
onChange={(e) => handleFieldChange('personalInfo.lastName', e.target.value)}
onBlur={(e) => handleFieldBlur('personalInfo.lastName', e.target.value)}
className={errors['personalInfo.lastName'] ? 'border-destructive' : ''}
placeholder="Enter your last name"
/>
{errors['personalInfo.lastName'] && (
<p className="text-destructive text-sm">{errors['personalInfo.lastName']}</p>
)}
</div>
</div>
</div>
{/* This is a very long code block that should test the shrinking behavior */}
{/* The component continues with more sections... */}
</CardContent>
<CardFooter className="flex justify-between">
<Button type="button" variant="outline" onClick={onCancel}>
Cancel
</Button>
<Button
type="submit"
disabled={isSubmitting || isLoading}
className="min-w-[120px]"
>
{isSubmitting ? 'Saving...' : 'Save Changes'}
</Button>
</CardFooter>
</form>
</Card>
</div>
<Toaster />
</ToastProvider>
);
};
export default ComplexUserProfileForm;
Very Long Code Block with Horizontal Scrolling
// This is an extremely long line that should trigger horizontal scrolling: const veryLongVariableName = "This is a very long string that contains a lot of text and should demonstrate horizontal scrolling behavior in code blocks when the content exceeds the available width of the container element";
function processLargeDataset(data, options = {}) {
const {
chunkSize = 1000,
parallel = true,
timeout = 30000,
retryAttempts = 3,
onProgress = () => {},
onError = () => {},
transformFunction = (item) => item,
filterFunction = (item) => true,
sortFunction = (a, b) => a.id.localeCompare(b.id),
aggregateFunction = (acc, item) => ({ ...acc, [item.id]: item }),
} = options;
return new Promise(async (resolve, reject) => {
try {
// Initialize processing state
const processingState = {
totalItems: data.length,
processedItems: 0,
errors: [],
results: {},
startTime: Date.now(),
chunks: [],
currentChunkIndex: 0,
};
// Split data into chunks for processing
for (let i = 0; i < data.length; i += chunkSize) {
processingState.chunks.push(data.slice(i, i + chunkSize));
}
// Process chunks sequentially or in parallel based on options
if (parallel) {
const chunkPromises = processingState.chunks.map(async (chunk, index) => {
return await processChunk(chunk, index, processingState, {
transformFunction,
filterFunction,
sortFunction,
aggregateFunction,
retryAttempts,
timeout,
onProgress,
onError,
});
});
const chunkResults = await Promise.allSettled(chunkPromises);
// Aggregate results from all chunks
chunkResults.forEach((result, index) => {
if (result.status === 'fulfilled') {
Object.assign(processingState.results, result.value);
} else {
processingState.errors.push({
chunkIndex: index,
error: result.reason,
timestamp: Date.now(),
});
}
});
} else {
// Sequential processing
for (let i = 0; i < processingState.chunks.length; i++) {
try {
const chunkResult = await processChunk(
processingState.chunks[i],
i,
processingState,
{
transformFunction,
filterFunction,
sortFunction,
aggregateFunction,
retryAttempts,
timeout,
onProgress,
onError,
}
);
Object.assign(processingState.results, chunkResult);
} catch (error) {
processingState.errors.push({
chunkIndex: i,
error,
timestamp: Date.now(),
});
}
}
}
// Final processing and cleanup
const endTime = Date.now();
const processingTime = endTime - processingState.startTime;
resolve({
results: processingState.results,
errors: processingState.errors,
statistics: {
totalItems: processingState.totalItems,
processedItems: Object.keys(processingState.results).length,
errorCount: processingState.errors.length,
processingTime,
averageTimePerItem: processingTime / processingState.totalItems,
throughput: processingState.totalItems / (processingTime / 1000),
},
});
} catch (error) {
reject(error);
}
});
}
async function processChunk(chunk, chunkIndex, state, options) {
const { transformFunction, filterFunction, sortFunction, aggregateFunction, retryAttempts, timeout, onProgress, onError } = options;
// Another extremely long line for testing horizontal scroll: const anotherVeryLongVariableNameThatShouldTriggerHorizontalScrollingInTheCodeBlockComponentWhenTheContentExceedsTheAvailableWidthOfTheContainer = "This is another very long string for testing purposes";
let attempts = 0;
while (attempts < retryAttempts) {
try {
const processedChunk = await Promise.race([
new Promise(async (resolve) => {
const results = {};
for (const item of chunk) {
if (filterFunction(item)) {
const transformedItem = await transformFunction(item);
results[item.id] = transformedItem;
state.processedItems++;
// Report progress
const progress = (state.processedItems / state.totalItems) * 100;
onProgress({
progress,
processedItems: state.processedItems,
totalItems: state.totalItems,
currentChunk: chunkIndex,
currentItem: item,
});
}
}
resolve(results);
}),
new Promise((_, reject) => {
setTimeout(() => reject(new Error('Processing timeout')), timeout);
}),
]);
return processedChunk;
} catch (error) {
attempts++;
onError({
error,
chunkIndex,
attempt: attempts,
maxAttempts: retryAttempts,
});
if (attempts >= retryAttempts) {
throw error;
}
// Exponential backoff
await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempts) * 1000));
}
}
}
Test Complete
The CodeBlocks should now display properly without shrinking, even with large amounts of content.