refactor: standardize status badges and fix composer whitespace handling

This commit is contained in:
Mohamed Boudra
2026-08-16 19:14:04 +02:00
parent 3136117906
commit 90c97c1b1a
16 changed files with 457 additions and 163 deletions
@@ -57,7 +57,7 @@ export function hasDraftContent(input: {
text: string;
attachments: UserComposerAttachment[];
}): boolean {
return input.text.trim().length > 0 || input.attachments.length > 0;
return input.text.length > 0 || input.attachments.length > 0;
}
export function areAttachmentsEqual(input: {
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { resolveDraftKey } from "./input-draft-core";
import { hasDraftContent, resolveDraftKey } from "./input-draft-core";
import {
buildDraftCommandConfig,
resolveEffectiveComposerModelId,
@@ -26,6 +26,12 @@ describe("resolveDraftKey", () => {
});
});
describe("hasDraftContent", () => {
it("preserves whitespace-only text while the user is editing", () => {
expect(hasDraftContent({ text: "\n\n\n\n\n", attachments: [] })).toBe(true);
});
});
describe("resolveEffectiveComposerModelId", () => {
it("returns the selected model trimmed", () => {
expect(
@@ -8,6 +8,7 @@ interface Args {
onHeight: (height: number) => void;
}
export function useComposerHeightMirror(_args: Args): void {
export function useComposerHeightMirror(_args: Args): (_value: string) => void {
// No-op on native: onContentSizeChange drives height natively.
return () => undefined;
}
@@ -37,7 +37,7 @@ export function useComposerHeightMirror({
minHeight,
maxHeight,
onHeight,
}: Args): void {
}: Args): (value: string) => void {
const paramsRef = useRef({ value, minHeight, maxHeight, onHeight });
paramsRef.current = { value, minHeight, maxHeight, onHeight };
@@ -70,34 +70,36 @@ export function useComposerHeightMirror({
};
}, []);
const measure = useCallback(() => {
const mirror = mirrorRef.current;
const source = textareaRef.current;
if (!mirror || !source || typeof window === "undefined") return;
if (!(source instanceof HTMLElement)) return;
const measure = useCallback(
(text: string) => {
const mirror = mirrorRef.current;
const source = textareaRef.current;
if (!mirror || !source || typeof window === "undefined") return;
if (!(source instanceof HTMLElement)) return;
const cs = window.getComputedStyle(source);
const ms = mirror.style;
for (const prop of COPIED_STYLES) {
ms[prop] = cs[prop];
}
ms.width = `${source.clientWidth}px`;
const cs = window.getComputedStyle(source);
const ms = mirror.style;
for (const prop of COPIED_STYLES) {
ms[prop] = cs[prop];
}
ms.width = `${source.clientWidth}px`;
const {
value: currentValue,
minHeight: currentMinHeight,
maxHeight: currentMaxHeight,
onHeight: currentOnHeight,
} = paramsRef.current;
// Trailing newline is collapsed by textarea measurement — pad with a space.
mirror.value = currentValue.endsWith("\n") ? `${currentValue} ` : currentValue;
const {
minHeight: currentMinHeight,
maxHeight: currentMaxHeight,
onHeight: currentOnHeight,
} = paramsRef.current;
// Trailing newline is collapsed by textarea measurement — pad with a space.
mirror.value = text.endsWith("\n") ? `${text} ` : text;
const next = Math.max(currentMinHeight, Math.min(currentMaxHeight, mirror.scrollHeight));
currentOnHeight(next);
}, [textareaRef]);
const next = Math.max(currentMinHeight, Math.min(currentMaxHeight, mirror.scrollHeight));
currentOnHeight(next);
},
[textareaRef],
);
useLayoutEffect(() => {
measure();
measure(value);
}, [maxHeight, minHeight, value, measure]);
useEffect(() => {
@@ -109,9 +111,11 @@ export function useComposerHeightMirror({
const nextWidth = source.clientWidth;
if (Math.abs(nextWidth - previousWidth) < 1) return;
previousWidth = nextWidth;
measure();
measure(paramsRef.current.value);
});
observer.observe(source);
return () => observer.disconnect();
}, [textareaRef, measure]);
return measure;
}
+3 -2
View File
@@ -1586,7 +1586,7 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(
setBoundedInputHeight(inputHeightRef.current);
}, [setBoundedInputHeight]);
useComposerHeightMirror({
const measureComposerHeight = useComposerHeightMirror({
value,
textareaRef: webTextareaRef,
minHeight: MIN_INPUT_HEIGHT,
@@ -1689,10 +1689,11 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(
const handleInputChange = useCallback(
(nextValue: string) => {
valueRef.current = nextValue;
measureComposerHeight(nextValue);
updateLiveTextPresence(nextValue);
onChangeText(nextValue);
},
[onChangeText, updateLiveTextPresence],
[measureComposerHeight, onChangeText, updateLiveTextPresence],
);
const handleInputFocus = useCallback(() => {