From f42f62bcf11de1f354bb0bbd7561eeaa985fbb8d Mon Sep 17 00:00:00 2001 From: NickTheDevOpsGuy Date: Tue, 18 Nov 2025 10:53:23 -0500 Subject: [PATCH 1/2] Adding it all in --- .github/workflows/github-pr.yml | 39 ------------------------- .github/workflows/vercel-production.yml | 18 ++++++++++++ README.md | 8 ++++- eslint.config.js => eslint.config.ts | 0 src/app/components/SnakeCanvas.tsx | 4 ++- 5 files changed, 28 insertions(+), 41 deletions(-) delete mode 100644 .github/workflows/github-pr.yml create mode 100644 .github/workflows/vercel-production.yml rename eslint.config.js => eslint.config.ts (100%) diff --git a/.github/workflows/github-pr.yml b/.github/workflows/github-pr.yml deleted file mode 100644 index 217803f..0000000 --- a/.github/workflows/github-pr.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: PR โ†’ Discord -on: - pull_request: - types: [opened, reopened, ready_for_review] -permissions: - contents: read - pull-requests: read - -jobs: - notify: - # Block untrusted forks from using secrets: - if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} - runs-on: ubuntu-latest - steps: - - name: Send embed to Discord - env: - DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} - TITLE: ${{ github.event.pull_request.title }} - URL: ${{ github.event.pull_request.html_url }} - USER: ${{ github.event.pull_request.user.login }} - BASE: ${{ github.event.pull_request.base.ref }} - HEAD: ${{ github.event.pull_request.head.ref }} - run: | - payload=$(jq -n \ - --arg title "$TITLE" \ - --arg url "$URL" \ - --arg user "$USER" \ - --arg base "$BASE" \ - --arg head "$HEAD" \ - '{embeds:[{ - title:$title, - url:$url, - description:("by @" + $user), - fields:[ - {name:"Branch", value: $head + " โ†’ " + $base, inline:true} - ] - }]}) - curl -sS -X POST -H "Content-Type: application/json" \ - -d "$payload" "$DISCORD_WEBHOOK" diff --git a/.github/workflows/vercel-production.yml b/.github/workflows/vercel-production.yml new file mode 100644 index 0000000..ea6509f --- /dev/null +++ b/.github/workflows/vercel-production.yml @@ -0,0 +1,18 @@ +name: Deploy ๐Ÿ Snake to Vercel + +on: + push: + branches: [develop] + +jobs: + deploy: + runs-on: ubuntu-latest + environment: production + + steps: + - uses: actions/checkout@v4 + - uses: amondnet/vercel-action@v25 + with: + vercel-token: ${{ secrets.VERCEL_TOKEN }} + vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} + vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} diff --git a/README.md b/README.md index 3e0e3af..b4d70c1 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,13 @@ A minimalist Snake clone built with React, TypeScript, and HTML Canvas โ€” focus ![Gameplay preview](./src/app/assets/snake-gameplay.gif) -> Try it yourself โ€” `npm run dev` then open [http://localhost:5173](http://localhost:5173) +--- + +๐ŸŒ Live Demo + +Try Snake here: https://snake-beryl-six.vercel.app/ + +--- ## ๐ŸŽฏ Features diff --git a/eslint.config.js b/eslint.config.ts similarity index 100% rename from eslint.config.js rename to eslint.config.ts diff --git a/src/app/components/SnakeCanvas.tsx b/src/app/components/SnakeCanvas.tsx index 67303f6..451899c 100644 --- a/src/app/components/SnakeCanvas.tsx +++ b/src/app/components/SnakeCanvas.tsx @@ -39,7 +39,9 @@ export default function SnakeCanvas() { try { a.currentTime = 0; void a.play(); - } catch {} + } catch { + console.error("Audio play exception:", err); + } }, []); // random free cell based on current tuning From 9590c7311da2ac517f9604951aa8da17018fa9a8 Mon Sep 17 00:00:00 2001 From: NickTheDevOpsGuy Date: Tue, 18 Nov 2025 10:53:34 -0500 Subject: [PATCH 2/2] style: auto-format with Prettier [skip-precheck] --- eslint.config.ts | 62 +++++++++++++++--------------- src/app/components/SnakeCanvas.tsx | 2 +- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/eslint.config.ts b/eslint.config.ts index cc36ada..1d29924 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -1,82 +1,82 @@ // eslint.config.js -import js from "@eslint/js"; -import react from "eslint-plugin-react"; -import reactHooks from "eslint-plugin-react-hooks"; -import reactRefresh from "eslint-plugin-react-refresh"; -import globals from "globals"; -import tseslint from "typescript-eslint"; +import js from '@eslint/js'; +import react from 'eslint-plugin-react'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; export default [ // ๐Ÿงน Ignore build + coverage folders - { ignores: ["dist", "coverage"] }, + { ignores: ['dist', 'coverage'] }, // โœ… Base + TypeScript + React presets js.configs.recommended, ...tseslint.configs.recommended, - reactHooks.configs["recommended-latest"], + reactHooks.configs['recommended-latest'], reactRefresh.configs.vite, // ๐Ÿงฉ App code (browser) { - files: ["src/**/*.{ts,tsx,js,jsx}"], + files: ['src/**/*.{ts,tsx,js,jsx}'], languageOptions: { parser: tseslint.parser, - ecmaVersion: "latest", - sourceType: "module", + ecmaVersion: 'latest', + sourceType: 'module', parserOptions: { ecmaFeatures: { jsx: true } }, globals: globals.browser, }, plugins: { react }, rules: { // ๐Ÿง  React 17+ no longer requires importing React in JSX - "react/react-in-jsx-scope": "off", - "react/prop-types": "off", + 'react/react-in-jsx-scope': 'off', + 'react/prop-types': 'off', // ๐Ÿงน Lint polish - "@typescript-eslint/no-unused-vars": [ - "warn", - { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }, + '@typescript-eslint/no-unused-vars': [ + 'warn', + { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, ], - "@typescript-eslint/no-unused-expressions": [ - "error", + '@typescript-eslint/no-unused-expressions': [ + 'error', { allowShortCircuit: true, allowTernary: true }, ], - "react-hooks/exhaustive-deps": "warn", + 'react-hooks/exhaustive-deps': 'warn', }, settings: { - react: { version: "detect" }, + react: { version: 'detect' }, }, }, // ๐Ÿงช Tests (Vitest) { - files: ["tests/**/*.{ts,tsx,js,jsx}", "**/*.test.{ts,tsx,js,jsx}"], + files: ['tests/**/*.{ts,tsx,js,jsx}', '**/*.test.{ts,tsx,js,jsx}'], languageOptions: { parser: tseslint.parser, - ecmaVersion: "latest", - sourceType: "module", + ecmaVersion: 'latest', + sourceType: 'module', parserOptions: { ecmaFeatures: { jsx: true } }, globals: { ...globals.browser, ...globals.vitest }, }, plugins: { react }, rules: { - "react/react-in-jsx-scope": "off", + 'react/react-in-jsx-scope': 'off', }, }, // โš™๏ธ Config + scripts (Node env) { files: [ - "*.config.{js,cjs,mjs,ts}", - "vite.config.*", - "vitest.config.*", - "scripts/**", + '*.config.{js,cjs,mjs,ts}', + 'vite.config.*', + 'vitest.config.*', + 'scripts/**', ], languageOptions: { parser: tseslint.parser, - ecmaVersion: "latest", - sourceType: "module", + ecmaVersion: 'latest', + sourceType: 'module', globals: globals.node, }, }, -]; \ No newline at end of file +]; diff --git a/src/app/components/SnakeCanvas.tsx b/src/app/components/SnakeCanvas.tsx index 451899c..656f819 100644 --- a/src/app/components/SnakeCanvas.tsx +++ b/src/app/components/SnakeCanvas.tsx @@ -40,7 +40,7 @@ export default function SnakeCanvas() { a.currentTime = 0; void a.play(); } catch { - console.error("Audio play exception:", err); + console.error('Audio play exception:', err); } }, []);