···11+import { describe, expect, it } from "./suite.ts";
22+import { createTerm } from "../term.ts";
33+import { close, fixed, open, text } from "../ops.ts";
44+import { print } from "./print.ts";
55+66+const decode = (b: Uint8Array) => new TextDecoder().decode(b);
77+88+describe("translate", () => {
99+ it("shifts the element visually but keeps its flow slot", async () => {
1010+ let term = await createTerm({ width: 5, height: 1 });
1111+ // PROPOSED layout.translate: a post-layout visual shift that does NOT remove
1212+ // the element from flow (CSS translate). Cast so it type-checks today; the
1313+ // field is dropped at runtime against the current build.
1414+ let res = term.render([
1515+ open("row", { layout: { width: fixed(5), direction: "ltr" } }),
1616+ open("a", { layout: { translate: { x: 2, y: 0 } } as never }),
1717+ text("A"),
1818+ close(),
1919+ text("B"),
2020+ close(),
2121+ ]);
2222+ // pins: "a" reserves its flow slot at x=0, then renders shifted +2 -> x=2
2323+ expect(res.info.get("a")!.bounds.x).toBe(2);
2424+ });
2525+2626+ it("does not let the sibling reflow into the translated element's slot", async () => {
2727+ let term = await createTerm({ width: 5, height: 1 });
2828+ let res = term.render([
2929+ open("row", { layout: { width: fixed(5), direction: "ltr" } }),
3030+ open("a", { layout: { translate: { x: 2, y: 0 } } as never }),
3131+ text("A"),
3232+ close(),
3333+ text("B"),
3434+ close(),
3535+ ]);
3636+ // pins: sibling "B" stays at its flow column 1 and "A" draws at column 2,
3737+ // so the full row reads " BA" (not "B A", which removing-from-flow produces)
3838+ expect(print(decode(res.output), 5, 1)).toBe(" BA ");
3939+ });
4040+});