Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 19 additions & 19 deletions .github/workflows/Phuzzle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
name: 🧩 Quality + Build
runs-on: ubuntu-latest
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

permissions:
contents: read
Expand All @@ -28,12 +28,12 @@ jobs:

steps:
- name: ⬇️ Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: 🧰 Setup Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@v6.4.0
with:
node-version: "24"
cache: npm
Expand Down Expand Up @@ -67,7 +67,7 @@ jobs:
run: npm run test

- name: 📤 Upload production build
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: phuzzle-dist
path: dist
Expand All @@ -79,7 +79,7 @@ jobs:
runs-on: ubuntu-latest
needs: quality
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
PW_USE_EXISTING_BUILD: "1"
PW_USE_EDGE: "1"

Expand All @@ -89,10 +89,10 @@ jobs:

steps:
- name: ⬇️ Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6

- name: 🧰 Setup Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version: "24"
cache: npm
Expand All @@ -102,7 +102,7 @@ jobs:
run: npm ci

- name: 📥 Download production build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: phuzzle-dist
path: dist
Expand All @@ -115,7 +115,7 @@ jobs:

- name: 📤 Upload Playwright artifacts
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: playwright-smoke-artifacts
path: |
Expand All @@ -129,7 +129,7 @@ jobs:
runs-on: ubuntu-latest
needs: quality
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
PW_USE_EXISTING_BUILD: "1"

defaults:
Expand All @@ -138,10 +138,10 @@ jobs:

steps:
- name: ⬇️ Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6

- name: 🧰 Setup Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version: "24"
cache: npm
Expand All @@ -151,7 +151,7 @@ jobs:
run: npm ci

- name: 📥 Download production build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: phuzzle-dist
path: dist
Expand All @@ -164,7 +164,7 @@ jobs:

- name: 📤 Upload Playwright artifacts
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: playwright-layout-artifacts
path: |
Expand All @@ -178,7 +178,7 @@ jobs:
runs-on: ubuntu-latest
needs: quality
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

permissions:
contents: read
Expand All @@ -191,10 +191,10 @@ jobs:

steps:
- name: ⬇️ Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6

- name: 🧰 Setup Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version: "24"
cache: npm
Expand All @@ -204,7 +204,7 @@ jobs:
run: npm ci

- name: 📥 Download production build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: phuzzle-dist
path: dist
Expand All @@ -216,7 +216,7 @@ jobs:

- name: 📤 Upload Lighthouse reports
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: lhci-reports
path: lhci-reports
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/vercel-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6

- name: Setup Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@v6.4.0
with:
node-version: "24"
cache: npm
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,34 +280,46 @@

@media (min-width: 820px) and (max-width: 1366px) and (min-height: 650px) and (max-height: 860px) and (pointer: coarse) and (orientation: landscape) {
.header {
padding: 5px 10px;
gap: 6px;
padding: 3px 8px;
gap: 4px;
min-height: 34px;
}

.quickFilters,
.scrollIndicator {
display: none;
}

.randomBtn {
min-width: 34px;
min-height: 34px;
padding: 5px;
}

.segment button {
min-height: 34px;
padding: 4px 8px;
}

.scrollerWrap {
min-height: 62px;
padding: 4px 8px 6px;
min-height: 46px;
padding: 2px 8px 4px;
}

.scroller,
.trayCompact .scroller,
.trayExtraCompact .scroller {
padding: 4px 8px;
padding: 2px 8px;
}

.trayCompact .thumbWrap {
width: 52px;
height: 52px;
width: 44px;
height: 44px;
}

.trayCompact .pieceButton,
.trayCompact .blankSlot {
min-width: 56px;
min-height: 56px;
min-width: 48px;
min-height: 48px;
}
}
18 changes: 10 additions & 8 deletions src/app/puzzle/canvas/render/renderTrayPiece.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ export function renderTrayPiece(
scale: number = 0.5,
options: RenderTrayPieceOptions = {},
): HTMLCanvasElement {
const CANVAS_PAD = 4;
const maxDim = Math.max(piece.w, piece.h);
const scaledSize = Math.ceil(maxDim * scale);
const canvasSize = scaledSize + CANVAS_PAD * 2;
const OUTLINE_GUTTER = 8;
const rot90 = piece.rotation === 90 || piece.rotation === 270;
const renderW = rot90 ? piece.h : piece.w;
const renderH = rot90 ? piece.w : piece.h;
const canvasW = Math.ceil(renderW * scale) + OUTLINE_GUTTER * 2;
const canvasH = Math.ceil(renderH * scale) + OUTLINE_GUTTER * 2;

const canvas = document.createElement("canvas");
canvas.width = canvasSize;
canvas.height = canvasSize;
canvas.width = canvasW;
canvas.height = canvasH;

const ctx = canvas.getContext("2d");
if (!ctx) return canvas;
Expand All @@ -54,8 +56,8 @@ export function renderTrayPiece(
return canvas;
}

const canvasCenterX = canvasSize / 2;
const canvasCenterY = canvasSize / 2;
const canvasCenterX = canvasW / 2;
const canvasCenterY = canvasH / 2;
ctx.translate(canvasCenterX, canvasCenterY);
ctx.scale(scale, scale);
ctx.rotate((piece.rotation * Math.PI) / 180);
Expand Down
24 changes: 16 additions & 8 deletions src/app/responsive.responsive.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ test.describe("Responsive smoke", () => {

expect(boardBox).not.toBeNull();
expect(trayBox).not.toBeNull();
expect(boardBox?.width ?? 0).toBeGreaterThanOrEqual(560);
expect(trayBox?.y ?? 0).toBeGreaterThanOrEqual(
(boardBox?.y ?? 0) + (boardBox?.height ?? 0) - 2,
);
Expand Down Expand Up @@ -228,6 +229,9 @@ test.describe("Responsive smoke", () => {

const tray = page.getByRole("list");
await expect(tray).toBeVisible({ timeout: 15000 });
await expect(tray.getByRole("button", { name: /place piece/i }).first()).toBeVisible({
timeout: 15000,
});

const hasCoarsePointer = await page.evaluate(
() => window.matchMedia("(pointer: coarse)").matches,
Expand All @@ -237,16 +241,20 @@ test.describe("Responsive smoke", () => {
await expect(page.getByRole("button", { name: /scroll right/i })).toHaveCount(0);
}

await expect
.poll(
async () =>
tray.evaluate((node) => {
const el = node as HTMLDivElement;
return Math.max(0, el.scrollWidth - el.clientWidth);
}),
{ timeout: 15000 },
)
.toBeGreaterThan(0);

const right = await tray.evaluate((node) => {
const el = node as HTMLDivElement;
const paddingStart = 12;
const paddingEnd = 12;
const row = el.firstElementChild as HTMLElement | null;
const contentWidth =
row && row.offsetWidth > 0
? paddingStart + row.offsetWidth + paddingEnd
: el.scrollWidth;
const max = Math.max(0, contentWidth - el.clientWidth);
const max = Math.max(0, el.scrollWidth - el.clientWidth);
const before = el.scrollLeft;
el.scrollTo({ left: max });
el.scrollBy({ left: 2000 });
Expand Down
1 change: 1 addition & 0 deletions src/app/screens/Play/PlayScreen.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@
await openCompletionOptions(page);
await expect(
page.getByRole("menuitem", { name: SHARE_RESULT_MENU_ITEM }),
).toBeVisible({ timeout: 10000 });

Check failure on line 175 in src/app/screens/Play/PlayScreen.e2e.spec.ts

View workflow job for this annotation

GitHub Actions / 🎭 E2E smoke

[firefox] › src/app/screens/Play/PlayScreen.e2e.spec.ts:167:3 › Play screen › mobile completion options menu lists share actions

1) [firefox] › src/app/screens/Play/PlayScreen.e2e.spec.ts:167:3 › Play screen › mobile completion options menu lists share actions Error: expect(locator).toBeVisible() failed Locator: getByRole('menuitem', { name: /share your result/i }) Expected: visible Timeout: 10000ms Error: element(s) not found Call log: - Expect "toBeVisible" with timeout 10000ms - waiting for getByRole('menuitem', { name: /share your result/i }) 173 | await expect( 174 | page.getByRole("menuitem", { name: SHARE_RESULT_MENU_ITEM }), > 175 | ).toBeVisible({ timeout: 10000 }); | ^ 176 | await expect(page.getByRole("menuitem", { name: CHALLENGE_MENU_ITEM })).toBeVisible({ 177 | timeout: 10000, 178 | }); at /home/runner/work/phuzzle/phuzzle/src/app/screens/Play/PlayScreen.e2e.spec.ts:175:7
await expect(page.getByRole("menuitem", { name: CHALLENGE_MENU_ITEM })).toBeVisible({
timeout: 10000,
});
Expand Down Expand Up @@ -394,7 +394,7 @@

await page.getByRole("button", { name: /new puzzle/i }).click();

await expect(page.getByRole("dialog", { name: /choose category/i })).toBeVisible({

Check failure on line 397 in src/app/screens/Play/PlayScreen.e2e.spec.ts

View workflow job for this annotation

GitHub Actions / 🎭 E2E smoke

[firefox] › src/app/screens/Play/PlayScreen.e2e.spec.ts:385:3 › Play screen › starting a new puzzle from play applies the newly selected grid

2) [firefox] › src/app/screens/Play/PlayScreen.e2e.spec.ts:385:3 › Play screen › starting a new puzzle from play applies the newly selected grid Error: expect(locator).toBeVisible() failed Locator: getByRole('dialog', { name: /choose category/i }) Expected: visible Timeout: 15000ms Error: element(s) not found Call log: - Expect "toBeVisible" with timeout 15000ms - waiting for getByRole('dialog', { name: /choose category/i }) 395 | await page.getByRole("button", { name: /new puzzle/i }).click(); 396 | > 397 | await expect(page.getByRole("dialog", { name: /choose category/i })).toBeVisible({ | ^ 398 | timeout: 15000, 399 | }); 400 | at /home/runner/work/phuzzle/phuzzle/src/app/screens/Play/PlayScreen.e2e.spec.ts:397:74
timeout: 15000,
});

Expand Down Expand Up @@ -647,6 +647,7 @@
expect(
Math.abs((liveBoardBox?.width ?? 0) - (liveBoardBox?.height ?? 0)),
).toBeLessThanOrEqual(2);
expect(liveBoardBox?.width ?? 0).toBeGreaterThanOrEqual(560);
expect(
(trayBox?.y ?? Number.POSITIVE_INFINITY) + (trayBox?.height ?? 0),
).toBeLessThanOrEqual(834);
Expand Down
36 changes: 20 additions & 16 deletions src/app/screens/Play/styles/PlayScreen.board.base.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
.playBody {
--play-board-stage-size: min(
var(--play-board-tray-width, min(100%, 760px)),
calc(var(--app-vh-fill, 100dvh) - 338px)
calc(var(--app-vh-fill, 100dvh) - 286px)
);
}

Expand All @@ -56,31 +56,35 @@
--play-board-tray-gap: 14px;
--play-board-stage-size: min(
var(--play-board-tray-width, min(100%, 820px)),
calc(var(--app-vh-fill, 100dvh) - 384px)
calc(var(--app-vh-fill, 100dvh) - 264px)
);
padding-top: 12px;
padding-top: 8px;
}

.playBody .main {
height: calc(var(--play-board-stage-size) + 34px);
max-height: calc(var(--play-board-stage-size) + 34px);
height: auto;
max-height: none;
padding-top: 0;
padding-bottom: 0;
}

.playBody .trayArea .trayInDock,
.playBody .trayArea .trayInDock.trayWrapLarge {
height: clamp(104px, 13vh, 138px);
max-height: min(138px, 18vh);
min-height: 92px;
height: clamp(92px, 11vh, 116px);
max-height: min(116px, 15vh);
min-height: 88px;
}
}

@media (min-width: 601px) and (max-width: 1366px) and (pointer: coarse) and (orientation: landscape) {
.playBody {
--play-board-tray-width: min(100%, 760px);
--play-board-tray-gap: 12px;
padding-top: 8px;
--play-board-tray-width: min(100%, 960px);
--play-board-tray-gap: 10px;
--play-board-stage-size: min(
var(--play-board-tray-width, min(100%, 960px)),
calc(var(--app-vh-fill, 100dvh) - 214px)
);
padding-top: 4px;
}

.playBody .main {
Expand All @@ -95,11 +99,11 @@
max-width: min(100%, var(--play-board-tray-width));
}

.playBody .trayArea .tray,
.playBody .trayArea .tray.trayWrapLarge {
height: clamp(88px, 12vh, 112px);
max-height: min(112px, 14vh);
min-height: 82px;
.playBody .trayArea .trayInDock,
.playBody .trayArea .trayInDock.trayWrapLarge {
height: clamp(82px, 10vh, 98px);
max-height: min(98px, 12vh);
min-height: 78px;
}
}

Expand Down
Loading
Loading