Compare commits

..

7 Commits

Author SHA1 Message Date
Gavin McDonald
269abd237e middleware -> proxy 2025-12-06 17:40:39 -05:00
Gavin McDonald
52636017a5 up to date, audited 391 packages in 15s
144 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
2025-12-06 17:37:07 -05:00
Gavin McDonald
9ca34540e8 fix tilt settings 2025-07-08 16:04:30 -04:00
Gavin McDonald
6e312d5d2e touch tilt 2025-07-08 15:49:02 -04:00
Gavin McDonald
11245bf4d8 fix mobile spacing and scrolling 2025-07-08 08:24:40 -04:00
Gavin McDonald
62c3b7b557 clean up fonts 2025-07-08 07:52:43 -04:00
Gavin McDonald
01a05d2aa1 update screenshot 2025-07-04 14:38:53 -04:00
15 changed files with 1324 additions and 850 deletions

View File

@@ -63,14 +63,15 @@ export function AppProvider({ children }: { children: ReactNode }) {
const { dmID } = gameData;
const isDM = !!dmID;
const settings = { ...gameData.settings, ...localSettings };
const appInterface = {
gameData,
isDM,
noGame,
selectCardIndex,
settings: { ...gameData.settings, ...localSettings },
tilts: reduceTilts(gameData, localTilt),
settings,
tilts: reduceTilts(gameData, localTilt, settings),
emitFlip,
emitSettings,
emitRedraw,

View File

@@ -24,7 +24,7 @@ export default function GamePage() {
return noGame ? (
<NotFound />
) : (
<main className="min-h-screen flex flex-col items-center justify-center gap-4 bg-[url('/img/table3.png')] bg-cover bg-center">
<main className="h-dvh flex flex-col items-center justify-center gap-4 bg-[url('/img/table3.png')] bg-cover bg-center">
<SpectatorLink />
<Settings />
<TarokkaGrid />

View File

@@ -1,26 +1,14 @@
import type { Metadata } from 'next';
import { Pirata_One, Eagle_Lake, Cinzel_Decorative } from 'next/font/google';
import { Eagle_Lake } from 'next/font/google';
import { AppProvider } from '@/app/AppContext';
import './globals.css';
const pirataOne = Pirata_One({
variable: '--font-pirata',
subsets: ['latin'],
weight: '400',
});
const eagleLake = Eagle_Lake({
variable: '--font-eagle-lake',
subsets: ['latin'],
weight: '400',
});
const cinzel = Cinzel_Decorative({
variable: '--font-cinzel',
subsets: ['latin'],
weight: '400',
});
export const metadata: Metadata = {
title: 'Tarokka',
description: 'Fortune telling for D&Ds Curse of Strahd',
@@ -37,11 +25,8 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html
lang="en"
className={`${pirataOne.variable} ${eagleLake.variable} ${cinzel.variable} antialiased`}
>
<body className={`${eagleLake.className} antialiased`}>
<html lang="en" className={`${eagleLake.variable} antialiased overscroll-none`}>
<body className={`${eagleLake.className} antialiased h-dvh`}>
<AppProvider>{children}</AppProvider>
</body>
</html>

View File

@@ -16,7 +16,7 @@ export default function Home() {
};
return (
<main className="min-h-screen flex justify-center items-center text-yellow-400 bg-[url('/img/table3.png')] bg-cover bg-center">
<main className="flex justify-center items-center h-dvh text-yellow-400 bg-[url('/img/table3.png')] bg-cover bg-center">
<div className="flex flex-col items-center gap-8 text-center">
<h1 className="text-5xl font-bold text-center text-primary">Tarokka</h1>
<p className="text-l text-center w-[350px] m-auto">

View File

@@ -54,7 +54,7 @@ export default function Card({ card, cardIndex }: CardProps) {
return (
<ToolTip content={tooltip || getTooltip()}>
<TiltCard
className={`h-[21vh] w-[15vh] relative perspective transition-transform duration-200 z-0 hover:z-10 hover:scale-150 ${isDM ? 'cursor-pointer' : ''} `}
className={`h-[21vh] w-[15vh] max-w-[30vw] relative perspective transition-transform duration-200 z-0 hover:z-10 hover:scale-150 ${isDM ? 'cursor-pointer' : ''} `}
cardIndex={cardIndex}
>
<div

View File

@@ -29,7 +29,7 @@ export default function CardStyle({ className }: { className?: string }) {
flex justify-center
cursor-pointer
w-full px-3 py-2
text-xs font-medium
text-xs font-medium capitalize
border border-yellow-500
transition hover:text-yellow-300 hover:drop-shadow-[0_0_3px_#ffd700]
${settings.cardStyle === option ? 'bg-slate-700 text-yellow-300 font-extrabold' : 'bg-slate-800 hover:bg-slate-700'}

View File

@@ -2,7 +2,6 @@
import { useState } from 'react';
import { CircleX, Settings as Gear } from 'lucide-react';
import { Cinzel_Decorative } from 'next/font/google';
import { useAppContext } from '@/app/AppContext';
import Scrim from '@/components/Scrim';
@@ -12,18 +11,12 @@ import ExternalLinks from './ExternalLinks';
import GameLinks from './GameLinks';
import Permissions from './Permissions';
const cinzel = Cinzel_Decorative({
variable: '--font-cinzel',
subsets: ['latin'],
weight: '400',
});
export default function Settings() {
const [open, setOpen] = useState(false);
const { isDM } = useAppContext();
return (
<div className={`fixed top-4 right-4 z-25 ${cinzel.className}`}>
<div className={`fixed top-4 right-4 z-25`}>
<Scrim
clickAction={() => setOpen((prev) => !prev)}
className={`transition-all duration-250 ${open ? 'pointer-events-auto opacity-100' : 'pointer-events-none opacity-0'}`}

View File

@@ -15,7 +15,7 @@ export default function TarokkaGrid() {
const arrangeCards = (_cell: unknown, index: number) => cards[cardMap[index]];
return (
<div className="grid grid-cols-3 grid-rows-3 gap-8 w-fit mx-auto">
<div className="grid grid-cols-3 grid-rows-3 gap-2 sm:gap-4 md:gap-8 w-fit mx-auto">
{Array.from({ length: 9 })
.map(arrangeCards)
.map((card, index) => (

View File

@@ -41,13 +41,14 @@ export default function TiltCard({
card.style.transform = ZERO_ROTATION;
}, [untilt]);
const handleMouseMove = throttle((e: React.MouseEvent) => {
const handleTilt = (x: number, y: number) => {
const card = cardRef.current;
if (!card) return;
const rect = card.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
x -= rect.left;
y -= rect.top;
const centerX = rect.width / 2;
const centerY = rect.height / 2;
@@ -65,6 +66,27 @@ export default function TiltCard({
};
setLocalTilt(newTilt);
};
const handleMouseMove = throttle((e: React.MouseEvent) => {
handleTilt(e.clientX, e.clientY);
}, thirtyFPS);
const handleTouchMove = throttle((e: React.TouchEvent) => {
const card = cardRef.current;
const touch = e.touches[0];
if (card && touch) {
const rect = card.getBoundingClientRect();
const x = touch.clientX;
const y = touch.clientY;
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
handleTilt(x, y);
} else {
setLocalTilt([]);
}
}
}, thirtyFPS);
const handleMouseLeave = () => {
@@ -75,6 +97,8 @@ export default function TiltCard({
<div
className={`group ${className}`}
onMouseMove={settings.tilt ? handleMouseMove : undefined}
onTouchMove={settings.tilt ? handleTouchMove : undefined}
onTouchEnd={handleMouseLeave}
onMouseLeave={handleMouseLeave}
>
<div

2048
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
export function proxy(request: NextRequest) {
const url = request.nextUrl;
const slug = url.pathname.slice(1);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

After

Width:  |  Height:  |  Size: 5.9 MiB

View File

@@ -1,5 +1,5 @@
import { log, validTilt } from '@/tools';
import { GameUpdate, Tilt } from '@/types';
import { validTilt } from '@/tools';
import { GameUpdate, Settings, Tilt } from '@/types';
const combineTilts = (tilts: Tilt[]) =>
tilts.reduce(
@@ -13,13 +13,15 @@ const combineTilts = (tilts: Tilt[]) =>
{ pX: 0, pY: 0, rX: 0, rY: 0, count: 0 },
);
export function reduceTilts(gameData: GameUpdate, localTilt: Tilt[]): Tilt[] {
export function reduceTilts(
gameData: GameUpdate,
localTilt: Tilt[],
{ tilt, remoteTilt }: Settings,
): Tilt[] {
const remoteTilts = gameData.tilts;
const tiltEnabled = gameData.settings.tilt;
const remoteTiltEnabled = gameData.settings.remoteTilt;
if (!tiltEnabled) return [];
if (!remoteTiltEnabled) return Array.from({ length: 5 }, (_, i) => localTilt[i]);
if (!tilt) return [];
if (!remoteTilt) return localTilt;
return Array.from({ length: 5 }, (_, i) => (localTilt[i] ? [localTilt[i]] : []))
.map((cardTilts, cardIndex) => [...remoteTilts[cardIndex], ...cardTilts])

View File

@@ -1,4 +1,9 @@
import { Tilt } from '@/types';
export const validTilt = ({ percentX, percentY, rotateX, rotateY }: Tilt) =>
percentX >= 0 && percentY >= 0 && !!rotateX && !!rotateY;
export const validTilt = (tilt: Tilt) => {
if (!tilt) return false;
const { percentX, percentY, rotateX, rotateY } = tilt;
return percentX >= 0 && percentY >= 0 && !!rotateX && !!rotateY;
};

View File

@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "ES2020",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
@@ -12,7 +16,7 @@
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
@@ -20,10 +24,20 @@
}
],
"paths": {
"@/*": ["./*"]
"@/*": [
"./*"
]
},
"strictNullChecks": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}