| ... | ... | @@ -51,7 +51,13 @@ describe("chat canvas layout", () => { |
| 51 | 51 | |
| 52 | 52 | it("centers chat in the whole container without a preview", () => { |
| 53 | 53 | expect(resolve(1344, null).chat).toEqual({ left: 288, width: 768, insetStart: 0, insetEnd: 0 }); |
| 54 | | expect(resolve(390, null).chat).toEqual({ left: 20, width: 350, insetStart: 0, insetEnd: 0 }); |
| 54 | expect( |
| 55 | resolveChatCanvasLayout({ |
| 56 | container: { width: 390, height: 900 }, |
| 57 | preview: null, |
| 58 | padding: 12, |
| 59 | }).chat, |
| 60 | ).toEqual({ left: 12, width: 366, insetStart: 0, insetEnd: 0 }); |
| 55 | 61 | }); |
| 56 | 62 | it("does not move chat when a bottom-right preview fits in its margin", () => { |
| 57 | 63 | const result = resolve(1600); |
| ... | ... | @@ -66,21 +72,21 @@ describe("chat canvas layout", () => { |
| 66 | 72 | }); |
| 67 | 73 | it("shrinks chat modestly after using the left margin", () => { |
| 68 | 74 | const result = resolve(1200, { ...preview, width: 480 }); |
| 69 | | expect(result.chat.width).toBe(676); |
| 70 | | expect(result.chat.left).toBe(20); |
| 75 | expect(result.chat.width).toBe(648); |
| 76 | expect(result.chat.left).toBe(48); |
| 71 | 77 | expectClear(result); |
| 72 | 78 | }); |
| 73 | 79 | it("stops leftward dragging before the player can push chat to the right", () => { |
| 74 | 80 | const centered = resolve(1344, null).chat; |
| 75 | | for (const x of [1100, 800, 672, 600, 400, 12, -100]) { |
| 81 | for (const x of [1100, 800, 700, 600, 400, 12, -100]) { |
| 76 | 82 | const result = resolve(1344, { ...preview, position: { x, y: 500 } }); |
| 77 | | expect(result.frame!.x).toBeGreaterThanOrEqual(672); |
| 83 | expect(result.frame!.x).toBeGreaterThanOrEqual(700); |
| 78 | 84 | expect(result.chat.left).toBeLessThanOrEqual(centered.left); |
| 79 | 85 | expect(result.chat.insetStart).toBe(0); |
| 80 | 86 | expect(result.chat.width).toBeGreaterThanOrEqual(640); |
| 81 | 87 | expectClear(result); |
| 82 | 88 | } |
| 83 | | expect(resolve(1344, { ...preview, position: { x: 12, y: 500 } }).frame!.x).toBe(672); |
| 89 | expect(resolve(1344, { ...preview, position: { x: 12, y: 500 } }).frame!.x).toBe(700); |
| 84 | 90 | }); |
| 85 | 91 | it("keeps a dragged player above the composer when no readable lane fits beside it", () => { |
| 86 | 92 | const result = resolve(1000, { ...preview, position: { x: 12, y: 700 } }); |
| ... | ... | @@ -171,8 +177,8 @@ describe("chat canvas layout", () => { |
| 171 | 177 | expect(shifted.overlapsDetailsCard).toBe(false); |
| 172 | 178 | expectClear(shifted); |
| 173 | 179 | |
| 174 | | const narrowed = layout(560); |
| 175 | | expect(narrowed.frame!.width).toBe(560); |
| 180 | const narrowed = layout(532); |
| 181 | expect(narrowed.frame!.width).toBe(532); |
| 176 | 182 | expect(narrowed.chat.width).toBe(656); |
| 177 | 183 | expect(narrowed.overlapsDetailsCard).toBe(false); |
| 178 | 184 | expectClear(narrowed); |
| ... | ... | @@ -182,7 +188,7 @@ describe("chat canvas layout", () => { |
| 182 | 188 | expect(grown.overlapsDetailsCard).toBe(true); |
| 183 | 189 | expect(grown.overlapsChat).toBe(false); |
| 184 | 190 | expectClear(grown); |
| 185 | | expect(layout(560)).toEqual(narrowed); |
| 191 | expect(layout(532)).toEqual(narrowed); |
| 186 | 192 | }); |
| 187 | 193 | it.each(["west", "north"] as const)( |
| 188 | 194 | "folds the card before moving a %s resize beside it", |
| ... | ... | @@ -214,7 +220,7 @@ describe("chat canvas layout", () => { |
| 214 | 220 | const density = (result: ReturnType<typeof layout>) => { |
| 215 | 221 | const card = resolveThreadDetailsCardLayout({ |
| 216 | 222 | container, |
| 217 | | lane: { padding: 20, minChatWidth: 640 }, |
| 223 | lane: { padding: 48, minChatWidth: 640 }, |
| 218 | 224 | frame: result.frame, |
| 219 | 225 | overlapsDetailsCard: result.overlapsDetailsCard, |
| 220 | 226 | }); |
| ... | ... | @@ -274,16 +280,30 @@ describe("workspace card beside chat", () => { |
| 274 | 280 | expect(withCard(1147).chat).toEqual({ left: 87, width: 736, insetStart: 0, insetEnd: 237 }); |
| 275 | 281 | }); |
| 276 | 282 | it("narrows chat only after it reaches the left padding", () => { |
| 277 | | expect(withCard(1080).chat).toMatchObject({ left: 20, width: 736 }); |
| 278 | | expect(withCard(1000).chat).toEqual({ left: 20, width: 656, insetStart: 0, insetEnd: 304 }); |
| 279 | | expect(withCard(984).chat).toMatchObject({ left: 20, width: 640 }); |
| 283 | expect(withCard(1108).chat).toMatchObject({ left: 48, width: 736 }); |
| 284 | expect(withCard(1028).chat).toEqual({ left: 48, width: 656, insetStart: 0, insetEnd: 276 }); |
| 285 | expect(withCard(1012).chat).toMatchObject({ left: 48, width: 640 }); |
| 286 | }); |
| 287 | it("reserves the marker gutter across widths where the details card fits", () => { |
| 288 | for (let width = 1012; width <= 1440; width++) { |
| 289 | const container = { width, height: 900 }; |
| 290 | const card = resolveThreadDetailsCardLayout({ |
| 291 | container, |
| 292 | lane: { padding: 48, minChatWidth: 640 }, |
| 293 | frame: null, |
| 294 | })!; |
| 295 | const chat = withCard(width).chat; |
| 296 | expect(chat.left).toBeGreaterThanOrEqual(48); |
| 297 | expect(chat.width).toBeGreaterThanOrEqual(640); |
| 298 | expect(card.x - chat.left - chat.width).toBeGreaterThanOrEqual(32); |
| 299 | } |
| 280 | 300 | }); |
| 281 | 301 | it("keeps a full-width chat clear of the card", () => { |
| 282 | 302 | expect(withCard(1147, null, 10_000).chat).toEqual({ |
| 283 | | left: 20, |
| 284 | | width: 803, |
| 303 | left: 48, |
| 304 | width: 775, |
| 285 | 305 | insetStart: 0, |
| 286 | | insetEnd: 304, |
| 306 | insetEnd: 276, |
| 287 | 307 | }); |
| 288 | 308 | }); |
| 289 | 309 | it("keeps centered chat in place when a new preview opens below the card", () => { |