fix(desktop): close terminal composition edge cases

Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz>
Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz>
This commit is contained in:
npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta
2026-08-01 21:52:24 -04:00
parent ef0decaef5
commit 48b711cc6a
2 changed files with 32 additions and 19 deletions
@@ -131,6 +131,8 @@ test("mounted IME paths neither toggle nor emit preedit text", async () => {
fireEvent.input(input, { target: { value: "か" } });
assert.deepEqual(calls.input, []);
fireEvent.compositionEnd(input, { data: "か" });
assert.deepEqual(calls.input, []);
fireEvent.input(input, { target: { value: "か" } });
assert.deepEqual(calls.input, ["か"]);
});
@@ -148,6 +150,15 @@ test("welcome survives non-intersecting frame and dismisses on input or intersec
},
],
},
{
line: 8,
spans: [
{
style: { fg: 0, bg: 0, flags: 0 },
clusters: [{ column: 70, text: "x", width: 1 }],
},
],
},
],
viewport: { columns: 80, generation: 1, screenLines: 24 },
};
@@ -157,8 +168,13 @@ test("welcome survives non-intersecting frame and dismisses on input or intersec
toggleChord();
const input = view.getByLabelText("Terminal input");
fireEvent.input(input, { target: { value: "x" } });
await waitFor(() =>
assert.equal(view.container.querySelector(".buzz-terminal-welcome"), null),
await waitFor(
() =>
assert.equal(
view.container.querySelector(".buzz-terminal-welcome"),
null,
),
{ timeout: 1_000 },
);
cleanup();
@@ -171,7 +187,7 @@ test("welcome survives non-intersecting frame and dismisses on input or intersec
spans: [
{
style: { fg: 0, bg: 0, flags: 0 },
clusters: [{ column: 0, text: "x", width: 1 }],
clusters: [{ column: 30, text: "x", width: 1 }],
},
],
},
@@ -67,18 +67,18 @@ function welcomeRect(frame: TerminalFrame) {
}
function frameDamageRects(frame: TerminalFrame) {
return frame.rows
.filter((row) =>
row.spans.some((span) =>
span.clusters.some((cluster) => cluster.text.trim().length > 0),
),
)
.map((row) => ({
top: row.line,
left: 0,
bottom: row.line + 1,
right: frame.viewport.columns,
}));
return frame.rows.flatMap((row) =>
row.spans.flatMap((span) =>
span.clusters
.filter((cluster) => cluster.text.trim().length > 0)
.map((cluster) => ({
top: row.line,
left: cluster.column,
bottom: row.line + 1,
right: cluster.column + cluster.width,
})),
),
);
}
export function TerminalSubstrate({
@@ -388,13 +388,10 @@ export function TerminalSubstrate({
autoCapitalize="off"
autoComplete="off"
className="buzz-terminal-input"
onCompositionEnd={(event) => {
onCompositionEnd={() => {
handoffRef.current = reduceHandoff(handoffRef.current, {
type: "composition-end",
}).state;
const committed = event.currentTarget.value;
event.currentTarget.value = "";
sendInput(committed);
}}
onCompositionStart={() => {
handoffRef.current = reduceHandoff(handoffRef.current, {