authorgravatar for 136640686+MatthewFeroz@users.noreply.github.comMatthew Feroz <136640686+MatthewFeroz@users.noreply.github.com> 2026-10-10 23:36:13-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-11 01:36:13-05:00
log0cb38403feb8f2bbc29dc78dea1649af696b6f08
tree8d17600c3a5e6a8a0c325d3822b14e25d02c273b
parent5047ee78858bfdeb254c7386e2d427d3bccb8430
signature Signed by PGP key B5690EEEBB952194

fix(web): unpin preview dragging on narrow chat canvases (#15556)


2 files changed, 23 insertions(+), 5 deletions(-)

apps/web/src/components/chat/chatCanvasLayout.test.ts+17-2
......@@ -76,6 +76,21 @@ describe("chat canvas layout", () => {
7676 expect(result.chat.left).toBe(48);
7777 expectClear(result);
7878 });
79 it.each([900, 1000, 1004])("allows dragging across a %spx chat canvas", (width) => {
80 const start = resolve(width, { ...preview, position: { x: width - 332, y: 200 } }).frame!;
81 const moved = resolve(width, {
82 ...preview,
83 position: { x: start.x - 300, y: start.y },
84 lastInteraction: "drag",
85 });
86 expect(moved.frame).toEqual({ ...start, x: start.x - 300 });
87 const returned = resolve(width, {
88 ...preview,
89 position: { x: start.x, y: start.y },
90 lastInteraction: "drag",
91 });
92 expect(returned.frame).toEqual(start);
93 });
7994 it("stops leftward dragging before the player can push chat to the right", () => {
8095 const centered = resolve(1344, null).chat;
8196 for (const x of [1100, 800, 700, 600, 400, 12, -100]) {
......@@ -91,7 +106,7 @@ describe("chat canvas layout", () => {
91106 it("keeps a dragged player above the composer when no readable lane fits beside it", () => {
92107 const result = resolve(1000, { ...preview, position: { x: 12, y: 700 } });
93108 expect(result.chat).toEqual(resolve(1000, null).chat);
94 expect(result.frame!.x).toBe(668);
109 expect(result.frame!.x).toBe(12);
95110 expect(result.frame!.y + result.frame!.height).toBeLessThanOrEqual(708);
96111 });
97112 it("allows message overlap on narrow screens while excluding the composer", () => {
......@@ -299,7 +314,7 @@ describe("chat canvas layout", () => {
299314 });
300315 expect(result.overlapsDetailsCard).toBe(true);
301316 expect(result.overlapsChat).toBe(true);
302 expect(result.frame).toEqual({ x: 272, y: 396, width: 1300, height: 400 });
317 expect(result.frame).toEqual({ x: 12, y: 396, width: 1300, height: 400 });
303318 });
304319});
305320
apps/web/src/components/chat/chatCanvasLayout.ts+6-3
......@@ -66,10 +66,13 @@ export function resolveChatCanvasLayout({
6666 let overlapsChat = false;
6767 if (preview && container.width > 0 && container.height > 0) {
6868 frame = resolvePreviewMiniPlayerFrame({ ...preview, container });
69 // Dragging stops at a readable chat lane on the left. Resizing can still
70 // consume that space when the container requires message overlap.
69 // Reserve a readable chat lane only while it leaves room to drag. Otherwise
70 // the container's right edge would pin the preview at every pointer position.
7171 const minimumPreviewX =
72 preview.lastInteraction === "resize" ? GAP : padding + minChatWidth + GAP;
72 preview.lastInteraction === "resize" ||
73 container.width <= padding + minChatWidth + frame.width + GAP * 2
74 ? GAP
75 : padding + minChatWidth + GAP;
7376 // New players start beside the composer, with the workspace card above them.
7477 if (preview.position === null) frame = { ...frame, y: container.height - frame.height - GAP };
7578 if (preview.lastInteraction === "resize" && composerHeight > 0) {