+ const wrapText = (text: string, maxWidth: number, fontSize: number): string[] => {
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+ if (!ctx) return [text];
- {/* Media */}
- {orderedMedia.length > 0 && (
-
- {renderMedia(orderedMedia[currentPhotoIndex], "w-full h-full object-cover")}
- {orderedMedia.length > 1 && (
- <>
-
- {orderedMedia.map((_, idx) => (
-
-
-
- >
- )}
- {/* Text overlay */}
- {postText && (
-
-
+ ctx.font = `bold ${fontSize}px Arial, sans-serif`;
+ const words = text.split(' ');
+ const lines: string[] = [];
+ let currentLine = '';
+
+ for (const word of words) {
+ const testLine = currentLine ? `${currentLine} ${word}` : word;
+ const metrics = ctx.measureText(testLine);
+
+ if (metrics.width > maxWidth && currentLine) {
+ lines.push(currentLine);
+ currentLine = word;
+ } else {
+ currentLine = testLine;
+ }
+ }
+
+ if (currentLine) {
+ lines.push(currentLine);
+ }
+
+ return lines;
+ };
+
+ const calculateAdaptiveFontSize = (text: string, containerWidth: number, containerHeight: number): { fontSize: number; lines: string[]; isTruncated: boolean } => {
+ const MIN_FONT_SIZE = 24;
+ const MAX_FONT_SIZE = 72;
+ const LINE_HEIGHT_MULTIPLIER = 1.2;
+
+ let fontSize = MAX_FONT_SIZE;
+ let lines: string[] = [];
+ let totalTextHeight = 0;
+
+ while (fontSize >= MIN_FONT_SIZE) {
+ lines = wrapText(text, containerWidth, fontSize);
+ const lineHeight = fontSize * LINE_HEIGHT_MULTIPLIER;
+ totalTextHeight = lines.length * lineHeight;
+
+ if (totalTextHeight <= containerHeight) {
+ break;
+ }
+
+ fontSize -= 2;
+ }
+
+ let isTruncated = false;
+
+ if (fontSize < MIN_FONT_SIZE) {
+ fontSize = MIN_FONT_SIZE;
+ lines = wrapText(text, containerWidth, fontSize);
+ const lineHeight = fontSize * LINE_HEIGHT_MULTIPLIER;
+ totalTextHeight = lines.length * lineHeight;
+
+ const maxLines = Math.floor(containerHeight / lineHeight);
+ if (lines.length > maxLines) {
+ lines = lines.slice(0, maxLines);
+ if (lines[maxLines - 1]) {
+ lines[maxLines - 1] = lines[maxLines - 1].substring(0, lines[maxLines - 1].length - 3) + '...';
+ }
+ isTruncated = true;
+ }
+ }
+
+ return { fontSize, lines, isTruncated };
+ };
+
+ const renderStory = (platform: 'facebook' | 'instagram') => {
+ const STORY_WIDTH = 360;
+ const STORY_HEIGHT = 640;
+ const TEXT_BOX_HEIGHT_RATIO = 0.25;
+ const PADDING = 16;
+
+ const textBoxHeight = STORY_HEIGHT * TEXT_BOX_HEIGHT_RATIO;
+ const maxTextWidth = STORY_WIDTH - (PADDING * 2);
+ const maxTextHeight = textBoxHeight - (PADDING * 2);
+
+ const { fontSize, lines } = postText
+ ? calculateAdaptiveFontSize(postText, maxTextWidth, maxTextHeight)
+ : { fontSize: 24, lines: [] };
+
+ return (
+
+ {/* Header */}
+
+ {platform === 'instagram' ? (
+
+
+
+ ) : (
+
+
)}
+
- )}
-
- );
+
+ {/* Media */}
+ {orderedMedia.length > 0 && (
+
+ {renderMedia(orderedMedia[currentPhotoIndex], "w-full h-full object-cover")}
+ {orderedMedia.length > 1 && (
+ <>
+
+ {orderedMedia.map((_, idx) => (
+
+
+
+ >
+ )}
+
+ {/* Text overlay box - matching exact server rendering */}
+ {postText && (
+
+
+
+ {lines.map((line, idx) => (
+
{line}
+ ))}
+
+
+ )}
+
+ )}
+
+ );
+ };
+
+ const renderFacebookStory = () => renderStory('facebook');
+ const renderInstagramStory = () => renderStory('instagram');
return (