Skip to content

Commit b317f64

Browse files
committed
Update Home.jsx
1 parent 64c675d commit b317f64

File tree

1 file changed

+51
-42
lines changed

1 file changed

+51
-42
lines changed

src/components/Home.jsx

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ const ChatMessage = React.memo(function ChatMessage({ sender, message, timestamp
169169
</div>
170170
{/* Action buttons for AI only, moved below content */}
171171
{!isUser && (
172-
<div className="flex items-center gap-1.5 flex-shrink-0 p-2 pt-0 border-t border-border/50 mt-1 pt-1">
172+
<div className="flex items-center gap-1.5 flex-shrink-0 p-2 border-t border-border/50 mt-1 pt-1">
173173
<button
174174
className={iconButtonStyle}
175175
title={copied ? t('InstructionModel:copied') : t('InstructionModel:copy')}
@@ -525,6 +525,7 @@ export default function InstructionModel(properties) {
525525
const PARAGRAPH_SPACING = 10; // Extra space for paragraph breaks (\n\n)
526526
const CODE_BLOCK_PADDING = 40; // Padding/margin around code block + copy button space
527527
const CODE_LINE_HEIGHT = 20; // Approx height of a line within a code block
528+
const ROW_VERTICAL_PADDING = 16; // Buffer to avoid cramped layout after measurement
528529

529530
// --- Helper to estimate code block height ---
530531
const estimateCodeBlockHeight = (codeContent) => {
@@ -538,23 +539,26 @@ export default function InstructionModel(properties) {
538539
// Handle typing indicator height
539540
if (index === chatHistory.length) {
540541
const showTypingIndicator = isAiResponding && chatHistory.length > 0 && chatHistory[chatHistory.length - 1]?.sender === 'user';
541-
return showTypingIndicator ? 60 : 0; // Adjusted typing indicator height
542+
return showTypingIndicator ? (messageHeights.current[index] ?? 60) : 0; // Prefer measured height when available
542543
}
543544

544545
// Handle hidden first AI message
545546
if (index === 0 && chatHistory[0]?.sender === 'ai') {
546-
// Ensure cache is 0 if it wasn't already
547547
if (messageHeights.current[index] !== 0) {
548548
messageHeights.current[index] = 0;
549-
// No need to reset here, height is fixed at 0
550549
}
551550
return 0;
552551
}
553552

553+
const cachedHeight = messageHeights.current[index];
554+
if (cachedHeight != null) {
555+
return cachedHeight;
556+
}
557+
554558
// Estimate height based on content for other messages
555559
const messageData = chatHistory[index];
556560
if (!messageData || !messageData.message) {
557-
return BASE_MESSAGE_HEIGHT; // Default minimum
561+
return BASE_MESSAGE_HEIGHT + ROW_VERTICAL_PADDING; // Default minimum
558562
}
559563

560564
const message = messageData.message;
@@ -594,35 +598,36 @@ export default function InstructionModel(properties) {
594598
estimatedHeight += totalCodeBlockHeight;
595599

596600
// Use cached height if available and larger than estimate (measurement is king)
597-
const cachedHeight = messageHeights.current[index];
598-
if (cachedHeight && cachedHeight > estimatedHeight) {
599-
return cachedHeight;
600-
}
601-
602-
// Return the estimated height, ensuring a minimum
603-
// Add a small safety buffer
604-
return Math.max(estimatedHeight, BASE_MESSAGE_HEIGHT) + 10;
601+
// Return the estimated height, ensuring a minimum, plus padding buffer
602+
return Math.max(estimatedHeight, BASE_MESSAGE_HEIGHT) + ROW_VERTICAL_PADDING;
605603

606604
}, [chatHistory, isAiResponding]); // Dependencies: chatHistory and isAiResponding
607605

608606
// --- VariableSizeList row measurer ---
609607
const measureRow = useCallback((index, node) => {
610-
if (node && node.offsetHeight && messageHeights.current[index] !== node.offsetHeight) {
611-
// Ensure height is at least a minimum value to avoid collapse issues
612-
const newHeight = Math.max(node.offsetHeight, 20); // Ensure a minimum height
613-
if (messageHeights.current[index] !== newHeight) {
614-
messageHeights.current[index] = newHeight;
615-
if (chatListRef.current) {
616-
// Use requestAnimationFrame to avoid potential state update loops during measurement
617-
requestAnimationFrame(() => {
618-
if (chatListRef.current) {
619-
chatListRef.current.resetAfterIndex(index, false); // Use false to avoid immediate scroll jump
620-
}
621-
});
622-
}
608+
if (!node) return;
609+
610+
// Keep hidden first AI message collapsed
611+
if (index === 0 && chatHistory[0]?.sender === 'ai') {
612+
if (messageHeights.current[index] !== 0) {
613+
messageHeights.current[index] = 0;
614+
}
615+
return;
616+
}
617+
618+
const rawHeight = node.scrollHeight || node.offsetHeight || 0;
619+
if (!rawHeight) return;
620+
621+
const measuredHeight = Math.max(rawHeight + ROW_VERTICAL_PADDING, BASE_MESSAGE_HEIGHT);
622+
if (messageHeights.current[index] !== measuredHeight) {
623+
messageHeights.current[index] = measuredHeight;
624+
if (chatListRef.current) {
625+
requestAnimationFrame(() => {
626+
chatListRef.current?.resetAfterIndex(index, false);
627+
});
623628
}
624629
}
625-
}, []); // Dependencies removed
630+
}, [chatHistory]);
626631

627632
// --- Scroll to bottom if autoScroll ---
628633
useEffect(() => {
@@ -795,8 +800,8 @@ export default function InstructionModel(properties) {
795800
// 1. Handle initial loading indicator (Only if the very first message is AI and loading)
796801
if (index === 0 && isInitialAiResponsePhase) {
797802
return (
798-
<div ref={node => measureRow(index, node)} style={style}>
799-
<div className="flex text-left justify-center gap-2 p-4 text-muted-foreground">
803+
<div style={style}>
804+
<div ref={node => measureRow(index, node)} className="flex text-left justify-center gap-2 p-4 text-muted-foreground">
800805
<span>{t('InstructionModel:loading')}</span>
801806
</div>
802807
</div>
@@ -808,8 +813,10 @@ export default function InstructionModel(properties) {
808813
if (index === chatHistory.length && showTypingIndicator) {
809814
// Use a fixed, known key for the typing indicator
810815
return (
811-
<div key="typing-indicator" ref={node => measureRow(index, node)} style={style}>
812-
<ChatMessage sender="ai" message={t("InstructionModel:typing")} timestamp={new Date()} />
816+
<div key="typing-indicator" style={style}>
817+
<div ref={node => measureRow(index, node)}>
818+
<ChatMessage sender="ai" message={t("InstructionModel:typing")} timestamp={new Date()} />
819+
</div>
813820
</div>
814821
);
815822
}
@@ -838,17 +845,19 @@ export default function InstructionModel(properties) {
838845
const key = `${msg.sender}-${msg.timestamp?.getTime() || index}-${index}`;
839846

840847
return (
841-
<div key={key} ref={node => measureRow(index, node)} style={style}>
842-
<ChatMessage
843-
sender={msg.sender}
844-
message={msg.message}
845-
timestamp={msg.timestamp}
846-
canRegenerate={canRegen}
847-
onRegenerate={() => handleRegenerate(index)}
848-
onDelete={() => handleDeleteMessage(index)}
849-
// Pass isFirstAiMessage prop if needed by ChatMessage, though it's unused currently
850-
// isFirstAiMessage={index === firstAiIndex}
851-
/>
848+
<div key={key} style={style}>
849+
<div ref={node => measureRow(index, node)}>
850+
<ChatMessage
851+
sender={msg.sender}
852+
message={msg.message}
853+
timestamp={msg.timestamp}
854+
canRegenerate={canRegen}
855+
onRegenerate={() => handleRegenerate(index)}
856+
onDelete={() => handleDeleteMessage(index)}
857+
// Pass isFirstAiMessage prop if needed by ChatMessage, though it's unused currently
858+
// isFirstAiMessage={index === firstAiIndex}
859+
/>
860+
</div>
852861
</div>
853862
);
854863
}

0 commit comments

Comments
 (0)