From b363c188b15ee5f5aa4ee31632d204d4d64f5675 Mon Sep 17 00:00:00 2001 From: Lorenzo Barbieri Date: Wed, 11 Nov 2026 21:07:33 +0100 Subject: [PATCH 1/5] Add 2048 canvas extension Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/plugin/marketplace.json | 6 + docs/README.plugins.md | 1 + extensions/keyboard-2048/app.mjs | 209 ++++++++++++++++++ extensions/keyboard-2048/assets/preview.png | Bin 0 -> 164002 bytes extensions/keyboard-2048/extension.mjs | 73 +++++++ extensions/keyboard-2048/game.mjs | 134 ++++++++++++ extensions/keyboard-2048/game.test.mjs | 224 ++++++++++++++++++++ extensions/keyboard-2048/index.html | 74 +++++++ extensions/keyboard-2048/server.mjs | 170 +++++++++++++++ extensions/keyboard-2048/server.test.mjs | 163 ++++++++++++++ extensions/keyboard-2048/style.css | 117 ++++++++++ plugins/keyboard-2048/README.md | 18 ++ plugins/keyboard-2048/plugin.json | 30 +++ 13 files changed, 1219 insertions(+) create mode 100644 extensions/keyboard-2048/app.mjs create mode 100644 extensions/keyboard-2048/assets/preview.png create mode 100644 extensions/keyboard-2048/extension.mjs create mode 100644 extensions/keyboard-2048/game.mjs create mode 100644 extensions/keyboard-2048/game.test.mjs create mode 100644 extensions/keyboard-2048/index.html create mode 100644 extensions/keyboard-2048/server.mjs create mode 100644 extensions/keyboard-2048/server.test.mjs create mode 100644 extensions/keyboard-2048/style.css create mode 100644 plugins/keyboard-2048/README.md create mode 100644 plugins/keyboard-2048/plugin.json diff --git a/.github/plugin/marketplace.json b/.github/plugin/marketplace.json index 3715da3ec8..e718ec6c11 100644 --- a/.github/plugin/marketplace.json +++ b/.github/plugin/marketplace.json @@ -1788,6 +1788,12 @@ "sha": "2771046616a3c6bcd62916ed072f71feb5afd222" } }, + { + "name": "keyboard-2048", + "source": "plugins/keyboard-2048", + "description": "Play 2048 in a Copilot canvas with keyboard and touch controls, undo, and session saves.", + "version": "1.0.0" + }, { "name": "kotlin-mcp-development", "source": "plugins/kotlin-mcp-development", diff --git a/docs/README.plugins.md b/docs/README.plugins.md index 128cbbdc20..169af31b6b 100644 --- a/docs/README.plugins.md +++ b/docs/README.plugins.md @@ -84,6 +84,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-plugins) for guidelines on how t | [java-mcp-development](../plugins/java-mcp-development/README.md) | Build Model Context Protocol (MCP) servers in Java with the official MCP Java SDK 2.x. Includes a skill that generates a server project and an expert agent for guidance. | 2 items | java, mcp, model-context-protocol, mcp-java-sdk, server-development, spring-ai | | [java-modernization-studio](../plugins/java-modernization-studio/README.md) | Drive the GitHub Copilot App Modernization for Java workflow from an interactive canvas: environment readiness, repo assessment, prioritized plan and progress, validation gates, and one-click predefined-task runs grounded in the repo's real artifacts. | 1 items | app-modernization, assessment-dashboard, azure-migration, java-modernization, legacy-java, modernization-cockpit, validation-gates | | [jupyter-notebooks](../plugins/jupyter-notebooks/README.md) | Create, edit, run, save, and checkpoint Jupyter notebooks in an interactive Copilot canvas. | 1 items | data-analysis, interactive-notebooks, jupyter, python | +| [keyboard-2048](../plugins/keyboard-2048/README.md) | Play 2048 in a Copilot canvas with keyboard and touch controls, undo, and session saves. | 1 items | 2048, copilot-canvas, game, keyboard, puzzle, undo | | [kotlin-mcp-development](../plugins/kotlin-mcp-development/README.md) | Complete toolkit for building Model Context Protocol (MCP) servers in Kotlin using the official io.modelcontextprotocol:kotlin-sdk library. Includes instructions for best practices, a prompt for generating servers, and an expert chat mode for guidance. | 2 items | kotlin, mcp, model-context-protocol, kotlin-multiplatform, server-development, ktor | | [mcp-m365-copilot](../plugins/mcp-m365-copilot/README.md) | Comprehensive collection for building declarative agents with Model Context Protocol integration for Microsoft 365 Copilot | 4 items | mcp, m365-copilot, declarative-agents, api-plugins, model-context-protocol, adaptive-cards | | [modernization-workflow](../plugins/modernization-workflow/README.md) | Visualize and guide the GitHub Copilot Modernize CLI Assess, Plan, and Execute workflow for .NET, Java, and C++ application repositories. | 1 items | app-modernization, assessment-dashboard, azure-migration, cpp-modernization, dotnet-modernization, java-modernization, modernization-canvas, modernize-cli | diff --git a/extensions/keyboard-2048/app.mjs b/extensions/keyboard-2048/app.mjs new file mode 100644 index 0000000000..59e6933d15 --- /dev/null +++ b/extensions/keyboard-2048/app.mjs @@ -0,0 +1,209 @@ +const token = new URLSearchParams(location.search).get("token"); +const endpoint = path => `${path}?token=${encodeURIComponent(token)}`; +const board = document.querySelector("#board"); +const errorBox = document.querySelector("#error"); +const undoButton = document.querySelector("#undo"); +const dialog = document.querySelector("#restart-dialog"); +const number = new Intl.NumberFormat("en-US"); +let state; +let pending = Promise.resolve(); +let connected = false; +let storageError = false; +const reducedMotion = matchMedia("(prefers-reduced-motion: reduce)"); +const animations = new Set(); +const motionLayer = document.createElement("div"); +motionLayer.className = "motion-layer"; +motionLayer.setAttribute("aria-hidden", "true"); +board.parentElement.append(motionLayer); +const cells = Array.from({ length: 16 }, () => { + const cell = document.createElement("div"); + cell.className = "tile"; + board.append(cell); + return cell; +}); + +function showError(message) { + errorBox.textContent = message; + errorBox.hidden = false; +} + +function clearMotion() { + for (const animation of animations) { + animation.onfinish = null; + animation.cancel(); + } + animations.clear(); + motionLayer.replaceChildren(); + cells.forEach(cell => cell.classList.remove("in-flight")); +} + +function animateMove(motion) { + const merged = new Set(motion.merges); + const moving = motion.movements.filter(tile => tile.from !== tile.to || merged.has(tile.to)); + if (!moving.length) return; + // Layout offsets exclude the spawn/pulse transforms on the final board. + const rects = cells.map(cell => ({ + left: cell.offsetLeft, top: cell.offsetTop, + width: cell.offsetWidth, height: cell.offsetHeight, + })); + const arrivals = new Set(moving.map(tile => tile.to)); + arrivals.forEach(index => cells[index].classList.add("in-flight")); + let remaining = moving.length; + for (const tile of moving) { + const source = rects[tile.from]; + const target = rects[tile.to]; + const ghost = document.createElement("div"); + ghost.className = `tile motion-tile${tile.value >= 1024 ? " large" : ""}`; + ghost.dataset.level = Math.min(11, Math.log2(tile.value)); + ghost.textContent = tile.value; + Object.assign(ghost.style, { + left: `${source.left}px`, top: `${source.top}px`, + width: `${source.width}px`, height: `${source.height}px`, + }); + motionLayer.append(ghost); + const animation = ghost.animate([ + { transform: "translate(0, 0)" }, + { transform: `translate(${target.left - source.left}px, ${target.top - source.top}px)` }, + ], { duration: 120, easing: "ease-out", fill: "forwards" }); + animations.add(animation); + animation.onfinish = () => { + if (--remaining) return; + clearMotion(); + for (const index of merged) { + const pulse = cells[index].animate([ + { transform: "scale(1)" }, { transform: "scale(1.08)" }, { transform: "scale(1)" }, + ], { duration: 100, easing: "ease-out" }); + animations.add(pulse); + pulse.onfinish = () => { animations.delete(pulse); pulse.cancel(); }; + } + }; + } +} + +function render(next) { + // SSE and HTTP can deliver the same update, or an older HTTP reply after newer SSE. + if (state?.update && next.update?.stream === state.update.stream + && next.update.revision <= state.update.revision) return; + const motion = state?.update && next.update?.stream === state.update.stream + && next.update.revision === state.update.revision + 1 + && next.update.motion?.from.every((value, index) => value === state.board[index]) + ? next.update.motion : null; + clearMotion(); + cells.forEach((cell, index) => { + const value = next.board[index]; + const spawned = value && index === next.spawnedIndex; + cell.className = `tile${value >= 1024 ? " large" : ""}`; + if (spawned) cell.classList.add("spawned"); + cell.dataset.level = value ? Math.min(11, Math.log2(value)) : 0; + cell.textContent = value || ""; + cell.setAttribute("aria-label", `Row ${Math.floor(index / 4) + 1}, column ${index % 4 + 1}: ${value || "empty"}${spawned ? " (new tile)" : ""}`); + }); + document.querySelector("#score").textContent = number.format(next.score); + document.querySelector("#best").textContent = number.format(next.best); + document.querySelector("#moves").textContent = `${number.format(next.moves)} ${next.moves === 1 ? "move" : "moves"}`; + document.querySelector("#status").textContent = next.gameOver + ? "No moves left. Undo or start a new game." + : next.won ? "2048 reached! You can keep playing." : "Your move."; + document.querySelector("#game-over").hidden = !next.gameOver; + undoButton.disabled = !next.canUndo; + state = next; + if (motion && !reducedMotion.matches) animateMove(motion); +} + +reducedMotion.addEventListener("change", clearMotion); +window.addEventListener("resize", clearMotion); + +function command(action, direction) { + pending = pending.then(async () => { + const response = await fetch(endpoint("/command"), { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ action, direction }), + }); + const result = await response.json(); + if (!response.ok) throw new Error(result.error || `HTTP error ${response.status}`); + storageError = false; + if (connected) errorBox.hidden = true; + render(result); + }).catch(error => { + storageError = true; + showError(`Move not confirmed: ${error.message}`); + }); + return pending; +} + +function requestRestart() { + if (!dialog.open) { + dialog.returnValue = ""; + dialog.showModal(); + } +} +dialog.addEventListener("close", () => { + if (dialog.returnValue === "restart") void command("new"); + board.focus({ preventScroll: true }); +}); +document.querySelector("#new").addEventListener("click", requestRestart); +undoButton.addEventListener("click", () => { void command("undo"); board.focus({ preventScroll: true }); }); +document.querySelectorAll("[data-direction]").forEach(button => { + button.addEventListener("click", () => { + void command("move", button.dataset.direction); + board.focus({ preventScroll: true }); + }); +}); +const keys = { + ArrowLeft: "left", ArrowRight: "right", ArrowUp: "up", ArrowDown: "down", + a: "left", d: "right", w: "up", s: "down", +}; +document.addEventListener("keydown", event => { + if (dialog.open || event.isComposing || event.altKey + || event.target.closest("input, textarea, select, [contenteditable='true']")) return; + const key = event.key.length === 1 ? event.key.toLowerCase() : event.key; + if ((event.ctrlKey || event.metaKey) && key === "z" && !event.shiftKey) { + event.preventDefault(); + if (!event.repeat) void command("undo"); + return; + } + if (event.ctrlKey || event.metaKey) return; + if (keys[key]) { + event.preventDefault(); + void command("move", keys[key]); + } else if (key === "u") { + event.preventDefault(); + if (!event.repeat) void command("undo"); + } else if (key === "n") { + event.preventDefault(); + if (!event.repeat) requestRestart(); + } +}); +let start; +board.addEventListener("pointerdown", event => { + board.focus({ preventScroll: true }); + start = { x: event.clientX, y: event.clientY, id: event.pointerId }; + board.setPointerCapture(event.pointerId); +}); +board.addEventListener("pointercancel", () => { start = undefined; }); +board.addEventListener("pointerup", event => { + if (!start || start.id !== event.pointerId) return; + const dx = event.clientX - start.x; + const dy = event.clientY - start.y; + start = undefined; + if (Math.max(Math.abs(dx), Math.abs(dy)) < 28) return; + const direction = Math.abs(dx) > Math.abs(dy) ? (dx > 0 ? "right" : "left") : (dy > 0 ? "down" : "up"); + void command("move", direction); +}); + +const events = new EventSource(endpoint("/events")); +events.addEventListener("message", event => { + try { render(JSON.parse(event.data)); } + catch (error) { showError(`Invalid game state: ${error.message}`); } +}); +events.addEventListener("open", () => { + connected = true; + if (!storageError) errorBox.hidden = true; +}); +events.addEventListener("error", () => { + connected = false; + showError("Connection lost. Reconnecting automatically; your game is still saved."); +}); +window.addEventListener("pagehide", () => { clearMotion(); events.close(); }); +board.focus({ preventScroll: true }); diff --git a/extensions/keyboard-2048/assets/preview.png b/extensions/keyboard-2048/assets/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..3509aa7f77456ed8d1e6a196ea4c96d009cdc9f6 GIT binary patch literal 164002 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
+
+

KEYBOARD EDITION

2048.

+
+
SCORE0
+
BEST0
+
+
+

Use arrows or WASD to move tiles.
Merge equal tiles to reach 2048.

+
+ + +
+
+
+ +
+
Loading game...0 moves
+ +
+ + + + +
+
+

← ↑ ↓ → or W A S D to move

+

U or Ctrl/Cmd + Z to undo a move

+

Game saved in this session. Click the board to focus keyboard controls.

+
+ +

Start again?

+

Your current game will be replaced. Your best score will be kept.

+
+ + +
+
+
+ + diff --git a/extensions/keyboard-2048/server.mjs b/extensions/keyboard-2048/server.mjs new file mode 100644 index 0000000000..d19c0729f9 --- /dev/null +++ b/extensions/keyboard-2048/server.mjs @@ -0,0 +1,170 @@ +import { createServer } from "node:http"; +import { readFile, mkdir, rename, writeFile, rm } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { randomUUID } from "node:crypto"; +import { DIRECTIONS, move, newGame, slide, undo, validateSave, viewState } from "./game.mjs"; + +const assets = new Map([ + ["/", ["index.html", "text/html; charset=utf-8"]], + ["/app.mjs", ["app.mjs", "text/javascript; charset=utf-8"]], + ["/style.css", ["style.css", "text/css; charset=utf-8"]], +]); +const root = dirname(fileURLToPath(import.meta.url)); + +export async function createGameStore(savePath) { + let state; + try { + state = validateSave(JSON.parse(await readFile(savePath, "utf8"))); + } catch (error) { + if (error.code !== "ENOENT") throw error; + state = newGame(); + } + let queue = Promise.resolve(); + const stream = randomUUID(); + let update = { stream, revision: 0, motion: null }; + const snapshot = () => ({ ...viewState(state), update }); + const listeners = new Set(); + async function persist(next) { + await mkdir(dirname(savePath), { recursive: true }); + const temporary = `${savePath}.${randomUUID()}.tmp`; + try { + await writeFile(temporary, JSON.stringify(next), { flag: "wx" }); + await rename(temporary, savePath); + } finally { + await rm(temporary, { force: true }); + } + } + await persist(state); + return { + snapshot, + subscribe(listener) { + listeners.add(listener); + return () => listeners.delete(listener); + }, + execute(action, direction) { + const result = queue.then(async () => { + let next; + if (action === "move" && DIRECTIONS.includes(direction)) next = move(state, direction); + else if (action === "undo") next = undo(state); + else if (action === "new") next = newGame(state.best); + else throw new Error("Invalid command."); + if (next !== state) { + const motion = action === "move" ? slide(state.board, direction) : null; + await persist(next); + update = { + stream, + revision: update.revision + 1, + motion: motion ? { + from: state.board, + movements: motion.movements, + merges: motion.merges, + } : null, + }; + state = next; + for (const listener of listeners) listener(snapshot()); + } + return snapshot(); + }); + // A failed write is returned to its caller, without poisoning the command queue. + queue = result.then(() => undefined, () => undefined); + return result; + }, + }; +} + +export async function startGameServer(store) { + const token = randomUUID(); + const clients = new Set(); + const unsubscribe = store.subscribe(state => { + for (const client of clients) client.write(`data: ${JSON.stringify(state)}\n\n`); + }); + let origin; + function json(response, status, value) { + response.writeHead(status, { "Content-Type": "application/json; charset=utf-8" }); + response.end(JSON.stringify(value)); + } + const server = createServer(async (request, response) => { + response.setHeader("Cache-Control", "no-store"); + response.setHeader("X-Content-Type-Options", "nosniff"); + response.setHeader("Referrer-Policy", "no-referrer"); + if (request.headers.host !== new URL(origin).host) { + json(response, 403, { error: "Host not allowed." }); + return; + } + const url = new URL(request.url, origin); + if (url.searchParams.get("token") !== token) { + json(response, 403, { error: "Unauthorized access." }); + return; + } + try { + if (request.method === "GET" && assets.has(url.pathname)) { + const [file, type] = assets.get(url.pathname); + const body = await readFile(join(root, file)); + response.writeHead(200, { "Content-Type": type }); + response.end(body); + } else if (request.method === "GET" && url.pathname === "/state") { + json(response, 200, store.snapshot()); + } else if (request.method === "GET" && url.pathname === "/events") { + response.writeHead(200, { + "Content-Type": "text/event-stream", + Connection: "keep-alive", + }); + response.write(`data: ${JSON.stringify(store.snapshot())}\n\n`); + clients.add(response); + request.on("close", () => clients.delete(response)); + } else if (request.method === "POST" && url.pathname === "/command") { + if ((request.headers.origin && request.headers.origin !== origin) + || request.headers["content-type"] !== "application/json") { + json(response, 403, { error: "Origin or content type not allowed." }); + return; + } + let body = ""; + for await (const chunk of request) { + body += chunk; + if (body.length > 1024) { + json(response, 413, { error: "Request too large." }); + return; + } + } + let command; + try { command = JSON.parse(body); } + catch { json(response, 400, { error: "Invalid JSON." }); return; } + if (!command || !["move", "undo", "new"].includes(command.action) + || (command.action === "move" && !DIRECTIONS.includes(command.direction))) { + json(response, 400, { error: "Invalid command." }); + return; + } + json(response, 200, await store.execute(command.action, command.direction)); + } else { + json(response, 404, { error: "Resource not found." }); + } + } catch (error) { + process.stderr.write(`[2048] ${error.stack ?? error}\n`); + if (!response.headersSent) json(response, 500, { error: "Game or save error. Please try again." }); + else response.end(); + } + }); + try { + await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(0, "127.0.0.1", resolve); + }); + } catch (error) { + unsubscribe(); + throw error; + } + origin = `http://127.0.0.1:${server.address().port}`; + return { + url: `${origin}/?token=${token}`, + async close() { + unsubscribe(); + for (const client of clients) client.end(); + clients.clear(); + const closed = new Promise((resolve, reject) => + server.close(error => error ? reject(error) : resolve())); + server.closeAllConnections(); + await closed; + }, + }; +} diff --git a/extensions/keyboard-2048/server.test.mjs b/extensions/keyboard-2048/server.test.mjs new file mode 100644 index 0000000000..8823eecc5d --- /dev/null +++ b/extensions/keyboard-2048/server.test.mjs @@ -0,0 +1,163 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { mkdtemp, readFile, writeFile, rm, mkdir } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { createGameStore, startGameServer } from "./server.mjs"; +import { newGame, DIRECTIONS, slide } from "./game.mjs"; + +async function fixture(t) { + const directory = await mkdtemp(join(tmpdir(), "copilot-2048-test-")); + t.after(() => rm(directory, { recursive: true, force: true })); + const file = join(directory, "save.json"); + const store = await createGameStore(file); + return { directory, file, store }; +} +const movable = state => DIRECTIONS.find(direction => slide(state.board, direction).changed); + +test("state and one-level Undo survive provider restart", async t => { + const { file, store } = await fixture(t); + const before = store.snapshot(); + await store.execute("move", movable(before)); + const restored = await createGameStore(file); + const { update: restoredUpdate, ...restoredGame } = restored.snapshot(); + const { update: originalUpdate, ...originalGame } = store.snapshot(); + assert.deepEqual(restoredGame, originalGame); + assert.equal(restoredUpdate.motion, null); + assert.notEqual(restoredUpdate.stream, originalUpdate.stream); + const undone = await restored.execute("undo"); + assert.deepEqual(undone.board, before.board); + assert.equal(undone.score, before.score); + assert.equal(undone.moves, before.moves); + assert.equal(undone.canUndo, false); +}); + +test("animation updates are ephemeral, ordered, and identical for all panels and replies", async t => { + const { file } = await fixture(t); + const initial = { ...newGame(), board: [2, 2, 4, ...Array(13).fill(0)] }; + await writeFile(file, JSON.stringify(initial)); + const store = await createGameStore(file); + const firstPanel = []; + const secondPanel = []; + store.subscribe(state => firstPanel.push(state)); + store.subscribe(state => secondPanel.push(state)); + const before = store.snapshot(); + const plan = slide(before.board, "left"); + const moved = await store.execute("move", "left"); + assert.equal(moved.update.stream, before.update.stream); + assert.equal(moved.update.revision, before.update.revision + 1); + assert.deepEqual(moved.update.motion, { + from: before.board, movements: plan.movements, merges: plan.merges, + }); + assert.deepEqual(firstPanel, [moved]); + assert.deepEqual(secondPanel, [moved]); + assert.deepEqual(store.snapshot(), moved); + const saved = JSON.parse(await readFile(file, "utf8")); + assert.equal("update" in saved, false); + assert.equal("motion" in saved, false); + const undone = await store.execute("undo"); + assert.equal(undone.update.revision, moved.update.revision + 1); + assert.equal(undone.update.motion, null); + assert.deepEqual(undone.board, initial.board); + const repeatedUndo = await store.execute("undo"); + assert.deepEqual(repeatedUndo, undone); + const fresh = await store.execute("new"); + assert.equal(fresh.update.revision, undone.update.revision + 1); + assert.equal(fresh.update.motion, null); +}); + +test("new game clears Undo and keeps record", async t => { + const { file } = await fixture(t); + await writeFile(file, JSON.stringify({ + ...newGame(), board: [2, 2, ...Array(14).fill(0)], best: 100, + })); + const store = await createGameStore(file); + await store.execute("move", "left"); + const next = await store.execute("new"); + assert.equal(next.canUndo, false); + assert.equal(next.moves, 0); + assert.equal(next.score, 0); + assert.equal(next.best, 100); + assert.equal(next.board.filter(Boolean).length, 2); +}); + +test("invalid saved data is reported without being overwritten", async t => { + const { file } = await fixture(t); + await writeFile(file, '{"invalid":true}'); + await assert.rejects(createGameStore(file), /Invalid 2048 save/); + assert.equal(await readFile(file, "utf8"), '{"invalid":true}'); +}); + +test("failed persistence leaves live state unchanged and command queue recovers", async t => { + const { file, store } = await fixture(t); + const before = store.snapshot(); + await rm(file); + await mkdir(file); + await assert.rejects(store.execute("move", movable(before))); + assert.deepEqual(store.snapshot(), before); + await rm(file, { recursive: true }); + assert.equal((await store.execute("move", movable(before))).moves, 1); +}); + +test("commands are serialized and invalid commands do not poison the queue", async t => { + const { store } = await fixture(t); + await assert.rejects(store.execute("invalid"), /Invalid command/); + const before = store.snapshot(); + await Promise.all([store.execute("move", movable(before)), store.execute("undo")]); + assert.deepEqual(store.snapshot().board, before.board); + assert.equal(store.snapshot().canUndo, false); +}); + +test("loopback server serves UI and authenticated actions with live panel updates", async t => { + const { store } = await fixture(t); + const server = await startGameServer(store); + t.after(() => server.close()); + const url = new URL(server.url); + const endpoint = path => `${url.origin}${path}${url.search}`; + assert.equal((await fetch(url.origin)).status, 403); + const page = await fetch(server.url); + assert.equal(page.status, 200); + const html = await page.text(); + assert.match(html, //); + assert.match(html, /aria-label="Move up"/); + assert.match(html, /New game/); + assert.match(html, /GAME OVER!/); + assert.match(html, /

Use arrows or WASD to move tiles\.
Merge equal tiles to reach 2048<\/strong>\.<\/p>/); + for (const path of ["/app.mjs", "/style.css"]) { + assert.equal((await fetch(endpoint(path))).status, 200); + } + const script = await (await fetch(endpoint("/app.mjs"))).text(); + assert.match(script, /Intl\.NumberFormat\("en-US"\)/); + const css = await (await fetch(endpoint("/style.css"))).text(); + assert.match(css, /content: "NEW"/); + assert.equal((await fetch(endpoint("/missing"))).status, 404); + const before = await (await fetch(endpoint("/state"))).json(); + assert.deepEqual(before, store.snapshot()); + + const abort = new AbortController(); + t.after(() => abort.abort()); + const events = await fetch(endpoint("/events"), { signal: abort.signal }); + const reader = events.body.getReader(); + const first = new TextDecoder().decode((await reader.read()).value); + assert.match(first, /data:/); + + const send = (body, origin = url.origin) => fetch(endpoint("/command"), { + method: "POST", + headers: { "Content-Type": "application/json", Origin: origin }, + body: JSON.stringify(body), + }); + const invalid = await send({ action: "move", direction: "diagonal" }); + assert.equal(invalid.status, 400); + assert.deepEqual(await invalid.json(), { error: "Invalid command." }); + assert.equal((await send(null)).status, 400); + assert.equal((await send({ action: "new" }, "https://example.invalid")).status, 403); + const moved = await (await send({ action: "move", direction: movable(before) })).json(); + assert.equal(moved.moves, before.moves + 1); + const update = new TextDecoder().decode((await reader.read()).value); + assert.match(update, /"canUndo":true/); + assert.deepEqual(JSON.parse(update.trim().slice("data: ".length)), moved); + const undone = await (await send({ action: "undo" })).json(); + assert.deepEqual(undone.board, before.board); + assert.equal(undone.canUndo, false); + abort.abort(); +}); diff --git a/extensions/keyboard-2048/style.css b/extensions/keyboard-2048/style.css new file mode 100644 index 0000000000..1d0c47713b --- /dev/null +++ b/extensions/keyboard-2048/style.css @@ -0,0 +1,117 @@ +:root { + color-scheme: light; + --cp-bg: #f7f4ef; + --cp-bg-elevated: #fcfbf8; + --cp-surface: #ffffff; + --cp-surface-soft: #f5f5f5; + --cp-border: #dedede; + --cp-border-strong: #919191; + --cp-text: #242424; + --cp-text-muted: #5c5c5c; + --cp-text-soft: #6f6f6f; + --cp-accent: #b11f4b; + --cp-accent-hover: #9a1a41; + --cp-accent-soft: rgba(177, 31, 75, 0.08); + --cp-accent-fg: #ffffff; + --cp-success: #16a34a; + --cp-danger: #dc2626; + --cp-warning: #f59e0b; + --cp-link: #0078d4; + --cp-shadow: 0 18px 48px rgba(0, 0, 0, 0.12); + --cp-overlay: rgba(255, 255, 255, 0.8); + --cp-panel: rgba(255, 255, 255, 0.86); + --cp-panel-strong: rgba(255, 255, 255, 0.96); + --cp-sheen: rgba(255, 255, 255, 0.55); + --cp-highlight: rgba(177, 31, 75, 0.12); +} +html[data-theme="dark"] { + color-scheme: dark; + --cp-bg: #3d3b3a; + --cp-bg-elevated: #343231; + --cp-surface: #292929; + --cp-surface-soft: #2e2e2e; + --cp-border: #474747; + --cp-border-strong: #5f5f5f; + --cp-text: #dedede; + --cp-text-muted: #919191; + --cp-text-soft: #b0b0b0; + --cp-accent: #fd8ea1; + --cp-accent-hover: #fb7b91; + --cp-accent-soft: rgba(253, 142, 161, 0.14); + --cp-accent-fg: #1a1a1a; + --cp-success: #4ade80; + --cp-danger: #f87171; + --cp-warning: #fbbf24; + --cp-link: #4da6ff; + --cp-shadow: 0 18px 48px rgba(0, 0, 0, 0.32); + --cp-overlay: rgba(41, 41, 41, 0.88); + --cp-panel: rgba(41, 41, 41, 0.72); + --cp-panel-strong: rgba(41, 41, 41, 0.96); + --cp-sheen: rgba(255, 255, 255, 0.04); + --cp-highlight: rgba(253, 142, 161, 0.12); +} +* { box-sizing: border-box; } +body { margin: 0; background: var(--cp-bg); color: var(--cp-text); font: 14px/1.5 "Segoe UI", Aptos, Calibri, -apple-system, BlinkMacSystemFont, sans-serif; } +main { max-width: 480px; margin: auto; padding: 28px 20px; } +header { display: flex; align-items: center; justify-content: space-between; gap: 12px; } +.eyebrow { font-size: 10px; letter-spacing: 2px; margin: 0; color: var(--cp-text-muted); font-weight: 700; } +h1 { margin: 0; font-size: clamp(44px, 12vw, 64px); letter-spacing: -4px; line-height: 1.15; } +h1 span { color: var(--cp-accent); } +.scores { display: flex; gap: 8px; } +.score-card { border: 1px solid var(--cp-border); background: var(--cp-surface); padding: 8px 12px; border-radius: 10px; text-align: center; min-width: 72px; } +.score-card span { display: block; font-size: 10px; font-weight: 700; letter-spacing: 1px; color: var(--cp-text-muted); } +.score-card strong { display: block; font-size: 20px; font-variant-numeric: tabular-nums; } +.intro { color: var(--cp-text-muted); margin: 16px 0 20px; } +.intro strong { color: var(--cp-text); } +.toolbar { display: flex; justify-content: space-between; gap: 8px; margin-bottom: 16px; } +button { font: inherit; font-weight: 600; border: 1px solid var(--cp-border); border-radius: 10px; padding: 10px 12px; background: var(--cp-surface); color: var(--cp-text); cursor: pointer; min-height: 44px; } +button:hover:enabled { border-color: var(--cp-accent); background: var(--cp-accent-soft); } +button:disabled { opacity: .45; cursor: not-allowed; } +button.primary { background: var(--cp-accent); color: var(--cp-accent-fg); border-color: var(--cp-accent); } +button.primary:hover:enabled { background: var(--cp-accent-hover); } +button.primary kbd { color: var(--cp-accent-fg); background: var(--cp-accent-hover); border-color: var(--cp-accent-hover); } +:focus-visible { outline: 3px solid var(--cp-accent); outline-offset: 4px; } +kbd { display: inline-block; font: 11px Consolas, "Courier New", Courier, monospace; padding: 2px 5px; background: var(--cp-surface-soft); color: var(--cp-text-muted); border: 1px solid var(--cp-border); border-radius: 4px; } +.toolbar kbd { margin-left: 4px; } +.board-shell { position: relative; container-type: inline-size; } +.board { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; padding: 10px; border-radius: 16px; background: var(--cp-border); touch-action: none; user-select: none; } +.game-over { position: absolute; inset: 0; z-index: 2; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px; padding: 24px; border-radius: 16px; background: var(--cp-overlay); text-align: center; pointer-events: none; } +.game-over[hidden] { display: none; } +.game-over strong { max-width: 100%; font-size: clamp(32px, 18cqw, 80px); font-weight: 900; line-height: .95; letter-spacing: -2px; color: var(--cp-accent); text-wrap: balance; } +.game-over span { color: var(--cp-text); font-weight: 600; } +.tile { aspect-ratio: 1; display: flex; align-items: center; justify-content: center; border-radius: 10px; font-size: clamp(20px, 7vw, 36px); font-weight: 750; background: var(--cp-surface-soft); font-variant-numeric: tabular-nums; } +.tile[data-level="1"] { background: var(--cp-bg-elevated); } +.tile[data-level="2"] { background: var(--cp-surface); } +.tile[data-level="3"], .tile[data-level="4"] { background: var(--cp-highlight); color: var(--cp-text); box-shadow: inset 0 0 0 2px var(--cp-accent); } +.tile[data-level="5"], .tile[data-level="6"], .tile[data-level="7"] { background: var(--cp-accent); color: var(--cp-accent-fg); } +.tile[data-level="8"], .tile[data-level="9"], .tile[data-level="10"] { background: var(--cp-accent-hover); color: var(--cp-accent-fg); box-shadow: inset 0 0 0 3px var(--cp-accent); } +.tile[data-level="11"] { background: var(--cp-text); color: var(--cp-bg); box-shadow: inset 0 0 0 3px var(--cp-accent); } +.tile.large { font-size: clamp(14px, 5vw, 26px); } +.motion-layer { position: absolute; inset: 0; z-index: 1; pointer-events: none; } +.motion-tile { position: absolute; margin: 0; } +.tile.in-flight { color: transparent; background: var(--cp-surface-soft); box-shadow: none; outline: none; } +.tile.in-flight::after { display: none; } +.tile.spawned { position: relative; background: var(--cp-highlight); color: var(--cp-text); outline: 3px solid var(--cp-accent); outline-offset: -3px; animation: spawn-pop 360ms ease-out; } +.tile.spawned::after { content: "NEW"; position: absolute; top: 4px; right: 4px; padding: 2px 3px; border-radius: 4px; background: var(--cp-accent); color: var(--cp-accent-fg); font-size: clamp(7px, 2.4cqw, 10px); font-weight: 700; line-height: 1; pointer-events: none; } +@keyframes spawn-pop { from { transform: scale(.8); } to { transform: scale(1); } } +.game-info { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin: 16px 0; min-height: 24px; } +#status { font-weight: 600; } +#moves { color: var(--cp-text-muted); white-space: nowrap; font-size: 12px; } +#error { padding: 12px; border: 1px solid var(--cp-danger); border-radius: 10px; color: var(--cp-danger); background: var(--cp-surface); } +.dpad { display: flex; justify-content: center; gap: 8px; margin: 20px 0; } +.dpad button { width: 48px; font-size: 20px; } +footer { color: var(--cp-text-muted); font-size: 12px; border-top: 1px solid var(--cp-border); padding-top: 12px; } +footer p { margin: 8px 0; } +.hint { color: var(--cp-text-soft); font-size: 11px; } +dialog { max-width: min(400px, calc(100vw - 32px)); padding: 24px; background: var(--cp-surface); color: var(--cp-text); border: 1px solid var(--cp-border); border-radius: 16px; } +dialog::backdrop { background: var(--cp-overlay); } +dialog h2 { margin-top: 0; } +dialog form { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; } +@media (max-width: 340px) { + main { padding: 20px 12px; } + .board { gap: 6px; padding: 6px; } + .score-card { min-width: 56px; padding: 8px; } + .score-card strong { font-size: 16px; } + .toolbar kbd { display: none; } +} +@media (prefers-reduced-motion: reduce) { .tile.spawned { animation: none; } } diff --git a/plugins/keyboard-2048/README.md b/plugins/keyboard-2048/README.md new file mode 100644 index 0000000000..9971245287 --- /dev/null +++ b/plugins/keyboard-2048/README.md @@ -0,0 +1,18 @@ +# Keyboard 2048 Canvas + +Play 2048 in a Copilot canvas with keyboard and touch controls, undo, and session saves. + +## Installation + +```bash +copilot plugin install keyboard-2048@awesome-copilot +``` + +## Source + +Created by [geniodelmale](https://github.com/geniodelmale). The source project is +[2048 Keyboard Canvas](https://github.com/geniodelmale/2048-keyboard-canvas). + +## License + +MIT diff --git a/plugins/keyboard-2048/plugin.json b/plugins/keyboard-2048/plugin.json new file mode 100644 index 0000000000..d9e6c1ddf5 --- /dev/null +++ b/plugins/keyboard-2048/plugin.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", + "name": "keyboard-2048", + "description": "Play 2048 in a Copilot canvas with keyboard and touch controls, undo, and session saves.", + "version": "1.0.0", + "author": { + "name": "geniodelmale", + "url": "https://github.com/geniodelmale" + }, + "repository": "https://github.com/geniodelmale/2048-keyboard-canvas", + "license": "MIT", + "keywords": [ + "2048", + "copilot-canvas", + "game", + "keyboard", + "puzzle", + "undo" + ], + "extensions": { + "com.github.copilot": { + "logo": "assets/preview.png" + }, + "com.github.awesome-copilot": { + "extensions": [ + "./extensions/keyboard-2048" + ] + } + } +} From 91cbbbc6ddd76170870648a159ff17d39452430d Mon Sep 17 00:00:00 2001 From: Lorenzo Barbieri Date: Wed, 11 Nov 2026 21:47:00 +0100 Subject: [PATCH 2/5] Implement URL parsing with error handling Add error handling for invalid request URLs. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- extensions/keyboard-2048/server.mjs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/extensions/keyboard-2048/server.mjs b/extensions/keyboard-2048/server.mjs index d19c0729f9..82b74f1097 100644 --- a/extensions/keyboard-2048/server.mjs +++ b/extensions/keyboard-2048/server.mjs @@ -92,7 +92,13 @@ export async function startGameServer(store) { json(response, 403, { error: "Host not allowed." }); return; } - const url = new URL(request.url, origin); + let url; + try { + url = new URL(request.url, origin); + } catch { + json(response, 400, { error: "Invalid request URL." }); + return; + } if (url.searchParams.get("token") !== token) { json(response, 403, { error: "Unauthorized access." }); return; From 938272ac3f4d10ee8f7d707656572e6ad6a7d838 Mon Sep 17 00:00:00 2001 From: Lorenzo Barbieri Date: Thu, 12 Nov 2026 15:01:42 +0100 Subject: [PATCH 3/5] Improve 2048 canvas accessibility Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- extensions/keyboard-2048/app.mjs | 1 + extensions/keyboard-2048/style.css | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/keyboard-2048/app.mjs b/extensions/keyboard-2048/app.mjs index 59e6933d15..18bcffc7eb 100644 --- a/extensions/keyboard-2048/app.mjs +++ b/extensions/keyboard-2048/app.mjs @@ -18,6 +18,7 @@ board.parentElement.append(motionLayer); const cells = Array.from({ length: 16 }, () => { const cell = document.createElement("div"); cell.className = "tile"; + cell.setAttribute("role", "img"); board.append(cell); return cell; }); diff --git a/extensions/keyboard-2048/style.css b/extensions/keyboard-2048/style.css index 1d0c47713b..f98435cc2f 100644 --- a/extensions/keyboard-2048/style.css +++ b/extensions/keyboard-2048/style.css @@ -33,7 +33,7 @@ html[data-theme="dark"] { --cp-border: #474747; --cp-border-strong: #5f5f5f; --cp-text: #dedede; - --cp-text-muted: #919191; + --cp-text-muted: #b0b0b0; --cp-text-soft: #b0b0b0; --cp-accent: #fd8ea1; --cp-accent-hover: #fb7b91; From 9fcd1c41ec4d80a7a8b5ccd5b4e99f457250ceff Mon Sep 17 00:00:00 2001 From: Lorenzo Barbieri Date: Thu, 12 Nov 2026 15:29:10 +0100 Subject: [PATCH 4/5] Scope 2048 shortcuts and defer initialization until DOM ready Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- extensions/keyboard-2048/app.mjs | 2 +- extensions/keyboard-2048/index.html | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/extensions/keyboard-2048/app.mjs b/extensions/keyboard-2048/app.mjs index 18bcffc7eb..fd08bfafc4 100644 --- a/extensions/keyboard-2048/app.mjs +++ b/extensions/keyboard-2048/app.mjs @@ -155,7 +155,7 @@ const keys = { ArrowLeft: "left", ArrowRight: "right", ArrowUp: "up", ArrowDown: "down", a: "left", d: "right", w: "up", s: "down", }; -document.addEventListener("keydown", event => { +board.addEventListener("keydown", event => { if (dialog.open || event.isComposing || event.altKey || event.target.closest("input, textarea, select, [contenteditable='true']")) return; const key = event.key.length === 1 ? event.key.toLowerCase() : event.key; diff --git a/extensions/keyboard-2048/index.html b/extensions/keyboard-2048/index.html index 2d724fe493..77c00d1e4f 100644 --- a/extensions/keyboard-2048/index.html +++ b/extensions/keyboard-2048/index.html @@ -19,10 +19,12 @@ stylesheet.rel = "stylesheet"; stylesheet.href = "/style.css?token=" + encodeURIComponent(token); document.head.append(stylesheet); - const app = document.createElement("script"); - app.type = "module"; - app.src = "/app.mjs?token=" + encodeURIComponent(token); - document.head.append(app); + document.addEventListener("DOMContentLoaded", () => { + const app = document.createElement("script"); + app.type = "module"; + app.src = "/app.mjs?token=" + encodeURIComponent(token); + document.head.append(app); + }, { once: true }); From 8803736fd4ff5f934267ea489fb8d3f504486133 Mon Sep 17 00:00:00 2001 From: Lorenzo Barbieri Date: Thu, 12 Nov 2026 15:40:01 +0100 Subject: [PATCH 5/5] Share pending 2048 canvas startup across overlapping opens Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- extensions/keyboard-2048/extension.mjs | 8 +- extensions/keyboard-2048/extension.test.mjs | 154 ++++++++++++++++++++ plugins/keyboard-2048/README.md | 8 + 3 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 extensions/keyboard-2048/extension.test.mjs diff --git a/extensions/keyboard-2048/extension.mjs b/extensions/keyboard-2048/extension.mjs index ce6d1ca65d..e8dcc804f5 100644 --- a/extensions/keyboard-2048/extension.mjs +++ b/extensions/keyboard-2048/extension.mjs @@ -57,10 +57,12 @@ session = await joinSession({ open: async ctx => { let panel = panels.get(ctx.instanceId); if (!panel) { - panel = startGameServer(await store()); + panel = Promise.resolve().then(() => store()).then(startGameServer) + .catch(error => { + if (panels.get(ctx.instanceId) === panel) panels.delete(ctx.instanceId); + throw error; + }); panels.set(ctx.instanceId, panel); - try { await panel; } - catch (error) { panels.delete(ctx.instanceId); throw error; } } return { url: (await panel).url, title: "2048 · Keyboard edition" }; }, diff --git a/extensions/keyboard-2048/extension.test.mjs b/extensions/keyboard-2048/extension.test.mjs new file mode 100644 index 0000000000..466f9bc168 --- /dev/null +++ b/extensions/keyboard-2048/extension.test.mjs @@ -0,0 +1,154 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { registerHooks } from "node:module"; +import { startGameServer } from "./server.mjs"; + +let fixtureId = 0; +async function loadCanvas(t, dependencies) { + const key = `__keyboard2048Test${++fixtureId}`; + const fixture = globalThis[key] = { ...dependencies }; + const stub = source => `data:text/javascript,${encodeURIComponent(source)}`; + const sdk = stub(` + export const createCanvas = options => options; + export class CanvasError extends Error {} + export async function joinSession(options) { + globalThis.${key}.canvas = options.canvases[0]; + return { workspacePath: "test-workspace" }; + } + `); + const server = stub(` + export const { createGameStore, startGameServer } = globalThis.${key}; + `); + const entry = new URL("./extension.mjs", import.meta.url); + entry.searchParams.set("test", key); + const hooks = registerHooks({ + resolve(specifier, context, next) { + if (context.parentURL === entry.href) { + if (specifier === "@github/copilot-sdk/extension") return { url: sdk, shortCircuit: true }; + if (specifier === "./server.mjs") return { url: server, shortCircuit: true }; + } + return next(specifier, context); + }, + }); + t.after(() => { hooks.deregister(); delete globalThis[key]; }); + await import(entry.href); + return fixture.canvas; +} + +test("overlapping opens share initialization and one server, which close releases", async t => { + const ready = Promise.withResolvers(); + const initializing = Promise.withResolvers(); + let stores = 0; + let servers = 0; + let subscriptions = 0; + const canvas = await loadCanvas(t, { + createGameStore() { + stores++; + initializing.resolve(); + return ready.promise; + }, + startGameServer(store) { + servers++; + return startGameServer(store); + }, + }); + const ctx = { instanceId: "shared" }; + t.after(() => canvas.onClose(ctx)); + const first = canvas.open(ctx); + const second = canvas.open(ctx); + await initializing.promise; + assert.equal(stores, 1); + assert.equal(servers, 0); + ready.resolve({ + snapshot: () => ({}), + subscribe() { + subscriptions++; + return () => { subscriptions--; }; + }, + }); + const [a, b] = await Promise.all([first, second]); + assert.equal(a.url, b.url); + assert.equal(servers, 1); + assert.equal(subscriptions, 1); + assert.equal((await fetch(a.url)).status, 200); + await canvas.onClose(ctx); + assert.equal(subscriptions, 0); + await assert.rejects(fetch(a.url)); +}); + +test("failed store initialization rejects overlapping opens and permits retry", async t => { + const ready = Promise.withResolvers(); + let stores = 0; + let servers = 0; + let closed = 0; + const canvas = await loadCanvas(t, { + createGameStore() { return ++stores === 1 ? ready.promise : Promise.resolve({}); }, + async startGameServer() { + servers++; + return { url: "http://127.0.0.1/retry", async close() { closed++; } }; + }, + }); + const ctx = { instanceId: "retry" }; + const first = assert.rejects(canvas.open(ctx), /save unavailable/); + const second = assert.rejects(canvas.open(ctx), /save unavailable/); + ready.reject(new Error("save unavailable")); + await Promise.all([first, second]); + const result = await canvas.open(ctx); + assert.equal(result.url, "http://127.0.0.1/retry"); + assert.equal(stores, 2); + assert.equal(servers, 1); + await canvas.onClose(ctx); + assert.equal(closed, 1); +}); + +test("close during initialization releases the pending server", async t => { + const ready = Promise.withResolvers(); + let closed = 0; + const canvas = await loadCanvas(t, { + createGameStore: () => ready.promise, + startGameServer: async () => ({ + url: "http://127.0.0.1/pending", + async close() { closed++; }, + }), + }); + const ctx = { instanceId: "pending" }; + const opening = canvas.open(ctx); + const closing = canvas.onClose(ctx); + assert.equal(closed, 0); + ready.resolve({}); + await Promise.all([opening, closing]); + assert.equal(closed, 1); + await canvas.onClose(ctx); + assert.equal(closed, 1); +}); + +test("a failed closed startup cannot evict a replacement panel", async t => { + const ready = Promise.withResolvers(); + const starting = Promise.withResolvers(); + let servers = 0; + let closed = 0; + const canvas = await loadCanvas(t, { + createGameStore: async () => ({}), + startGameServer() { + if (++servers === 1) { + starting.resolve(); + return ready.promise; + } + return Promise.resolve({ + url: "http://127.0.0.1/replacement", + async close() { closed++; }, + }); + }, + }); + const ctx = { instanceId: "replacement" }; + const opening = assert.rejects(canvas.open(ctx), /listen failed/); + await starting.promise; + const closing = assert.rejects(canvas.onClose(ctx), /listen failed/); + const replacement = await canvas.open(ctx); + ready.reject(new Error("listen failed")); + await Promise.all([opening, closing]); + assert.equal((await canvas.open(ctx)).url, replacement.url); + assert.equal(servers, 2); + await canvas.onClose(ctx); + assert.equal(closed, 1); +}); diff --git a/plugins/keyboard-2048/README.md b/plugins/keyboard-2048/README.md index 9971245287..6ea7ec7130 100644 --- a/plugins/keyboard-2048/README.md +++ b/plugins/keyboard-2048/README.md @@ -8,6 +8,14 @@ Play 2048 in a Copilot canvas with keyboard and touch controls, undo, and sessio copilot plugin install keyboard-2048@awesome-copilot ``` +## Development + +From `extensions/keyboard-2048`, run the lifecycle, game, and server tests with Node.js 22.15 or later: + +```bash +node --test extension.test.mjs game.test.mjs server.test.mjs +``` + ## Source Created by [geniodelmale](https://github.com/geniodelmale). The source project is

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 GXa5bN5IRx- literal 0 HcmV?d00001 diff --git a/extensions/keyboard-2048/extension.mjs b/extensions/keyboard-2048/extension.mjs new file mode 100644 index 0000000000..ce6d1ca65d --- /dev/null +++ b/extensions/keyboard-2048/extension.mjs @@ -0,0 +1,73 @@ +import { join } from "node:path"; +import { createCanvas, CanvasError, joinSession } from "@github/copilot-sdk/extension"; +import { createGameStore, startGameServer } from "./server.mjs"; + +const panels = new Map(); +let storePromise; +let session; +function store() { + if (!session?.workspacePath) { + throw new CanvasError("workspace_unavailable", "Session workspace is unavailable."); + } + if (!storePromise) { + storePromise = createGameStore(join(session.workspacePath, "2048-save.json")) + .catch(error => { storePromise = undefined; throw error; }); + } + return storePromise; +} +const emptyInput = { type: "object", properties: {}, additionalProperties: false }; + +session = await joinSession({ + canvases: [createCanvas({ + id: "keyboard-2048", + displayName: "2048", + description: "Play 2048 with arrow keys or WASD, undo the last move, and resume your saved game.", + inputSchema: emptyInput, + actions: [ + { + name: "get_state", + description: "Read the board, score, and Undo availability.", + inputSchema: emptyInput, + handler: async () => (await store()).snapshot(), + }, + { + name: "move", + description: "Move the tiles in a direction.", + inputSchema: { + type: "object", + properties: { direction: { type: "string", enum: ["left", "right", "up", "down"] } }, + required: ["direction"], + additionalProperties: false, + }, + handler: async ctx => (await store()).execute("move", ctx.input.direction), + }, + { + name: "undo", + description: "Undo only the last valid move, restoring the score and removing the spawned tile.", + inputSchema: emptyInput, + handler: async () => (await store()).execute("undo"), + }, + { + name: "new_game", + description: "Start a new game, keeping the session's best score.", + inputSchema: emptyInput, + handler: async () => (await store()).execute("new"), + }, + ], + open: async ctx => { + let panel = panels.get(ctx.instanceId); + if (!panel) { + panel = startGameServer(await store()); + panels.set(ctx.instanceId, panel); + try { await panel; } + catch (error) { panels.delete(ctx.instanceId); throw error; } + } + return { url: (await panel).url, title: "2048 · Keyboard edition" }; + }, + onClose: async ctx => { + const panel = panels.get(ctx.instanceId); + panels.delete(ctx.instanceId); + if (panel) await (await panel).close(); + }, + })], +}); diff --git a/extensions/keyboard-2048/game.mjs b/extensions/keyboard-2048/game.mjs new file mode 100644 index 0000000000..8ff9071c1a --- /dev/null +++ b/extensions/keyboard-2048/game.mjs @@ -0,0 +1,134 @@ +export const DIRECTIONS = ["left", "right", "up", "down"]; + +function traceLine(line) { + const values = line.flatMap((value, from) => value ? [{ value, from }] : []); + const merged = []; + const movements = []; + const merges = []; + let score = 0; + for (let i = 0; i < values.length; i++) { + const to = merged.length; + movements.push({ from: values[i].from, to, value: values[i].value }); + if (values[i].value === values[i + 1]?.value) { + const value = values[i].value * 2; + movements.push({ from: values[i + 1].from, to, value: values[i + 1].value }); + merges.push(to); + merged.push(value); + score += value; + i++; + } else { + merged.push(values[i].value); + } + } + return { line: merged.concat(Array(4 - merged.length).fill(0)), score, movements, merges }; +} + +export function mergeLine(line) { + const { line: merged, score } = traceLine(line); + return { line: merged, score }; +} + +export function slide(board, direction) { + if (!DIRECTIONS.includes(direction)) throw new Error("Invalid direction."); + const next = [...board]; + const movements = []; + const merges = []; + let score = 0; + for (let row = 0; row < 4; row++) { + const indices = Array.from({ length: 4 }, (_, column) => { + if (direction === "left") return row * 4 + column; + if (direction === "right") return row * 4 + 3 - column; + if (direction === "up") return column * 4 + row; + return (3 - column) * 4 + row; + }); + const result = traceLine(indices.map(index => board[index])); + indices.forEach((index, column) => { next[index] = result.line[column]; }); + movements.push(...result.movements.map(tile => ({ + ...tile, from: indices[tile.from], to: indices[tile.to], + }))); + merges.push(...result.merges.map(column => indices[column])); + score += result.score; + } + return { board: next, score, changed: next.some((value, index) => value !== board[index]), movements, merges }; +} + +function spawn(board, random) { + const empty = board.flatMap((value, index) => value === 0 ? [index] : []); + if (empty.length === 0) return { board: [...board], index: null }; + const next = [...board]; + const index = empty[Math.floor(random() * empty.length)]; + next[index] = random() < 0.9 ? 2 : 4; + return { board: next, index }; +} + +export function newGame(best = 0, random = Math.random) { + const first = spawn(Array(16).fill(0), random); + return { + version: 1, + board: spawn(first.board, random).board, + spawnedIndex: null, + score: 0, + best, + moves: 0, + previous: null, + }; +} + +export function isGameOver(board) { + return !DIRECTIONS.some(direction => slide(board, direction).changed); +} + +export function move(state, direction, random = Math.random) { + const result = slide(state.board, direction); + if (!result.changed) return state; + const score = state.score + result.score; + const spawned = spawn(result.board, random); + return { + ...state, + board: spawned.board, + spawnedIndex: spawned.index, + score, + best: Math.max(state.best, score), + moves: state.moves + 1, + previous: { board: [...state.board], score: state.score, moves: state.moves }, + }; +} + +export function undo(state) { + if (!state.previous) return state; + return { ...state, ...state.previous, spawnedIndex: null, previous: null }; +} + +function validSnapshot(value) { + return value !== null && typeof value === "object" + && Array.isArray(value.board) && value.board.length === 16 + && value.board.every(tile => Number.isSafeInteger(tile) && tile >= 0 + && (tile === 0 || (tile >= 2 && Number.isInteger(Math.log2(tile))))) + && Number.isSafeInteger(value.score) && value.score >= 0 + && Number.isSafeInteger(value.moves) && value.moves >= 0; +} + +export function validateSave(value) { + if (!validSnapshot(value) || value.version !== 1 + || !Number.isSafeInteger(value.best) || value.best < value.score + || !(value.spawnedIndex === undefined || value.spawnedIndex === null + || (Number.isInteger(value.spawnedIndex) && value.spawnedIndex >= 0 + && value.spawnedIndex < 16 && [2, 4].includes(value.board[value.spawnedIndex]))) + || !(value.previous === null || validSnapshot(value.previous))) { + throw new Error("Invalid 2048 save. Back up or rename the file before trying again."); + } + return value; +} + +export function viewState(state) { + return { + board: state.board, + score: state.score, + best: state.best, + moves: state.moves, + spawnedIndex: state.spawnedIndex ?? null, + canUndo: state.previous !== null, + won: state.board.some(value => value >= 2048), + gameOver: isGameOver(state.board), + }; +} diff --git a/extensions/keyboard-2048/game.test.mjs b/extensions/keyboard-2048/game.test.mjs new file mode 100644 index 0000000000..cef6bd24cd --- /dev/null +++ b/extensions/keyboard-2048/game.test.mjs @@ -0,0 +1,224 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { mergeLine, slide, newGame, move, undo, isGameOver, validateSave, viewState, DIRECTIONS } from "./game.mjs"; + +const stateWith = board => ({ version: 1, board, score: 0, best: 0, moves: 0, previous: null }); +const zero = () => 0; + +test("merges pairs only once and skips empty cells", () => { + assert.deepEqual(mergeLine([2, 2, 2, 2]), { line: [4, 4, 0, 0], score: 8 }); + assert.deepEqual(mergeLine([2, 2, 4, 0]), { line: [4, 4, 0, 0], score: 4 }); + assert.deepEqual(mergeLine([2, 0, 2, 2]), { line: [4, 2, 0, 0], score: 4 }); + assert.deepEqual(mergeLine([0, 0, 0, 0]), { line: [0, 0, 0, 0], score: 0 }); +}); + +test("all four directions use the correct merge order", () => { + const board = [2, 2, 4, 4, ...Array(12).fill(0)]; + assert.deepEqual(slide(board, "left").board.slice(0, 4), [4, 8, 0, 0]); + assert.deepEqual(slide(board, "right").board.slice(0, 4), [0, 0, 4, 8]); + const vertical = [2, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0]; + assert.deepEqual(slide(vertical, "up").board.filter((_, i) => i % 4 === 0), [4, 8, 0, 0]); + assert.deepEqual(slide(vertical, "down").board.filter((_, i) => i % 4 === 0), [0, 0, 4, 8]); + assert.throws(() => slide(board, "diagonal"), /Invalid direction/); +}); + +test("slides preserve mass and do not mutate their inputs", () => { + let seed = 41; + const random = () => ((seed = (seed * 1664525 + 1013904223) >>> 0) / 2 ** 32); + for (let sample = 0; sample < 200; sample++) { + const board = Array.from({ length: 16 }, () => [0, 2, 4, 8, 16][Math.floor(random() * 5)]); + const original = [...board]; + for (const direction of DIRECTIONS) { + const result = slide(board, direction); + assert.equal(result.board.reduce((a, b) => a + b), board.reduce((a, b) => a + b)); + assert.deepEqual(board, original); + assert.equal(result.changed, result.board.some((value, i) => value !== board[i])); + assert.deepEqual(result.movements.map(tile => tile.from).sort((a, b) => a - b), + board.flatMap((value, index) => value ? [index] : [])); + const reconstructed = Array(16).fill(0); + for (const tile of result.movements) { + assert.equal(tile.value, board[tile.from]); + reconstructed[tile.to] += tile.value; + if (direction === "left" || direction === "right") { + assert.equal(Math.floor(tile.from / 4), Math.floor(tile.to / 4)); + assert.ok(direction === "left" ? tile.to <= tile.from : tile.to >= tile.from); + } else { + assert.equal(tile.from % 4, tile.to % 4); + assert.ok(direction === "up" ? tile.to <= tile.from : tile.to >= tile.from); + } + } + assert.deepEqual(reconstructed, result.board); + assert.equal(result.score, result.merges.reduce((sum, index) => sum + result.board[index], 0)); + for (let index = 0; index < 16; index++) { + const sources = result.movements.filter(tile => tile.to === index); + assert.ok(sources.length <= 2); + assert.equal(result.merges.includes(index), sources.length === 2); + if (sources.length === 2) assert.equal(sources[0].value, sources[1].value); + } + } + } +}); + +test("movement traces track equal-valued cells and prevent chain merges", () => { + const result = slide([2, 0, 2, 4, ...Array(12).fill(0)], "left"); + assert.deepEqual(result.movements, [ + { from: 0, to: 0, value: 2 }, + { from: 2, to: 0, value: 2 }, + { from: 3, to: 1, value: 4 }, + ]); + assert.deepEqual(result.merges, [0]); + assert.deepEqual(result.board.slice(0, 4), [4, 4, 0, 0]); + const pairs = slide([2, 2, 2, 2, ...Array(12).fill(0)], "right"); + assert.deepEqual(pairs.movements, [ + { from: 3, to: 3, value: 2 }, { from: 2, to: 3, value: 2 }, + { from: 1, to: 2, value: 2 }, { from: 0, to: 2, value: 2 }, + ]); + assert.deepEqual(pairs.merges, [3, 2]); + const unchangedCell = slide([2, 2, ...Array(14).fill(0)], "right"); + assert.equal(unchangedCell.board[0], 0); + assert.deepEqual(unchangedCell.movements.map(tile => tile.to), [3, 3]); + const noOp = slide([2, ...Array(15).fill(0)], "left"); + assert.equal(noOp.changed, false); + assert.deepEqual(noOp.movements, [{ from: 0, to: 0, value: 2 }]); + assert.deepEqual(noOp.merges, []); +}); + +test("new game spawns exactly two tiles, with both spawn values supported", () => { + const game = newGame(100, zero); + assert.equal(game.board.filter(Boolean).length, 2); + assert.equal(game.board.reduce((a, b) => a + b), 4); + assert.equal(game.best, 100); + assert.equal(game.previous, null); + assert.deepEqual(newGame(0, () => .99).board.filter(Boolean), [4, 4]); +}); + +test("Undo removes the spawned tile and restores board, score and moves exactly", () => { + const original = { ...stateWith([2, 2, ...Array(14).fill(0)]), score: 12, best: 12, moves: 3 }; + const next = move(original, "left", zero); + assert.equal(next.score, 16); + assert.equal(next.moves, 4); + assert.equal(next.board.reduce((a, b) => a + b), 6); + const restored = undo(next); + assert.deepEqual(restored.board, original.board); + assert.equal(restored.score, original.score); + assert.equal(restored.moves, original.moves); + assert.equal(restored.best, 16); + assert.equal(restored.previous, null); + assert.equal(undo(restored), restored); + assert.deepEqual(original.board, [2, 2, ...Array(14).fill(0)]); +}); + +test("a no-op move neither spawns nor consumes Undo", () => { + const state = stateWith([2, ...Array(15).fill(0)]); + const next = move(state, "right", zero); + const noOp = move(next, "up", () => { throw new Error("Must not spawn"); }); + assert.equal(noOp, next); + assert.deepEqual(undo(noOp).board, state.board); +}); + +test("only the last valid move can be undone", () => { + const first = move(newGame(0, zero), "right", zero); + const second = move(first, "down", zero); + const restored = undo(second); + assert.deepEqual(restored.board, first.board); + assert.equal(restored.score, first.score); + assert.equal(restored.previous, null); + assert.equal(undo(restored), restored); +}); + +test("full board may still merge and terminal board allows Undo", () => { + const lost = [2, 4, 2, 4, 4, 2, 4, 2, 2, 4, 2, 4, 4, 2, 4, 2]; + assert.equal(isGameOver(lost), true); + const mergeable = [...lost]; + mergeable[0] = 4; + assert.equal(isGameOver(mergeable), false); + const playable = [...lost]; + playable[0] = 0; + assert.equal(isGameOver(playable), false); + const last = move(stateWith(playable), "left", zero); + assert.equal(last.previous !== null, true); + assert.deepEqual(undo(last).board, playable); + const beforeLoss = [4, 2, 4, 0, ...lost.slice(4)]; + const terminal = move(stateWith(beforeLoss), "right", zero); + assert.equal(isGameOver(terminal.board), true); + assert.deepEqual(undo(terminal).board, beforeLoss); +}); + +test("2048 is a win, not a blocking game-over condition", () => { + const result = move(stateWith([1024, 1024, ...Array(14).fill(0)]), "left", zero); + assert.equal(viewState(result).won, true); + assert.equal(viewState(result).gameOver, false); + assert.equal(viewState(result).canUndo, true); + assert.equal(viewState(undo(result)).won, false); +}); + +test("saved state including Undo round-trips and malformed saves are rejected", () => { + const state = move(newGame(0, zero), "right", zero); + assert.deepEqual(validateSave(JSON.parse(JSON.stringify(state))), state); + for (const invalid of [null, {}, { ...state, board: [2] }, { ...state, score: -1 }, + { ...state, best: -1 }, { ...state, previous: {} }, + { ...state, board: Array(16).fill(3) }, { ...state, version: 2 }]) { + assert.throws(() => validateSave(invalid), /Invalid 2048 save/); + } +}); + +test("tracks the spawned tile even when its value matches the previous cell", () => { + const before = stateWith([2, ...Array(15).fill(0)]); + const after = move(before, "right", zero); + assert.equal(after.spawnedIndex, 0); + assert.equal(before.board[0], after.board[0]); + assert.equal(after.board[after.spawnedIndex], 2); + assert.equal(after.board[3], 2); + assert.equal(viewState(after).spawnedIndex, 0); + assert.equal(move(after, "up", zero), after); + assert.equal(undo(after).spawnedIndex, null); + assert.equal(newGame().spawnedIndex, null); +}); + +test("spawn metadata identifies only the random tile, not a merged tile", () => { + const after = move(stateWith([2, 2, ...Array(14).fill(0)]), "left", zero); + assert.equal(after.board[0], 4); + assert.equal(after.spawnedIndex, 1); + assert.equal(after.board[1], 2); + const four = move(stateWith([2, ...Array(15).fill(0)]), "right", () => .99); + assert.equal(four.spawnedIndex, 15); + assert.equal(four.board[15], 4); +}); + +test("spawning uses every empty cell regardless of direction and preserves the 90/10 RNG threshold", () => { + const board = Array(16).fill(0); + board[5] = 2; + for (const direction of DIRECTIONS) { + const state = stateWith(board); + const shifted = slide(board, direction); + const empty = shifted.board.flatMap((value, index) => value === 0 ? [index] : []); + for (let slot = 0; slot < empty.length; slot++) { + for (const [roll, value] of [[0, 2], [0.899999, 2], [0.9, 4], [0.999999, 4]]) { + const rolls = [(slot + 0.5) / empty.length, roll]; + let calls = 0; + const after = move(state, direction, () => { + assert.ok(calls < 2, "a valid move makes exactly two RNG calls"); + return rolls[calls++]; + }); + assert.equal(calls, 2); + assert.equal(after.spawnedIndex, empty[slot]); + assert.equal(after.board[empty[slot]], value); + assert.deepEqual(after.board.filter((_, index) => index !== empty[slot]), + shifted.board.filter((_, index) => index !== empty[slot])); + } + } + } +}); + +test("spawn metadata persists without rejecting saves made before this feature", () => { + const after = move(stateWith([2, ...Array(15).fill(0)]), "right", zero); + const saved = validateSave(JSON.parse(JSON.stringify(after))); + assert.equal(viewState(saved).spawnedIndex, 0); + const legacy = { ...after }; + delete legacy.spawnedIndex; + assert.equal(viewState(validateSave(legacy)).spawnedIndex, null); + assert.equal(viewState(undo(legacy)).spawnedIndex, null); + for (const index of [-1, 16, 1.5, "0", 1]) { + assert.throws(() => validateSave({ ...after, spawnedIndex: index }), /Invalid 2048 save/); + } +}); diff --git a/extensions/keyboard-2048/index.html b/extensions/keyboard-2048/index.html new file mode 100644 index 0000000000..2d724fe493 --- /dev/null +++ b/extensions/keyboard-2048/index.html @@ -0,0 +1,74 @@ + + + + + + + 2048 · Keyboard edition + + + + +