diff --git a/apps/web/src/components/chat/chatCanvasLayout.test.ts b/apps/web/src/components/chat/chatCanvasLayout.test.ts index ea99128a73d0..60afe86b70b5 100644 --- a/apps/web/src/components/chat/chatCanvasLayout.test.ts +++ b/apps/web/src/components/chat/chatCanvasLayout.test.ts @@ -70,6 +70,21 @@ describe("chat canvas layout", () => { expect(result.chat.left).toBe(20); expectClear(result); }); + it.each([900, 1000, 1004])("allows dragging across a %spx chat canvas", (width) => { + const start = resolve(width, { ...preview, position: { x: width - 332, y: 200 } }).frame!; + const moved = resolve(width, { + ...preview, + position: { x: start.x - 300, y: start.y }, + lastInteraction: "drag", + }); + expect(moved.frame).toEqual({ ...start, x: start.x - 300 }); + const returned = resolve(width, { + ...preview, + position: { x: start.x, y: start.y }, + lastInteraction: "drag", + }); + expect(returned.frame).toEqual(start); + }); it("stops leftward dragging before the player can push chat to the right", () => { const centered = resolve(1344, null).chat; for (const x of [1100, 800, 672, 600, 400, 12, -100]) { @@ -85,7 +100,7 @@ describe("chat canvas layout", () => { it("keeps a dragged player above the composer when no readable lane fits beside it", () => { const result = resolve(1000, { ...preview, position: { x: 12, y: 700 } }); expect(result.chat).toEqual(resolve(1000, null).chat); - expect(result.frame!.x).toBe(668); + expect(result.frame!.x).toBe(12); expect(result.frame!.y + result.frame!.height).toBeLessThanOrEqual(708); }); it("allows message overlap on narrow screens while excluding the composer", () => { @@ -253,7 +268,7 @@ describe("chat canvas layout", () => { }); expect(result.overlapsDetailsCard).toBe(true); expect(result.overlapsChat).toBe(true); - expect(result.frame).toEqual({ x: 272, y: 396, width: 1300, height: 400 }); + expect(result.frame).toEqual({ x: 12, y: 396, width: 1300, height: 400 }); }); }); diff --git a/apps/web/src/components/chat/chatCanvasLayout.ts b/apps/web/src/components/chat/chatCanvasLayout.ts index 714b9e286402..5460f490257d 100644 --- a/apps/web/src/components/chat/chatCanvasLayout.ts +++ b/apps/web/src/components/chat/chatCanvasLayout.ts @@ -63,10 +63,13 @@ export function resolveChatCanvasLayout({ let overlapsChat = false; if (preview && container.width > 0 && container.height > 0) { frame = resolvePreviewMiniPlayerFrame({ ...preview, container }); - // Dragging stops at a readable chat lane on the left. Resizing can still - // consume that space when the container requires message overlap. + // Reserve a readable chat lane only while it leaves room to drag. Otherwise + // the container's right edge would pin the preview at every pointer position. const minimumPreviewX = - preview.lastInteraction === "resize" ? GAP : padding + minChatWidth + GAP; + preview.lastInteraction === "resize" || + container.width <= padding + minChatWidth + frame.width + GAP * 2 + ? GAP + : padding + minChatWidth + GAP; // New players start beside the composer, with the workspace card above them. if (preview.position === null) frame = { ...frame, y: container.height - frame.height - GAP }; if (preview.lastInteraction === "resize" && composerHeight > 0) {