fix(web): preserve agent composer shrink sizing
This commit is contained in:
@@ -46,6 +46,23 @@ function dayLabel(date: Date | null) {
|
||||
today.setDate(today.getDate() - 1);
|
||||
return dayKey(date) === dayKey(today) ? "Yesterday" : dayFormat.format(date);
|
||||
}
|
||||
function measureComposerHeight(textarea: HTMLTextAreaElement) {
|
||||
const measurement = textarea.cloneNode(false) as HTMLTextAreaElement;
|
||||
measurement.removeAttribute("id");
|
||||
measurement.removeAttribute("name");
|
||||
measurement.setAttribute("aria-hidden", "true");
|
||||
measurement.tabIndex = -1;
|
||||
measurement.value = textarea.value;
|
||||
Object.assign(measurement.style, {
|
||||
position: "fixed", left: "-10000px", top: "0", visibility: "hidden", pointerEvents: "none",
|
||||
width: `${textarea.getBoundingClientRect().width}px`, height: "0px", minHeight: "0px",
|
||||
maxHeight: "none", overflow: "hidden", resize: "none",
|
||||
});
|
||||
textarea.parentElement?.appendChild(measurement);
|
||||
const height = measurement.scrollHeight;
|
||||
measurement.remove();
|
||||
return height;
|
||||
}
|
||||
function toolName(value: unknown) {
|
||||
return typeof value === "string" ? value.replace(/_/g, " ") : "Tool";
|
||||
}
|
||||
@@ -288,12 +305,10 @@ export function AgentConversation({ room, onOpenSidebar, isVisible = true }: Pro
|
||||
useLayoutEffect(() => {
|
||||
const textarea = textareaRef.current;
|
||||
if (!textarea) return;
|
||||
// scrollHeight reflects the full draft even when the textarea is already
|
||||
// capped. Avoid collapsing a focused field to its minimum height while
|
||||
// measuring: the browser may scroll the enclosing transcript to reveal its
|
||||
// caret, and no ResizeObserver can correct that transient scroll once the
|
||||
// field returns to the same capped height.
|
||||
textarea.style.height = `${Math.min(156, Math.max(46, textarea.scrollHeight))}px`;
|
||||
// Measure offscreen: collapsing a focused textarea can make its scroll
|
||||
// parent reveal the caret, while its own scrollHeight cannot measure a
|
||||
// smaller draft until its current height has been removed.
|
||||
textarea.style.height = `${Math.min(156, Math.max(46, measureComposerHeight(textarea)))}px`;
|
||||
}, [draft]);
|
||||
|
||||
const updateDraft = (value: string) => {
|
||||
|
||||
@@ -107,11 +107,19 @@ try {
|
||||
if (process.env.RECORD_ONLY !== '1') for (const result of all) {
|
||||
for (const sample of result.bottom) assert.ok(Math.abs(sample.gap) <= 1,
|
||||
`bottom pin drift at ${result.config.width}px/${result.config.dpr}x: ${JSON.stringify(sample)}`);
|
||||
const byLabel = Object.fromEntries(result.bottom.map(sample => [sample.label, sample]));
|
||||
assert.equal(byLabel['wrapped-and-capped'].textareaHeight, 156, `textarea cap changed: ${JSON.stringify(result.bottom)}`);
|
||||
assert.ok(byLabel.shrink.textareaHeight <= 48 && byLabel.cleared.textareaHeight <= 48,
|
||||
`textarea did not shrink with the draft: ${JSON.stringify(result.bottom)}`);
|
||||
assert.equal(byLabel.cleared.clientHeight, byLabel.empty.clientHeight,
|
||||
`composer did not return to its empty height: ${JSON.stringify(result.bottom)}`);
|
||||
const before = result.nonbottom.find(sample => sample.before)?.before;
|
||||
for (const sample of result.nonbottom.filter(sample => sample.label?.startsWith('nonbottom-'))) {
|
||||
assert.equal(sample.anchorId, before.anchorId, `nonbottom anchor changed: ${JSON.stringify({ before, sample })}`);
|
||||
assert.ok(Math.abs(sample.anchorTop - before.anchorTop) <= 1, `nonbottom transcript jumped: ${JSON.stringify({ before, sample })}`);
|
||||
}
|
||||
const nonbottomShrink = result.nonbottom.find(sample => sample.label === 'nonbottom-shrink');
|
||||
assert.ok(nonbottomShrink.textareaHeight <= 48, `nonbottom textarea did not shrink: ${JSON.stringify(nonbottomShrink)}`);
|
||||
}
|
||||
process.stdout.write(JSON.stringify({ browser, all }, null, 2) + '\n');
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user