From 3da8a0d3db54be8e56489c181ef11b28a4fc3b24 Mon Sep 17 00:00:00 2001 From: Bill Leoutsakos Date: Mon, 10 Aug 2026 10:13:27 -0700 Subject: [PATCH 1/4] fix(chat): autosize long prompts in composer --- .../user-input/components/constants.ts | 4 +-- .../prompt-editor/prompt-editor.test.tsx | 27 +++++++++++++++++++ .../home/components/user-input/user-input.tsx | 2 +- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts index 75c0b4da123..23b275e60ac 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts @@ -90,10 +90,10 @@ export const OVERLAY_CLASSES = cn( 'text-[var(--text-primary)]' ) -/** Single scroll container for the textarea + overlay; caps height and hides its scrollbar. */ +/** Single scroll container for the textarea + overlay; caps height and scrolls both together. */ export const SCROLLER_CLASSES = cn( 'relative overflow-y-auto overflow-x-hidden', - '[-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden' + '[scrollbar-gutter:stable]' ) export const SEND_BUTTON_BASE = 'h-[28px] w-[28px] rounded-full border-0 p-0 transition-colors' diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/prompt-editor.test.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/prompt-editor.test.tsx index 5b7fc95ee03..1b3965bb383 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/prompt-editor.test.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/prompt-editor.test.tsx @@ -98,6 +98,7 @@ function mountEditor() { if (!textarea) throw new Error('textarea did not render') return { + scroller: textarea.closest('.overflow-y-auto'), textarea, unmount: () => { act(() => root.unmount()) @@ -155,6 +156,32 @@ describe('PromptEditor autosize', () => { unmount() }) + it('uses a visible, stable scroll container when content exceeds the height cap', () => { + const { scroller, unmount } = mountEditor() + + expect(scroller).not.toBeNull() + expect(scroller).toHaveClass('overflow-y-auto', '[scrollbar-gutter:stable]') + expect(scroller).not.toHaveClass('[scrollbar-width:none]', '[&::-webkit-scrollbar]:hidden') + unmount() + }) + + it('shrinks the textarea again when the prompt becomes shorter', () => { + const { textarea, unmount } = mountEditor() + const valueSetter = Object.getOwnPropertyDescriptor( + window.HTMLTextAreaElement.prototype, + 'value' + )?.set + + act(() => { + contentHeight = 24 + valueSetter?.call(textarea, 'short') + textarea.dispatchEvent(new Event('input', { bubbles: true })) + }) + + expect(textarea.style.height).toBe('24px') + unmount() + }) + it('re-measures when the editor width changes so no text falls outside the textarea', () => { const { textarea, unmount } = mountEditor() settle() diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx index 810484c9d84..a1d325697a4 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx @@ -551,7 +551,7 @@ const UserInputImpl = forwardRef(function UserI placeholder='Ask Sim to ' onSubmit={handleEnterSubmit} onArrowUpOnEmpty={handleArrowUpOnEmpty} - className={isInitialView ? 'h-[56px]' : 'max-h-[200px]'} + className={isInitialView ? 'max-h-[200px] min-h-[56px]' : 'max-h-[200px]'} />
From 923e2b5330278336db65f101579258de236ec539 Mon Sep 17 00:00:00 2001 From: Bill Leoutsakos Date: Mon, 10 Aug 2026 10:21:09 -0700 Subject: [PATCH 2/4] fix(chat): refine composer scrollbar --- .../user-input/components/constants.ts | 9 +- .../prompt-editor/prompt-editor.test.tsx | 10 +- .../components/user-input/user-input.test.tsx | 137 ++++++++++++++++++ 3 files changed, 152 insertions(+), 4 deletions(-) create mode 100644 apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.test.tsx diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts index 23b275e60ac..fb52d8261d2 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts @@ -90,10 +90,15 @@ export const OVERLAY_CLASSES = cn( 'text-[var(--text-primary)]' ) -/** Single scroll container for the textarea + overlay; caps height and scrolls both together. */ +/** + * Single scroll container for the textarea + overlay. Its inset thumb stays + * quiet until hover so it does not visually merge with the send control. + */ export const SCROLLER_CLASSES = cn( 'relative overflow-y-auto overflow-x-hidden', - '[scrollbar-gutter:stable]' + '[scrollbar-color:transparent_transparent] [scrollbar-gutter:stable] [scrollbar-width:thin]', + '[&::-webkit-scrollbar]:w-1 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-transparent [&::-webkit-scrollbar-thumb:hover]:bg-[var(--scrollbar-thumb-hover-color)] [&::-webkit-scrollbar-track]:[margin-block:8px]', + 'hover:[scrollbar-color:var(--scrollbar-thumb-color)_transparent] hover:[&::-webkit-scrollbar-thumb]:bg-[var(--scrollbar-thumb-color)]' ) export const SEND_BUTTON_BASE = 'h-[28px] w-[28px] rounded-full border-0 p-0 transition-colors' diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/prompt-editor.test.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/prompt-editor.test.tsx index 1b3965bb383..c759e2c61ba 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/prompt-editor.test.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/prompt-editor.test.tsx @@ -156,11 +156,17 @@ describe('PromptEditor autosize', () => { unmount() }) - it('uses a visible, stable scroll container when content exceeds the height cap', () => { + it('uses a stable, hover-revealed scroll container when content exceeds the height cap', () => { const { scroller, unmount } = mountEditor() expect(scroller).not.toBeNull() - expect(scroller).toHaveClass('overflow-y-auto', '[scrollbar-gutter:stable]') + expect(scroller).toHaveClass( + 'overflow-y-auto', + '[scrollbar-gutter:stable]', + '[scrollbar-color:transparent_transparent]', + '[&::-webkit-scrollbar-thumb]:bg-transparent', + 'hover:[&::-webkit-scrollbar-thumb]:bg-[var(--scrollbar-thumb-color)]' + ) expect(scroller).not.toHaveClass('[scrollbar-width:none]', '[&::-webkit-scrollbar]:hidden') unmount() }) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.test.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.test.tsx new file mode 100644 index 00000000000..a186729cefd --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.test.tsx @@ -0,0 +1,137 @@ +/** + * @vitest-environment jsdom + */ +import { act } from 'react' +import { createRoot, type Root } from 'react-dom/client' +import { afterEach, describe, expect, it, vi } from 'vitest' + +vi.mock('next/navigation', () => ({ + useParams: () => ({ workspaceId: 'ws-1' }), +})) + +vi.mock('@/blocks/integration-matcher', () => ({ + mentionifyIntegrations: (text: string) => text, +})) + +vi.mock('@/hooks/use-settings-navigation', () => ({ + useSettingsNavigation: () => ({ navigateToSettings: vi.fn() }), +})) + +vi.mock('@/hooks/use-speech-to-text', () => ({ + useSpeechToText: () => ({ + isListening: false, + isSupported: false, + resetTranscript: vi.fn(), + toggleListening: vi.fn(), + }), +})) + +vi.mock('@/app/workspace/[workspaceId]/home/components/chat-surface-context', () => ({ + useChatSurface: () => ({ + userId: 'user-1', + onContextAdd: vi.fn(), + onContextRemove: vi.fn(), + }), +})) + +vi.mock( + '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks', + () => ({ + useFileAttachments: () => ({ + attachedFiles: [], + clearAttachedFiles: vi.fn(), + fileInputRef: { current: null }, + handleDragEnter: vi.fn(), + handleDragLeave: vi.fn(), + handleDragOver: vi.fn(), + handleDrop: vi.fn(), + handleFileChange: vi.fn(), + handleFileClick: vi.fn(), + handleFileSelect: vi.fn(), + isDragging: false, + processFiles: vi.fn(), + removeFile: vi.fn(), + restoreAttachedFiles: vi.fn(), + }), + }) +) + +vi.mock('@/app/workspace/[workspaceId]/home/components/user-input/components', () => ({ + AnimatedPlaceholderEffect: () => null, + AttachedFilesList: () => null, + DropOverlay: () => null, + MicButton: () => null, + PromptEditor: ({ className }: { className?: string }) => ( +
+ ), + SendButton: () => null, + usePromptEditor: () => ({ + clear: vi.fn(), + contexts: [], + focusAtEnd: vi.fn(), + getActiveContexts: () => [], + getPlainValue: () => '', + getValue: () => '', + insertResources: vi.fn(), + insertSlashTrigger: vi.fn(), + openResourceMenu: vi.fn(), + setContexts: vi.fn(), + setValue: vi.fn(), + textareaRef: { current: null }, + value: '', + }), +})) + +import { UserInput } from '@/app/workspace/[workspaceId]/home/components/user-input/user-input' + +interface RenderUserInputOptions { + isInitialView?: boolean +} + +function renderUserInput({ isInitialView }: RenderUserInputOptions = {}) { + ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true + const container = document.createElement('div') + document.body.appendChild(container) + const root: Root = createRoot(container) + + act(() => { + root.render( + + ) + }) + + return { + editor: container.querySelector('[data-testid="prompt-editor"]'), + unmount: () => { + act(() => root.unmount()) + container.remove() + }, + } +} + +describe('UserInput prompt sizing', () => { + afterEach(() => { + vi.clearAllMocks() + }) + + it('grows the initial composer from its 56px resting height to the 200px cap', () => { + const { editor, unmount } = renderUserInput() + + expect(editor).toHaveClass('min-h-[56px]', 'max-h-[200px]') + expect(editor).not.toHaveClass('h-[56px]') + unmount() + }) + + it('keeps the active composer capped at 200px without the initial-view height floor', () => { + const { editor, unmount } = renderUserInput({ isInitialView: false }) + + expect(editor).toHaveClass('max-h-[200px]') + expect(editor).not.toHaveClass('min-h-[56px]', 'h-[56px]') + unmount() + }) +}) From 3605bd728fbac178e3dca884b11a6644f6b61f2a Mon Sep 17 00:00:00 2001 From: Bill Leoutsakos Date: Mon, 10 Aug 2026 10:29:10 -0700 Subject: [PATCH 3/4] fix(chat): separate scrollbar from send control --- .../home/components/user-input/user-input.test.tsx | 4 ++-- .../[workspaceId]/home/components/user-input/user-input.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.test.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.test.tsx index a186729cefd..8cf57dbb8fd 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.test.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.test.tsx @@ -122,7 +122,7 @@ describe('UserInput prompt sizing', () => { it('grows the initial composer from its 56px resting height to the 200px cap', () => { const { editor, unmount } = renderUserInput() - expect(editor).toHaveClass('min-h-[56px]', 'max-h-[200px]') + expect(editor).toHaveClass('-mr-1.5', 'mb-2', 'max-h-[200px]', 'min-h-[56px]', 'pr-1.5') expect(editor).not.toHaveClass('h-[56px]') unmount() }) @@ -130,7 +130,7 @@ describe('UserInput prompt sizing', () => { it('keeps the active composer capped at 200px without the initial-view height floor', () => { const { editor, unmount } = renderUserInput({ isInitialView: false }) - expect(editor).toHaveClass('max-h-[200px]') + expect(editor).toHaveClass('-mr-1.5', 'mb-2', 'max-h-[200px]', 'pr-1.5') expect(editor).not.toHaveClass('min-h-[56px]', 'h-[56px]') unmount() }) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx index a1d325697a4..75dee5866db 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx @@ -551,7 +551,7 @@ const UserInputImpl = forwardRef(function UserI placeholder='Ask Sim to ' onSubmit={handleEnterSubmit} onArrowUpOnEmpty={handleArrowUpOnEmpty} - className={isInitialView ? 'max-h-[200px] min-h-[56px]' : 'max-h-[200px]'} + className={cn('-mr-1.5 mb-2 max-h-[200px] pr-1.5', isInitialView && 'min-h-[56px]')} />
From 956634d7e901698c62d11642b2d4adb7107c263a Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Mon, 10 Aug 2026 11:53:34 -0700 Subject: [PATCH 4/4] fix(chat): drop the composer scrollbar restyle, keep the autosize fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scrollbar half of this branch did not do what it claimed. Chrome ignores `::-webkit-scrollbar` customization whenever `scrollbar-width` is set, and globals.css already sets `* { scrollbar-width: thin }`, so the 4px width, the pill radius and the 8px track inset were all inert. What survived was `scrollbar-gutter: stable`, which permanently reserved the 11px thin scrollbar and pushed the prompt text 11px off-centre (measured 14px/25px left/right against 14px/14px before) even for a one-word prompt. `hover:[&::-webkit-scrollbar-thumb]:…` also compiles to `::-webkit-scrollbar-thumb:hover`, so it targeted the thumb rather than the container and silently overrode the hover colour it sat next to. Revert SCROLLER_CLASSES to the hidden scrollbar it had before, and drop the margin/padding pair and the 8px bottom margin that only existed to position that scrollbar. The actual bug fix stays: the initial view was pinned to `h-[56px]`, so a long prompt scrolled inside a two-line box. Both views now share one sizing policy — cap at 200px, autosize between — with a 56px resting floor for the hero composer. Replace the class-literal assertions with coverage that can fail. The old scrollHeight stub returned the content height unconditionally, so deleting the `height = 'auto'` collapse — the entire reason autosize can shrink — still passed every test. The stub now models the browser rule that scroll height is the greater of the text height and the pinned box, which makes both the new shrink test and the existing widen-back test go red when that collapse is removed. --- .../user-input/components/constants.ts | 9 +- .../prompt-editor/prompt-editor.test.tsx | 30 ++-- .../components/user-input/user-input.test.tsx | 137 ------------------ .../home/components/user-input/user-input.tsx | 2 +- 4 files changed, 15 insertions(+), 163 deletions(-) delete mode 100644 apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.test.tsx diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts index fb52d8261d2..75c0b4da123 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts @@ -90,15 +90,10 @@ export const OVERLAY_CLASSES = cn( 'text-[var(--text-primary)]' ) -/** - * Single scroll container for the textarea + overlay. Its inset thumb stays - * quiet until hover so it does not visually merge with the send control. - */ +/** Single scroll container for the textarea + overlay; caps height and hides its scrollbar. */ export const SCROLLER_CLASSES = cn( 'relative overflow-y-auto overflow-x-hidden', - '[scrollbar-color:transparent_transparent] [scrollbar-gutter:stable] [scrollbar-width:thin]', - '[&::-webkit-scrollbar]:w-1 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-transparent [&::-webkit-scrollbar-thumb:hover]:bg-[var(--scrollbar-thumb-hover-color)] [&::-webkit-scrollbar-track]:[margin-block:8px]', - 'hover:[scrollbar-color:var(--scrollbar-thumb-color)_transparent] hover:[&::-webkit-scrollbar-thumb]:bg-[var(--scrollbar-thumb-color)]' + '[-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden' ) export const SEND_BUTTON_BASE = 'h-[28px] w-[28px] rounded-full border-0 p-0 transition-colors' diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/prompt-editor.test.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/prompt-editor.test.tsx index c759e2c61ba..0a69d460d47 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/prompt-editor.test.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/prompt-editor.test.tsx @@ -27,6 +27,10 @@ import { usePromptEditor } from '@/app/workspace/[workspaceId]/home/components/u * stands for the scroller's content width and `contentHeight` for the height * the text wraps to at that width. Narrowing raises the content height, exactly * as rewrapping does in a browser. + * + * Scroll height is the greater of the text height and the box the element is + * pinned to, so the stub reports `contentHeight` only while the inline height + * is cleared. */ let contentHeight = 240 let editorWidth = 700 @@ -98,7 +102,6 @@ function mountEditor() { if (!textarea) throw new Error('textarea did not render') return { - scroller: textarea.closest('.overflow-y-auto'), textarea, unmount: () => { act(() => root.unmount()) @@ -136,7 +139,7 @@ describe('PromptEditor autosize', () => { get() { if (!(this instanceof HTMLTextAreaElement)) return 0 autosizeCalls++ - return contentHeight + return Math.max(contentHeight, Number.parseFloat(this.style.height) || 0) }, }) vi.stubGlobal('ResizeObserver', FakeResizeObserver) @@ -156,31 +159,22 @@ describe('PromptEditor autosize', () => { unmount() }) - it('uses a stable, hover-revealed scroll container when content exceeds the height cap', () => { - const { scroller, unmount } = mountEditor() - - expect(scroller).not.toBeNull() - expect(scroller).toHaveClass( - 'overflow-y-auto', - '[scrollbar-gutter:stable]', - '[scrollbar-color:transparent_transparent]', - '[&::-webkit-scrollbar-thumb]:bg-transparent', - 'hover:[&::-webkit-scrollbar-thumb]:bg-[var(--scrollbar-thumb-color)]' - ) - expect(scroller).not.toHaveClass('[scrollbar-width:none]', '[&::-webkit-scrollbar]:hidden') - unmount() - }) - + /** + * Guards the `height = 'auto'` collapse: a textarea pinned taller than its + * text reports the pinned height, so measuring without collapsing first can + * only ever grow the box. + */ it('shrinks the textarea again when the prompt becomes shorter', () => { const { textarea, unmount } = mountEditor() const valueSetter = Object.getOwnPropertyDescriptor( window.HTMLTextAreaElement.prototype, 'value' )?.set + if (!valueSetter) throw new Error('textarea value setter is unavailable') act(() => { contentHeight = 24 - valueSetter?.call(textarea, 'short') + valueSetter.call(textarea, 'short') textarea.dispatchEvent(new Event('input', { bubbles: true })) }) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.test.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.test.tsx deleted file mode 100644 index 8cf57dbb8fd..00000000000 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.test.tsx +++ /dev/null @@ -1,137 +0,0 @@ -/** - * @vitest-environment jsdom - */ -import { act } from 'react' -import { createRoot, type Root } from 'react-dom/client' -import { afterEach, describe, expect, it, vi } from 'vitest' - -vi.mock('next/navigation', () => ({ - useParams: () => ({ workspaceId: 'ws-1' }), -})) - -vi.mock('@/blocks/integration-matcher', () => ({ - mentionifyIntegrations: (text: string) => text, -})) - -vi.mock('@/hooks/use-settings-navigation', () => ({ - useSettingsNavigation: () => ({ navigateToSettings: vi.fn() }), -})) - -vi.mock('@/hooks/use-speech-to-text', () => ({ - useSpeechToText: () => ({ - isListening: false, - isSupported: false, - resetTranscript: vi.fn(), - toggleListening: vi.fn(), - }), -})) - -vi.mock('@/app/workspace/[workspaceId]/home/components/chat-surface-context', () => ({ - useChatSurface: () => ({ - userId: 'user-1', - onContextAdd: vi.fn(), - onContextRemove: vi.fn(), - }), -})) - -vi.mock( - '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks', - () => ({ - useFileAttachments: () => ({ - attachedFiles: [], - clearAttachedFiles: vi.fn(), - fileInputRef: { current: null }, - handleDragEnter: vi.fn(), - handleDragLeave: vi.fn(), - handleDragOver: vi.fn(), - handleDrop: vi.fn(), - handleFileChange: vi.fn(), - handleFileClick: vi.fn(), - handleFileSelect: vi.fn(), - isDragging: false, - processFiles: vi.fn(), - removeFile: vi.fn(), - restoreAttachedFiles: vi.fn(), - }), - }) -) - -vi.mock('@/app/workspace/[workspaceId]/home/components/user-input/components', () => ({ - AnimatedPlaceholderEffect: () => null, - AttachedFilesList: () => null, - DropOverlay: () => null, - MicButton: () => null, - PromptEditor: ({ className }: { className?: string }) => ( -
- ), - SendButton: () => null, - usePromptEditor: () => ({ - clear: vi.fn(), - contexts: [], - focusAtEnd: vi.fn(), - getActiveContexts: () => [], - getPlainValue: () => '', - getValue: () => '', - insertResources: vi.fn(), - insertSlashTrigger: vi.fn(), - openResourceMenu: vi.fn(), - setContexts: vi.fn(), - setValue: vi.fn(), - textareaRef: { current: null }, - value: '', - }), -})) - -import { UserInput } from '@/app/workspace/[workspaceId]/home/components/user-input/user-input' - -interface RenderUserInputOptions { - isInitialView?: boolean -} - -function renderUserInput({ isInitialView }: RenderUserInputOptions = {}) { - ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true - const container = document.createElement('div') - document.body.appendChild(container) - const root: Root = createRoot(container) - - act(() => { - root.render( - - ) - }) - - return { - editor: container.querySelector('[data-testid="prompt-editor"]'), - unmount: () => { - act(() => root.unmount()) - container.remove() - }, - } -} - -describe('UserInput prompt sizing', () => { - afterEach(() => { - vi.clearAllMocks() - }) - - it('grows the initial composer from its 56px resting height to the 200px cap', () => { - const { editor, unmount } = renderUserInput() - - expect(editor).toHaveClass('-mr-1.5', 'mb-2', 'max-h-[200px]', 'min-h-[56px]', 'pr-1.5') - expect(editor).not.toHaveClass('h-[56px]') - unmount() - }) - - it('keeps the active composer capped at 200px without the initial-view height floor', () => { - const { editor, unmount } = renderUserInput({ isInitialView: false }) - - expect(editor).toHaveClass('-mr-1.5', 'mb-2', 'max-h-[200px]', 'pr-1.5') - expect(editor).not.toHaveClass('min-h-[56px]', 'h-[56px]') - unmount() - }) -}) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx index 75dee5866db..1f78a7199af 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx @@ -551,7 +551,7 @@ const UserInputImpl = forwardRef(function UserI placeholder='Ask Sim to ' onSubmit={handleEnterSubmit} onArrowUpOnEmpty={handleArrowUpOnEmpty} - className={cn('-mr-1.5 mb-2 max-h-[200px] pr-1.5', isInitialView && 'min-h-[56px]')} + className={cn('max-h-[200px]', isInitialView && 'min-h-[56px]')} />