diff --git a/src/renderer/src/components/NewWorkspaceComposerCard.test.tsx b/src/renderer/src/components/NewWorkspaceComposerCard.test.tsx index 16a7b82be..33fc74098 100644 --- a/src/renderer/src/components/NewWorkspaceComposerCard.test.tsx +++ b/src/renderer/src/components/NewWorkspaceComposerCard.test.tsx @@ -871,3 +871,42 @@ describe('NewWorkspaceComposerCard folder task source mode', () => { expect(recipeChanges).toEqual([null]) }) }) + +describe('NewWorkspaceComposerCard note sizing', () => { + // Sizing is layout-driven (field-sizing) rather than a JS measure pass, and happy-dom + // has no layout engine, so these assert the class contract that produces the growth. + afterEach(() => { + act(() => current?.root.unmount()) + current?.container.remove() + current = null + }) + + function findNoteTextarea(container: HTMLElement): HTMLTextAreaElement { + const label = [...container.querySelectorAll('label')].find( + (candidate) => candidate.textContent?.trim() === 'Note' + ) + const textarea = label?.parentElement?.querySelector('textarea') + expect(textarea).toBeTruthy() + return textarea as HTMLTextAreaElement + } + + it('sizes from the note value, so a PR prefill written straight to state still shows in full', () => { + // #10575: the prefill never fires an input event, so nothing but the value can drive height. + current = renderCard({ + advancedOpen: true, + note: `PR #10575 — ${'a note title long enough to wrap over several lines '.repeat(3)}` + }) + + expect(findNoteTextarea(current.container).className).toContain('[field-sizing:content]') + }) + + it('keeps a note past the height cap readable instead of clipping it', () => { + current = renderCard({ advancedOpen: true, note: 'a'.repeat(4000) }) + + const { className } = findNoteTextarea(current.container) + expect(className).toContain('max-h-40') + expect(className).toContain('overflow-y-auto') + expect(className).toContain('scrollbar-sleek') + expect(className).not.toContain('overflow-hidden') + }) +}) diff --git a/src/renderer/src/components/NewWorkspaceComposerCard.tsx b/src/renderer/src/components/NewWorkspaceComposerCard.tsx index f01350a6d..478494916 100644 --- a/src/renderer/src/components/NewWorkspaceComposerCard.tsx +++ b/src/renderer/src/components/NewWorkspaceComposerCard.tsx @@ -1001,18 +1001,15 @@ export default function NewWorkspaceComposerCard({ value={note} onChange={(event) => onNoteChange(event.target.value)} onPaste={handleNotePaste} - onInput={(event) => { - // Why: reset then size to content so short notes stay compact and long ones grow without a scrollbar until max-h clamps. - const ta = event.currentTarget - ta.style.height = 'auto' - ta.style.height = `${ta.scrollHeight}px` - }} placeholder={translate( 'auto.components.NewWorkspaceComposerCard.090cfedeb4', 'Write a note' )} rows={1} - className="w-full min-w-0 resize-none overflow-hidden rounded-md border border-input bg-transparent px-3 py-1.5 text-sm shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 max-h-40" + // Why (#10575): field-sizing:content grows the note with its value, so a PR/MR + // prefill written straight to state sizes like typed text — an onInput measure + // pass never saw it. Past the max-h clamp the sleek scrollbar keeps it readable. + className="w-full min-w-0 resize-none overflow-y-auto scrollbar-sleek rounded-md border border-input bg-transparent px-3 py-1.5 text-sm shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [field-sizing:content] max-h-40" />