Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
74 changes: 54 additions & 20 deletions app/components/chat/BaseChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -308,31 +308,65 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
useEffect(() => {
const textarea = textareaRef?.current;

if (textarea) {
// Use requestAnimationFrame to ensure we measure after browser paint
requestAnimationFrame(() => {
// Reset height to auto to get accurate scrollHeight
textarea.style.height = 'auto';
if (!textarea) {
return undefined;
}

// Get the actual content height
const contentHeight = textarea.scrollHeight;
// Use requestAnimationFrame to ensure we measure after browser paint
const rafId = requestAnimationFrame(() => {
// Save current scroll position and selection BEFORE any changes
const currentScrollTop = textarea.scrollTop;
const currentScrollHeight = textarea.scrollHeight;
const currentClientHeight = textarea.clientHeight;
const currentSelectionStart = textarea.selectionStart;
const currentSelectionEnd = textarea.selectionEnd;

// Calculate new height (constrained between min and max)
const newHeight = Math.min(Math.max(contentHeight, TEXTAREA_MIN_HEIGHT), TEXTAREA_MAX_HEIGHT);
// Check if user was at the bottom BEFORE height change (within 5px threshold)
const wasAtBottom = currentScrollTop + currentClientHeight >= currentScrollHeight - 5;

// Apply the new height with !important to override any CSS
textarea.style.setProperty('height', `${newHeight}px`, 'important');
// Reset height to auto to get accurate scrollHeight
textarea.style.height = 'auto';

// Only show scrollbar when content exceeds max height
const shouldScroll = contentHeight > TEXTAREA_MAX_HEIGHT;
textarea.style.setProperty('overflow-y', shouldScroll ? 'auto' : 'hidden', 'important');
// Get the actual content height
const contentHeight = textarea.scrollHeight;

// Add debug data attributes
textarea.setAttribute('data-max-height', String(TEXTAREA_MAX_HEIGHT));
textarea.setAttribute('data-calculated-height', String(newHeight));
textarea.setAttribute('data-should-scroll', String(shouldScroll));
});
}
// Calculate new height (constrained between min and max)
const newHeight = Math.min(Math.max(contentHeight, TEXTAREA_MIN_HEIGHT), TEXTAREA_MAX_HEIGHT);

// Apply the new height with !important to override any CSS
textarea.style.setProperty('height', `${newHeight}px`, 'important');

// Only show scrollbar when content exceeds max height
const shouldScroll = contentHeight > TEXTAREA_MAX_HEIGHT;
textarea.style.setProperty('overflow-y', shouldScroll ? 'auto' : 'hidden', 'important');

// Restore scroll position
if (shouldScroll) {
if (wasAtBottom) {
// If user was at bottom, keep them at bottom (natural typing behavior)
textarea.scrollTop = textarea.scrollHeight;
} else {
// If user was scrolled up (editing middle content), maintain their position
textarea.scrollTop = currentScrollTop;
}
} else {
// If content fits within max height, restore previous scroll position
textarea.scrollTop = currentScrollTop;
}

// Restore cursor position
textarea.setSelectionRange(currentSelectionStart, currentSelectionEnd);

// Add debug data attributes
textarea.setAttribute('data-max-height', String(TEXTAREA_MAX_HEIGHT));
textarea.setAttribute('data-calculated-height', String(newHeight));
textarea.setAttribute('data-should-scroll', String(shouldScroll));
});

// Cleanup function to cancel animation frame if component unmounts
return () => {
cancelAnimationFrame(rafId);
};
}, [input, TEXTAREA_MAX_HEIGHT, chatStarted, isXlViewport]);

// State to store scroll container ref
Expand Down
2 changes: 1 addition & 1 deletion app/components/chat/Chat.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1264,7 +1264,7 @@ export const ChatImpl = memo(
const temResp = await fetchTemplateFromAPI(
starterTemplateResp.template!,
starterTemplateResp.title,
starterTemplateResp.projectRepo,
starterTemplateResp.projectRepo || 'basic-project',
signal,
).catch((e) => {
checkAborted();
Expand Down
6 changes: 5 additions & 1 deletion app/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,13 @@ export const FIXED_MODELS = {
provider: PROVIDER_LIST.find((p) => p.name === PROVIDER_NAMES.GOOGLE_VERTEX_AI)!,
model: 'gemini-2.5-flash',
},
{
provider: PROVIDER_LIST.find((p) => p.name === PROVIDER_NAMES.OPEN_ROUTER)!,
model: 'openai/gpt-5-mini',
},
{
provider: PROVIDER_LIST.find((p) => p.name === PROVIDER_NAMES.GOOGLE_VERTEX_AI)!,
model: 'gemini-2.5-flash',
model: 'gemini-3-flash-preview',
},
],
PROMPT_ENHANCER_TEMPLATE: {
Expand Down