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