Skip to content
Open
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
170 changes: 170 additions & 0 deletions .github/workflows/mosaic-press-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
name: Mosaic Press Preview Deploy

on:
pull_request:
types:
- opened
- reopened
- synchronize
- closed
paths:
- "mosaic-press/**"
- ".github/workflows/mosaic-press-preview.yml"
push:
branches:
- main
paths:
- "mosaic-press/**"
- ".github/workflows/mosaic-press-preview.yml"

permissions:
contents: write
pull-requests: write

concurrency:
group: mosaic-press-preview-${{ github.event.pull_request.number || github.ref_name }}
cancel-in-progress: true

jobs:
deploy-preview:
if: github.event_name == 'pull_request' && github.event.action != 'closed' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
env:
PREVIEW_DIR: previews/pr-${{ github.event.pull_request.number }}
PREVIEW_URL: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/previews/pr-${{ github.event.pull_request.number }}/
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22

- name: Verify Mosaic Press
working-directory: mosaic-press
run: npm run verify

- name: Deploy preview to gh-pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_branch: gh-pages
publish_dir: ./mosaic-press
destination_dir: ${{ env.PREVIEW_DIR }}
keep_files: true
enable_jekyll: false

- name: Comment preview URL on pull request
uses: actions/github-script@v7
env:
PREVIEW_DIR: ${{ env.PREVIEW_DIR }}
PREVIEW_URL: ${{ env.PREVIEW_URL }}
with:
script: |
const marker = "<!-- mosaic-press-preview -->";
const body = `${marker}
Mosaic Press preview deployed.

URL: ${process.env.PREVIEW_URL}
Commit: ${context.sha.slice(0, 7)}
Path: \`${process.env.PREVIEW_DIR}/\`

If this is the first deployment, enable GitHub Pages once in repository settings and point it at the \`gh-pages\` branch.`;

const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});

const existing = comments.find(
(comment) => comment.user.type === "Bot" && comment.body.includes(marker)
);

if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body
});
}

cleanup-preview:
if: github.event_name == 'pull_request' && github.event.action == 'closed' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
env:
PREVIEW_DIR: previews/pr-${{ github.event.pull_request.number }}
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Remove preview directory from gh-pages
run: |
if ! git ls-remote --exit-code --heads origin gh-pages >/dev/null 2>&1; then
echo "gh-pages branch does not exist yet."
exit 0
fi

git fetch origin gh-pages:gh-pages
git switch gh-pages

if [ ! -d "${PREVIEW_DIR}" ]; then
echo "Preview directory already removed."
exit 0
fi

rm -rf "${PREVIEW_DIR}"

if [ -z "$(git status --short)" ]; then
echo "No cleanup changes to commit."
exit 0
fi

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add -A
git commit -m "Remove Mosaic Press preview for PR #${{ github.event.pull_request.number }}"
git push origin gh-pages

deploy-stable:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
env:
STABLE_DIR: mosaic-press
STABLE_URL: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/mosaic-press/
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22

- name: Verify Mosaic Press
working-directory: mosaic-press
run: npm run verify

- name: Deploy stable build to gh-pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_branch: gh-pages
publish_dir: ./mosaic-press
destination_dir: ${{ env.STABLE_DIR }}
keep_files: true
enable_jekyll: false

- name: Print stable URL
run: echo "Stable Mosaic Press build deployed to ${STABLE_URL}"
46 changes: 46 additions & 0 deletions mosaic-press/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Mosaic Press

Standalone first-playable vertical slice for the Mosaic Press release-order mosaic puzzle.

## Run

Open `index.html` in a browser, or serve the directory with any static file server.

## Test

```bash
npm test
```

## Verify

```bash
npm run verify
```

## What Is Included

- Data-driven Mosaic Press rules for press stacks, clamps, wax tabs, paper spacers, row entry lanes, gutter overflow, no-move fail, undo, hint, restart, win, and daily commission entry
- `12` handcrafted campaign cards in `2` packs of `6`, plus `1` deterministic daily generator keyed by UTC `YYYY-MM-DD`
- Portrait-first static HTML, CSS, and JS slice with gallery wrapper, card preview ribbon, press bench, tray rows, fail modal, and reward flow
- Validator-backed authoring that checks level shape limits, right-entry gating, repeated-mechanic claims, opening productive fasteners, and at least one completion path

## Implementation Notes

- Readability limits: the slice keeps rows to `2` to `4` cells, stacks to `2` to `5`, and active hit targets to the front strip only. Dense late levels still need phone QA, especially level `11`, where visual crowding is deliberate.
- Hit-target handling: clamps, wax tabs, and spacers render as oversized buttons anchored to authored slot positions on the front strip only. Covered fasteners still render when the design expects the player to notice them, but they reject taps with local copy instead of spending a penalty.
- Solver and validator needs: the current validator uses BFS over fastener removals because strip motion and destinations are deterministic. If production later adds manual row choice, dragging, irregular shard clusters, or buffered gutter parking, the authoring pipeline will need a deeper state-space solver and better diagnostics.

## QA Focus

- Verify `Gutter Overflow` reads as fair on level `5`, level `10`, and the daily templates.
- Verify `No Move` only triggers when no exposed fastener can lead to an immediate successful placement.
- Check narrow-phone readability for fastener overlap, row entry direction, and target-card preview cells.
- Confirm daily commission unlocks after level `6` and uses UTC date rollover rather than local device midnight.

## Preview Deployment

- GitHub Actions workflow: `.github/workflows/mosaic-press-preview.yml`
- Pull requests deploy previews to `https://<owner>.github.io/<repo>/previews/pr-<number>/`
- `main` deploys the stable build to `https://<owner>.github.io/<repo>/mosaic-press/`
- The first deployment still requires enabling GitHub Pages for the `gh-pages` branch in repository settings
13 changes: 13 additions & 0 deletions mosaic-press/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Mosaic Press</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<div id="app"></div>
<script type="module" src="./src/app.js"></script>
</body>
</html>
11 changes: 11 additions & 0 deletions mosaic-press/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "mosaic-press",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"test": "node --test",
"check:app": "node --check src/app.js",
"verify": "npm test && npm run check:app"
}
}
Loading
Loading