mirror of
https://github.com/warmbly/warmbly.git
synced 2026-09-13 00:03:42 +00:00
feat: decide the AI edit's No change signal from the value the composer will actually hold rather than from the model's answer, since a rewrite whose every added character falls past the body-length cap leaves the textarea exactly as it was and reporting Rewritten over an unchanged body is the one thing that signal exists to prevent
This commit is contained in:
@@ -241,10 +241,15 @@ export default function TextareaAIEdit({
|
||||
// go back on before anything is written or compared: what lands in
|
||||
// the box IS what "did it change?" is answered from (issue #432).
|
||||
const applied = restoreEdges(target.text, text);
|
||||
setChanged(applied !== target.text);
|
||||
const prefix = prevValue.slice(0, target.start);
|
||||
const suffix = prevValue.slice(target.end);
|
||||
const cap = (s: string) => (maxLen ? s.slice(0, maxLen) : s);
|
||||
// Against the value that will actually be in the box, not against
|
||||
// the model's answer: a rewrite that falls past the composer's cap
|
||||
// leaves the body exactly as it was, and "Rewritten" over an
|
||||
// unchanged body is the thing this signal exists to prevent.
|
||||
const settled = cap(prefix + applied + suffix);
|
||||
setChanged(settled !== prevValue);
|
||||
typewriter.run(
|
||||
applied,
|
||||
(partial) => {
|
||||
@@ -263,7 +268,6 @@ export default function TextareaAIEdit({
|
||||
// What the box holds, not what the model sent: maxLen can
|
||||
// cut the tail off, and a range recorded past the end would
|
||||
// have Undo and Again working on text that is not there.
|
||||
const settled = cap(prefix + applied + suffix);
|
||||
const end = Math.min(target.start + applied.length, settled.length);
|
||||
const newRange = { start: target.start, end, text: settled.slice(target.start, end) };
|
||||
lastRun.current = { instruction, prevValue, start: target.start, origEnd: target.end };
|
||||
|
||||
@@ -44,13 +44,13 @@ import TextareaAIEdit from "./TextareaAIEdit";
|
||||
|
||||
const MAX = 20;
|
||||
|
||||
function Host({ initial }: { initial: string }) {
|
||||
function Host({ initial, max = MAX }: { initial: string; max?: number }) {
|
||||
const ref = React.useRef<HTMLTextAreaElement>(null);
|
||||
const [value, setValue] = React.useState(initial);
|
||||
return (
|
||||
<>
|
||||
<textarea ref={ref} value={value} onChange={(e) => setValue(e.target.value)} />
|
||||
<TextareaAIEdit textareaRef={ref} value={value} onChange={setValue} maxLen={MAX} />
|
||||
<TextareaAIEdit textareaRef={ref} value={value} onChange={setValue} maxLen={max} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -102,6 +102,33 @@ describe("TextareaAIEdit against the composer's length cap", () => {
|
||||
expect(sent?.text).toBe("two");
|
||||
});
|
||||
|
||||
it("says No change when the cap swallows the rewrite whole", async () => {
|
||||
const { container } = render(
|
||||
<QueryClientProvider client={new QueryClient()}>
|
||||
<Host initial="abcdefg" max={7} />
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
const ta = container.querySelector("textarea")!;
|
||||
await act(async () => {
|
||||
ta.focus();
|
||||
ta.setSelectionRange(5, 7);
|
||||
document.dispatchEvent(new Event("selectionchange"));
|
||||
});
|
||||
// Everything this adds lands past the cap, so the body cannot move.
|
||||
reply = { text: "fg extra", credits_charged: 1, tokens_used: 10 };
|
||||
await act(async () => {
|
||||
fireEvent.mouseDown(await screen.findByText("Edit with AI"));
|
||||
});
|
||||
const input = await screen.findByPlaceholderText("Tell AI how to change it…");
|
||||
await act(async () => {
|
||||
fireEvent.change(input, { target: { value: "expand" } });
|
||||
fireEvent.keyDown(input, { key: "Enter" });
|
||||
});
|
||||
expect(ta.value).toBe("abcdefg");
|
||||
await screen.findByText("No change");
|
||||
expect(screen.queryByText("Rewritten")).toBeNull();
|
||||
});
|
||||
|
||||
it("leaves Undo pointing at the words that were selected, not a derived range", async () => {
|
||||
const initial = "one two three";
|
||||
const { container } = render(
|
||||
|
||||
Reference in New Issue
Block a user