Skip to content

Commit

Permalink
fix(ai): adjust chat input height dynamically
Browse files Browse the repository at this point in the history
So far the height of the chat input field only was increased with Ctrl+Enter, but not when the lines broke due to their length, or when the view was resized.

With this change, we adapt the height of the input also during normal typing when the content wraps into a new line. Moreover, we add a short transition when adjusting the height to make it look smoother.
  • Loading branch information
planger committed Nov 11, 2024
1 parent bc4acd4 commit ccbfead
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 20 deletions.
55 changes: 35 additions & 20 deletions packages/ai-chat-ui/src/browser/chat-input-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,17 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu
const lastRequest = allRequests.length === 0 ? undefined : allRequests[allRequests.length - 1];

const createInputElement = async () => {
const padding = 8;
const lineHeight = 20;
const maxHeight = 240;
const resource = await props.untitledResourceResolver.createUntitledResource('', CHAT_VIEW_LANGUAGE_EXTENSION);
const editor = await props.editorProvider.createInline(resource.uri, editorContainerRef.current!, {
language: CHAT_VIEW_LANGUAGE_EXTENSION,
// Disable code lens, inlay hints and hover support to avoid console errors from other contributions
codeLens: false,
inlayHints: { enabled: 'off' },
hover: { enabled: false },
autoSizing: true,
autoSizing: false, // we handle the sizing ourselves
scrollBeyondLastLine: false,
scrollBeyondLastColumn: 0,
minHeight: 1,
Expand All @@ -144,8 +147,8 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu
scrollbar: { horizontal: 'hidden' },
automaticLayout: true,
lineNumbers: 'off',
lineHeight: 20,
padding: { top: 8 },
lineHeight,
padding: { top: padding },
suggest: {
showIcons: true,
showSnippets: false,
Expand All @@ -158,8 +161,23 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu
stickyScroll: { enabled: false },
});

editor.getControl().onDidChangeModelContent(() => {
layout();
if (editorContainerRef.current) {
editorContainerRef.current.style.height = (lineHeight + (2 * padding)) + 'px';
}

const updateEditorHeight = () => {
if (editorContainerRef.current) {
const contentHeight = editor.getControl().getContentHeight() + padding;
editorContainerRef.current.style.height = `${Math.min(contentHeight, maxHeight)}px`;
}
};
editor.getControl().onDidChangeModelContent(updateEditorHeight);
const resizeObserver = new ResizeObserver(updateEditorHeight);
if (editorContainerRef.current) {
resizeObserver.observe(editorContainerRef.current);
}
editor.getControl().onDidDispose(() => {
resizeObserver.disconnect();
});

editor.getControl().onContextMenu(e =>
Expand Down Expand Up @@ -193,19 +211,6 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu
if (editorRef.current) {
editorRef.current.document.textEditorModel.setValue('');
}
};

function layout(): void {
if (editorRef.current === undefined) {
return;
}
const hiddenClass = 'hidden';
const editor = editorRef.current;
if (editor.document.textEditorModel.getValue().length > 0) {
placeholderRef.current?.classList.add(hiddenClass);
} else {
placeholderRef.current?.classList.remove(hiddenClass);
}
}

const onKeyDown = React.useCallback((event: React.KeyboardEvent) => {
Expand All @@ -218,10 +223,20 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu
}
}, [props.isEnabled]);

const handleInputFocus = () => {
placeholderRef.current?.classList.add('hidden');
};

const handleInputBlur = () => {
if (!editorRef.current?.getControl().getValue()) {
placeholderRef.current?.classList.remove('hidden');
}
};

return <div className='theia-ChatInput'>
<div className='theia-ChatInput-Editor-Box'>
<div className='theia-ChatInput-Editor' ref={editorContainerRef} onKeyDown={onKeyDown}>
<div ref={placeholderRef} className='theia-ChatInput-Editor-Placeholder'>Enter your question</div>
<div className='theia-ChatInput-Editor' ref={editorContainerRef} onKeyDown={onKeyDown} onFocus={handleInputFocus} onBlur={handleInputBlur}>
<div ref={placeholderRef} className='theia-ChatInput-Editor-Placeholder'>Ask a question</div>
</div>
</div>
<div className="theia-ChatInputOptions">
Expand Down
1 change: 1 addition & 0 deletions packages/ai-chat-ui/src/browser/style/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ div:last-child > .theia-ChatNode {
display: flex;
flex-direction: column-reverse;
overflow: hidden;
transition: height 0.05s ease-in-out;
}

.theia-ChatInput-Editor:has(.monaco-editor.focused) {
Expand Down

0 comments on commit ccbfead

Please sign in to comment.