This commit is contained in:
2025-08-03 00:17:54 +05:30
parent d936bf4608
commit 2be48dd7a8
10 changed files with 170 additions and 17 deletions

110
package-lock.json generated
View File

@@ -8,6 +8,7 @@
"name": "vite-react-typescript-starter", "name": "vite-react-typescript-starter",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@clerk/clerk-react": "^5.38.1",
"lucide-react": "^0.344.0", "lucide-react": "^0.344.0",
"partykit": "^0.0.115", "partykit": "^0.0.115",
"partysocket": "^1.1.4", "partysocket": "^1.1.4",
@@ -347,6 +348,66 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@clerk/clerk-react": {
"version": "5.38.1",
"resolved": "https://registry.npmjs.org/@clerk/clerk-react/-/clerk-react-5.38.1.tgz",
"integrity": "sha512-IOn/Raet3jwkug8P/gLMe2nsw2wKllWGOGPFOAaaYxbXfIZ8MPngNv2/MMgVRF7cAX1UwrmU1PzrLNtBJ/EHPQ==",
"license": "MIT",
"dependencies": {
"@clerk/shared": "^3.17.0",
"@clerk/types": "^4.72.0",
"tslib": "2.8.1"
},
"engines": {
"node": ">=18.17.0"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-0",
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0"
}
},
"node_modules/@clerk/shared": {
"version": "3.17.0",
"resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-3.17.0.tgz",
"integrity": "sha512-eYbA0xmKG1DluFmdVykXiElgZGTpCruEyXmIBAwokpxypd5nOpDsS1xvEKwYvZieLTZkFz21Z3Y6HdDI5cPxBQ==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@clerk/types": "^4.72.0",
"dequal": "2.0.3",
"glob-to-regexp": "0.4.1",
"js-cookie": "3.0.5",
"std-env": "^3.9.0",
"swr": "2.3.4"
},
"engines": {
"node": ">=18.17.0"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-0",
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/@clerk/types": {
"version": "4.72.0",
"resolved": "https://registry.npmjs.org/@clerk/types/-/types-4.72.0.tgz",
"integrity": "sha512-SEkgiQNeTstC0/mQjHCGBEyX0/ALyWAa5QZBBvVOok204r48MLipfIKsXQhyWE2Hk6FIo5WT6YyqD36jaxUEIw==",
"license": "MIT",
"dependencies": {
"csstype": "3.1.3"
},
"engines": {
"node": ">=18.17.0"
}
},
"node_modules/@cloudflare/workerd-darwin-64": { "node_modules/@cloudflare/workerd-darwin-64": {
"version": "1.20240718.0", "version": "1.20240718.0",
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20240718.0.tgz", "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20240718.0.tgz",
@@ -2040,8 +2101,7 @@
"node_modules/csstype": { "node_modules/csstype": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
"dev": true
}, },
"node_modules/data-uri-to-buffer": { "node_modules/data-uri-to-buffer": {
"version": "2.0.2", "version": "2.0.2",
@@ -2077,6 +2137,15 @@
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/didyoumean": { "node_modules/didyoumean": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@@ -2963,6 +3032,15 @@
"jiti": "bin/jiti.js" "jiti": "bin/jiti.js"
} }
}, },
"node_modules/js-cookie": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
"integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
"license": "MIT",
"engines": {
"node": ">=14"
}
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -3963,6 +4041,12 @@
"get-source": "^2.0.12" "get-source": "^2.0.12"
} }
}, },
"node_modules/std-env": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz",
"integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==",
"license": "MIT"
},
"node_modules/stoppable": { "node_modules/stoppable": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz",
@@ -4139,6 +4223,19 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/swr": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/swr/-/swr-2.3.4.tgz",
"integrity": "sha512-bYd2lrhc+VarcpkgWclcUi92wYCpOgMws9Sd1hG1ntAu0NEy+14CbotuFjshBU2kt9rYj9TSmDcybpxpeTU1fg==",
"license": "MIT",
"dependencies": {
"dequal": "^2.0.3",
"use-sync-external-store": "^1.4.0"
},
"peerDependencies": {
"react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/system-architecture": { "node_modules/system-architecture": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/system-architecture/-/system-architecture-0.1.0.tgz", "resolved": "https://registry.npmjs.org/system-architecture/-/system-architecture-0.1.0.tgz",
@@ -4387,6 +4484,15 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/use-sync-external-store": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/util-deprecate": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

View File

@@ -10,6 +10,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@clerk/clerk-react": "^5.38.1",
"lucide-react": "^0.344.0", "lucide-react": "^0.344.0",
"partykit": "^0.0.115", "partykit": "^0.0.115",
"partysocket": "^1.1.4", "partysocket": "^1.1.4",

View File

@@ -1,3 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type * as Party from 'partykit/server';
interface GameState { interface GameState {
totalClicks: number; totalClicks: number;
users: Record<string, { name: string; clicks: number; lastSeen: number }>; users: Record<string, { name: string; clicks: number; lastSeen: number }>;
@@ -47,7 +50,7 @@ const MILESTONES = [
]; ];
export default class GameServer implements Party.Server { export default class GameServer implements Party.Server {
constructor(readonly party: Party.Party) {} constructor(readonly party: Party.Party) { }
gameState: GameState = { gameState: GameState = {
totalClicks: 0, totalClicks: 0,
@@ -65,11 +68,11 @@ export default class GameServer implements Party.Server {
autoClickInterval?: NodeJS.Timeout; autoClickInterval?: NodeJS.Timeout;
onConnect(conn: Party.Connection, ctx: Party.ConnectionContext) { onConnect(conn: Party.Connection, _ctx: Party.ConnectionContext) {
conn.send(JSON.stringify({ type: 'game-state', state: this.gameState })); conn.send(JSON.stringify({ type: 'game-state', state: this.gameState }));
} }
onMessage(message: string, sender: Party.Connection) { onMessage(message: string, _sender: Party.Connection) {
const data = JSON.parse(message) as Message; const data = JSON.parse(message) as Message;
switch (data.type) { switch (data.type) {
@@ -140,10 +143,10 @@ export default class GameServer implements Party.Server {
Object.entries(this.gameState.upgrades).forEach(([upgradeId, upgrade]) => { Object.entries(this.gameState.upgrades).forEach(([upgradeId, upgrade]) => {
const config = UPGRADES[upgradeId as keyof typeof UPGRADES]; const config = UPGRADES[upgradeId as keyof typeof UPGRADES];
if (config.clickBonus) { if ('clickBonus' in config && config.clickBonus) {
this.gameState.clickMultiplier += config.clickBonus * upgrade.owned; this.gameState.clickMultiplier += config.clickBonus * upgrade.owned;
} }
if (config.autoClickRate) { if ('autoClickRate' in config && config.autoClickRate) {
this.gameState.autoClickRate += config.autoClickRate * upgrade.owned; this.gameState.autoClickRate += config.autoClickRate * upgrade.owned;
} }
}); });

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { usePartyKit } from './hooks/usePartyKit'; import { usePartyKit } from './hooks/usePartyKit';
import { ClickButton } from './components/ClickButton'; import { ClickButton } from './components/ClickButton';
import { Counter } from './components/Counter'; import { Counter } from './components/Counter';
@@ -7,6 +7,7 @@ import { Milestones } from './components/Milestones';
import { Leaderboard } from './components/Leaderboard'; import { Leaderboard } from './components/Leaderboard';
import { Background } from './components/Background'; import { Background } from './components/Background';
import { MILESTONES } from './config/milestones'; import { MILESTONES } from './config/milestones';
import { SignedIn, SignedOut, SignInButton, UserButton } from '@clerk/clerk-react';
function App() { function App() {
const { gameState, sendClick, purchaseUpgrade, userId } = usePartyKit(); const { gameState, sendClick, purchaseUpgrade, userId } = usePartyKit();
@@ -50,6 +51,13 @@ function App() {
<div className="min-h-screen relative overflow-x-hidden"> <div className="min-h-screen relative overflow-x-hidden">
<Background background={gameState.currentBackground} /> <Background background={gameState.currentBackground} />
{/* User Button */}
<div className="absolute top-4 right-4 z-50">
<SignedIn>
<UserButton />
</SignedIn>
</div>
{/* Celebration Message */} {/* Celebration Message */}
{celebrationMessage && ( {celebrationMessage && (
<div className="fixed top-0 left-0 right-0 z-50 flex justify-center pt-8"> <div className="fixed top-0 left-0 right-0 z-50 flex justify-center pt-8">
@@ -125,6 +133,20 @@ function App() {
</div> </div>
))} ))}
</div> </div>
{/* If not logged in, show popup overlaying the game to login */}
<SignedOut>
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
<div className="bg-white p-8 rounded-lg shadow-lg text-center">
<h2 className="text-2xl font-bold mb-4">Welcome to Bozo Clicker!</h2>
<p className="mb-6">Please sign in to start clicking!</p>
<SignInButton mode="modal">
<button className="bg-blue-500 text-white px-6 py-2 rounded-lg hover:bg-blue-600 transition">
Sign In
</button>
</SignInButton>
</div>
</div>
</SignedOut>
</div> </div>
); );
} }

View File

@@ -2,7 +2,7 @@ import { useState, useEffect, useCallback } from 'react';
import PartySocket from 'partysocket'; import PartySocket from 'partysocket';
import { GameState } from '../types'; import { GameState } from '../types';
const PARTY_HOST = import.meta.env.DEV ? 'localhost:1998' : 'bozo-clicker.your-username.partykit.dev'; const PARTY_HOST = import.meta.env.DEV ? 'localhost:1999' : 'bozo-clicker.your-username.partykit.dev';
export function usePartyKit() { export function usePartyKit() {
const [gameState, setGameState] = useState<GameState | null>(null); const [gameState, setGameState] = useState<GameState | null>(null);

View File

@@ -2,9 +2,18 @@ import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import App from './App.tsx'; import App from './App.tsx';
import './index.css'; import './index.css';
import { ClerkProvider } from '@clerk/clerk-react'
// Import your Publishable Key
const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
if (!PUBLISHABLE_KEY) {
throw new Error('Missing Publishable Key')
}
createRoot(document.getElementById('root')!).render( createRoot(document.getElementById('root')!).render(
<StrictMode> <StrictMode>
<ClerkProvider publishableKey={PUBLISHABLE_KEY}>
<App /> <App />
</ClerkProvider>
</StrictMode> </StrictMode>
); );

12
todo.md Normal file
View File

@@ -0,0 +1,12 @@
Basically add things from <https://neal.fun/stimulation-clicker/>
thewre should be an news section available as redeemable using clicks with fun news titles
Dylan the Diabolical Dutchman
[BOZO]
— 00:00
Like, at first it's only a bozo you click, after a while, codebugs pop up as bonuses, later, you can combo codebugs and shoominions as they pop up. Later, you get a mascot storm, where mascots of all types fly over the screen from left to right, and you have to click as many as possible.
And some more
Auth integration not completed with clerk, needs partykit to be integrated with clerk