ratpoison
This commit is contained in:
@@ -305,9 +305,25 @@ export default class GameServer implements Party.Server {
|
|||||||
const attackerUserState = this.gameState.users[authenticatedUserId];
|
const attackerUserState = this.gameState.users[authenticatedUserId];
|
||||||
|
|
||||||
if (!targetUserState || !attackerUserState || targetUserId === authenticatedUserId) {
|
if (!targetUserState || !attackerUserState || targetUserId === authenticatedUserId) {
|
||||||
|
console.log(`Rat Poison: Attempt to poison self or non-existent user (${targetUserId}). Aborting.`);
|
||||||
return; // Cannot poison self or non-existent user
|
return; // Cannot poison self or non-existent user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the target user is currently online
|
||||||
|
if (!this.userConnections.has(targetUserId)) {
|
||||||
|
const attackerConn = this.userConnections.get(authenticatedUserId);
|
||||||
|
if (attackerConn) {
|
||||||
|
attackerConn.send(JSON.stringify({
|
||||||
|
type: 'rat-poison-feedback',
|
||||||
|
message: `${targetUserState.name} is currently offline and cannot be targeted.`
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
console.log(`Rat Poison: Target user ${targetUserState.name} (${targetUserId}) is offline. Attacker: ${attackerUserState.name} (${authenticatedUserId}).`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (targetUserState.ratPoisonImmunityUntil && targetUserState.ratPoisonImmunityUntil > now) {
|
if (targetUserState.ratPoisonImmunityUntil && targetUserState.ratPoisonImmunityUntil > now) {
|
||||||
// Target is immune, notify attacker
|
// Target is immune, notify attacker
|
||||||
@@ -318,6 +334,7 @@ export default class GameServer implements Party.Server {
|
|||||||
message: `${targetUserState.name} is currently immune to rat poison!`
|
message: `${targetUserState.name} is currently immune to rat poison!`
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
console.log(`Rat Poison: Target user ${targetUserState.name} (${targetUserId}) is immune until ${new Date(targetUserState.ratPoisonImmunityUntil).toISOString()}. Attacker: ${attackerUserState.name} (${authenticatedUserId}).`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +342,7 @@ export default class GameServer implements Party.Server {
|
|||||||
const newsUpgrade = ALL_UPGRADES.find(u => u.id === 'news');
|
const newsUpgrade = ALL_UPGRADES.find(u => u.id === 'news');
|
||||||
const newsTitles = newsUpgrade?.newsTitles || [];
|
const newsTitles = newsUpgrade?.newsTitles || [];
|
||||||
if (newsTitles.length === 0) {
|
if (newsTitles.length === 0) {
|
||||||
console.warn('No news titles found for rat poison challenge.');
|
console.warn('Rat Poison: No news titles found for rat poison challenge.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,6 +354,9 @@ export default class GameServer implements Party.Server {
|
|||||||
challengeString,
|
challengeString,
|
||||||
expiresAt,
|
expiresAt,
|
||||||
};
|
};
|
||||||
|
targetUserState.ratPoisonImmunityUntil = expiresAt; // User is immune for the duration of the challenge
|
||||||
|
|
||||||
|
console.log(`Rat Poison: Challenge issued to ${targetUserState.name} (${targetUserId}). String: "${challengeString}", Expires: ${new Date(expiresAt).toISOString()}.`);
|
||||||
|
|
||||||
// Notify target user to start challenge
|
// Notify target user to start challenge
|
||||||
const targetConn = this.userConnections.get(targetUserId);
|
const targetConn = this.userConnections.get(targetUserId);
|
||||||
@@ -352,6 +372,7 @@ export default class GameServer implements Party.Server {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (targetUserState.ratPoisonChallenge && targetUserState.ratPoisonChallenge.expiresAt === expiresAt) {
|
if (targetUserState.ratPoisonChallenge && targetUserState.ratPoisonChallenge.expiresAt === expiresAt) {
|
||||||
// Challenge not solved in time
|
// Challenge not solved in time
|
||||||
|
console.log(`Rat Poison: Challenge for ${targetUserState.name} (${targetUserId}) expired.`);
|
||||||
this.applyRatPoisonPenalty(targetUserId);
|
this.applyRatPoisonPenalty(targetUserId);
|
||||||
this.broadcast();
|
this.broadcast();
|
||||||
}
|
}
|
||||||
@@ -363,12 +384,14 @@ export default class GameServer implements Party.Server {
|
|||||||
handleSolveRatPoison(data: SolveRatPoisonMessage, authenticatedUserId: string) {
|
handleSolveRatPoison(data: SolveRatPoisonMessage, authenticatedUserId: string) {
|
||||||
const userState = this.gameState.users[authenticatedUserId];
|
const userState = this.gameState.users[authenticatedUserId];
|
||||||
if (!userState || !userState.ratPoisonChallenge) {
|
if (!userState || !userState.ratPoisonChallenge) {
|
||||||
|
console.log(`Rat Poison: No active challenge for user ${authenticatedUserId}. Aborting solve attempt.`);
|
||||||
return; // No active challenge
|
return; // No active challenge
|
||||||
}
|
}
|
||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (now > userState.ratPoisonChallenge.expiresAt) {
|
if (now > userState.ratPoisonChallenge.expiresAt) {
|
||||||
// Challenge expired
|
// Challenge expired
|
||||||
|
console.log(`Rat Poison: User ${authenticatedUserId} attempted to solve expired challenge.`);
|
||||||
this.applyRatPoisonPenalty(authenticatedUserId);
|
this.applyRatPoisonPenalty(authenticatedUserId);
|
||||||
} else if (data.challengeString === userState.ratPoisonChallenge.challengeString) {
|
} else if (data.challengeString === userState.ratPoisonChallenge.challengeString) {
|
||||||
// Challenge solved successfully
|
// Challenge solved successfully
|
||||||
@@ -381,8 +404,10 @@ export default class GameServer implements Party.Server {
|
|||||||
message: 'Challenge solved! You are immune for 2 minutes.'
|
message: 'Challenge solved! You are immune for 2 minutes.'
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
console.log(`Rat Poison: User ${authenticatedUserId} successfully solved challenge. Immunity until ${new Date(userState.ratPoisonImmunityUntil).toISOString()}.`);
|
||||||
} else {
|
} else {
|
||||||
// Challenge failed (wrong string)
|
// Challenge failed (wrong string)
|
||||||
|
console.log(`Rat Poison: User ${authenticatedUserId} failed challenge (incorrect string).`);
|
||||||
this.applyRatPoisonPenalty(authenticatedUserId);
|
this.applyRatPoisonPenalty(authenticatedUserId);
|
||||||
const userConn = this.userConnections.get(authenticatedUserId);
|
const userConn = this.userConnections.get(authenticatedUserId);
|
||||||
if (userConn) {
|
if (userConn) {
|
||||||
@@ -400,11 +425,15 @@ export default class GameServer implements Party.Server {
|
|||||||
|
|
||||||
applyRatPoisonPenalty(userId: string) {
|
applyRatPoisonPenalty(userId: string) {
|
||||||
const userState = this.gameState.users[userId];
|
const userState = this.gameState.users[userId];
|
||||||
if (!userState) return;
|
if (!userState) {
|
||||||
|
console.log(`Rat Poison: Attempted to apply penalty to non-existent user ${userId}.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const clicksLost = Math.floor(userState.clicks * 0.30);
|
const clicksLost = Math.floor(userState.clicks * 0.30);
|
||||||
userState.clicks -= clicksLost;
|
userState.clicks -= clicksLost;
|
||||||
userState.ratPoisonImmunityUntil = Date.now() + (20 * 1000); // 20 seconds immunity
|
userState.ratPoisonImmunityUntil = Date.now() + (20 * 1000); // 20 seconds immunity
|
||||||
|
console.log(`Rat Poison: Penalty applied to ${userState.name} (${userId}). Lost ${clicksLost} clicks. Immunity until ${new Date(userState.ratPoisonImmunityUntil).toISOString()}.`);
|
||||||
|
|
||||||
const userConn = this.userConnections.get(userId);
|
const userConn = this.userConnections.get(userId);
|
||||||
if (userConn) {
|
if (userConn) {
|
||||||
|
|||||||
71
src/App.tsx
71
src/App.tsx
@@ -20,7 +20,7 @@ import { RatPoisonChallenge } from './components/RatPoisonChallenge'; // Import
|
|||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const { isSignedIn, isLoaded, userId: clerkUserId } = useAuth(); // Get clerkUserId from useAuth
|
const { isSignedIn, isLoaded, userId: clerkUserId } = useAuth(); // Get clerkUserId from useAuth
|
||||||
const { gameState, sendClick, purchaseUpgrade, userId, sendMascotClickBonus, lastMessage, sendSolveRatPoison } = usePartyKit(); // Renamed sendShoominionClickBonus
|
const { gameState, sendClick, purchaseUpgrade, userId, sendMascotClickBonus, lastMessage, sendSolveRatPoison, ratPoisonChallengeData, ratPoisonResultData, ratPoisonFeedbackData, clearRatPoisonChallenge } = usePartyKit(); // Renamed sendShoominionClickBonus
|
||||||
const [celebrationMessage, setCelebrationMessage] = useState<string | null>(null);
|
const [celebrationMessage, setCelebrationMessage] = useState<string | null>(null);
|
||||||
const [previousMilestones, setPreviousMilestones] = useState<Record<string, boolean>>({});
|
const [previousMilestones, setPreviousMilestones] = useState<Record<string, boolean>>({});
|
||||||
const [mascotEntities, setMascotEntities] = useState<ClickableMascotType[]>([]);
|
const [mascotEntities, setMascotEntities] = useState<ClickableMascotType[]>([]);
|
||||||
@@ -30,10 +30,11 @@ function App() {
|
|||||||
const [location] = useLocation(); // Get current location from wouter
|
const [location] = useLocation(); // Get current location from wouter
|
||||||
const [adminBroadcastMessage, setAdminBroadcastMessage] = useState<string | null>(null); // New state for admin messages
|
const [adminBroadcastMessage, setAdminBroadcastMessage] = useState<string | null>(null); // New state for admin messages
|
||||||
const [showSecretVideo, setShowSecretVideo] = useState(false); // New state for secret video
|
const [showSecretVideo, setShowSecretVideo] = useState(false); // New state for secret video
|
||||||
const [ratPoisonChallenge, setRatPoisonChallenge] = useState<{ challenge: string; expiresAt: number } | null>(null);
|
// Removed local ratPoisonChallenge and ratPoisonResult states, now directly from usePartyKit
|
||||||
const [ratPoisonResult, setRatPoisonResult] = useState<{ success: boolean; message: string } | null>(null);
|
|
||||||
|
|
||||||
const userState = isSignedIn && userId ? gameState?.users[userId] : null;
|
console.log('App.tsx: Current ratPoisonChallengeData:', ratPoisonChallengeData);
|
||||||
|
|
||||||
|
const userState = isSignedIn && userId ? (gameState?.users[userId] || null) : null;
|
||||||
|
|
||||||
// Admin user ID from .env
|
// Admin user ID from .env
|
||||||
const CLERK_ADMIN_USERID = import.meta.env.VITE_CLERK_ADMIN_USERID;
|
const CLERK_ADMIN_USERID = import.meta.env.VITE_CLERK_ADMIN_USERID;
|
||||||
@@ -62,8 +63,7 @@ function App() {
|
|||||||
|
|
||||||
// Effect for secret video
|
// Effect for secret video
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!userState) return; // Ensure userState is not null/undefined
|
if ((userState?.upgrades?.['secretVideo']?.owned || 0) > 0) {
|
||||||
if (userState.upgrades?.['secretVideo']?.owned > 0) {
|
|
||||||
setShowSecretVideo(true);
|
setShowSecretVideo(true);
|
||||||
}
|
}
|
||||||
}, [userState]);
|
}, [userState]);
|
||||||
@@ -77,24 +77,39 @@ function App() {
|
|||||||
if (parsedMessage.type === 'admin-message') {
|
if (parsedMessage.type === 'admin-message') {
|
||||||
setAdminBroadcastMessage(parsedMessage.message);
|
setAdminBroadcastMessage(parsedMessage.message);
|
||||||
setTimeout(() => setAdminBroadcastMessage(null), 7000); // Message disappears after 7 seconds
|
setTimeout(() => setAdminBroadcastMessage(null), 7000); // Message disappears after 7 seconds
|
||||||
} else if (parsedMessage.type === 'rat-poison-challenge') {
|
|
||||||
console.log('App.tsx: Received rat-poison-challenge:', parsedMessage); // Log challenge message
|
|
||||||
setRatPoisonChallenge({ challenge: parsedMessage.challenge, expiresAt: parsedMessage.expiresAt });
|
|
||||||
console.log('App.tsx: ratPoisonChallenge state set to:', { challenge: parsedMessage.challenge, expiresAt: parsedMessage.expiresAt }); // Log state after setting
|
|
||||||
} else if (parsedMessage.type === 'rat-poison-result') {
|
|
||||||
setRatPoisonResult({ success: parsedMessage.success, message: parsedMessage.message });
|
|
||||||
setTimeout(() => setRatPoisonResult(null), 5000); // Result message disappears after 5 seconds
|
|
||||||
} else if (parsedMessage.type === 'rat-poison-feedback') {
|
|
||||||
// Display feedback to the attacker
|
|
||||||
setAdminBroadcastMessage(parsedMessage.message); // Re-using admin message display for simplicity
|
|
||||||
setTimeout(() => setAdminBroadcastMessage(null), 5000);
|
|
||||||
}
|
}
|
||||||
|
// rat-poison-challenge, rat-poison-result, rat-poison-feedback are now handled by dedicated states from usePartyKit
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to parse last message in App.tsx:', error);
|
console.error('Failed to parse last message in App.tsx:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [lastMessage]);
|
}, [lastMessage]);
|
||||||
|
|
||||||
|
// Effect for displaying rat poison result
|
||||||
|
useEffect(() => {
|
||||||
|
if (ratPoisonResultData) {
|
||||||
|
console.log('App.tsx: ratPoisonResultData updated:', ratPoisonResultData);
|
||||||
|
// The modal will be rendered directly by ratPoisonChallengeData
|
||||||
|
// This effect is just for the temporary result message
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
// Clear the result data after a delay
|
||||||
|
// This requires a function in usePartyKit to clear it, or pass a prop to RatPoisonChallenge
|
||||||
|
// For now, we'll just let the message disappear.
|
||||||
|
}, 5000);
|
||||||
|
return () => clearTimeout(timeout);
|
||||||
|
}
|
||||||
|
}, [ratPoisonResultData]);
|
||||||
|
|
||||||
|
// Effect for displaying rat poison feedback
|
||||||
|
useEffect(() => {
|
||||||
|
if (ratPoisonFeedbackData) {
|
||||||
|
console.log('App.tsx: ratPoisonFeedbackData updated:', ratPoisonFeedbackData);
|
||||||
|
setAdminBroadcastMessage(ratPoisonFeedbackData); // Re-using admin message display for simplicity
|
||||||
|
const timeout = setTimeout(() => setAdminBroadcastMessage(null), 5000);
|
||||||
|
return () => clearTimeout(timeout);
|
||||||
|
}
|
||||||
|
}, [ratPoisonFeedbackData]);
|
||||||
|
|
||||||
// Effect to keep gameStateRef updated
|
// Effect to keep gameStateRef updated
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
gameStateRef.current = gameState;
|
gameStateRef.current = gameState;
|
||||||
@@ -105,7 +120,7 @@ function App() {
|
|||||||
if (!userState) return;
|
if (!userState) return;
|
||||||
|
|
||||||
const friendBoostUpgrade = UPGRADES.find(u => u.id === 'friendBoost') as Upgrade | undefined;
|
const friendBoostUpgrade = UPGRADES.find(u => u.id === 'friendBoost') as Upgrade | undefined;
|
||||||
const ownedFriendBoost = userState.upgrades['friendBoost']?.owned || 0;
|
const ownedFriendBoost = (userState?.upgrades?.['friendBoost']?.owned || 0);
|
||||||
|
|
||||||
if (timeoutRef.current) {
|
if (timeoutRef.current) {
|
||||||
clearTimeout(timeoutRef.current);
|
clearTimeout(timeoutRef.current);
|
||||||
@@ -193,7 +208,11 @@ function App() {
|
|||||||
|
|
||||||
const handleSolveRatPoison = (solvedString: string) => {
|
const handleSolveRatPoison = (solvedString: string) => {
|
||||||
sendSolveRatPoison(solvedString);
|
sendSolveRatPoison(solvedString);
|
||||||
setRatPoisonChallenge(null);
|
// The challenge data will be cleared by usePartyKit or by the modal's onClose
|
||||||
|
// To explicitly clear the modal, we can set ratPoisonChallengeData to null here.
|
||||||
|
// However, it's better if usePartyKit handles clearing it after a successful solve/fail.
|
||||||
|
// For now, we'll leave it as is, assuming the server will eventually clear it.
|
||||||
|
clearRatPoisonChallenge(); // Clear the challenge data when solved or closed
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isLoaded || !gameState) {
|
if (!isLoaded || !gameState) {
|
||||||
@@ -253,10 +272,10 @@ function App() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Rat Poison Result Message */}
|
{/* Rat Poison Result Message */}
|
||||||
{ratPoisonResult && (
|
{ratPoisonResultData && (
|
||||||
<div className="fixed top-40 left-0 right-0 z-50 flex justify-center pt-8">
|
<div className="fixed top-40 left-0 right-0 z-50 flex justify-center pt-8">
|
||||||
<div className={`px-8 py-4 rounded-full text-2xl font-bold shadow-2xl animate-pulse border-4 border-white ${ratPoisonResult.success ? 'bg-green-600' : 'bg-red-600'}`}>
|
<div className={`px-8 py-4 rounded-full text-2xl font-bold shadow-2xl animate-pulse border-4 border-white ${ratPoisonResultData.success ? 'bg-green-600' : 'bg-red-600'}`}>
|
||||||
{ratPoisonResult.message}
|
{ratPoisonResultData.message}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -349,12 +368,12 @@ function App() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Rat Poison Challenge Modal */}
|
{/* Rat Poison Challenge Modal */}
|
||||||
{ratPoisonChallenge && (
|
{ratPoisonChallengeData && (
|
||||||
<RatPoisonChallenge
|
<RatPoisonChallenge
|
||||||
challengeString={ratPoisonChallenge.challenge}
|
challengeString={ratPoisonChallengeData.challengeString}
|
||||||
expiresAt={ratPoisonChallenge.expiresAt}
|
expiresAt={ratPoisonChallengeData.expiresAt}
|
||||||
onSolve={handleSolveRatPoison}
|
onSolve={handleSolveRatPoison}
|
||||||
onClose={() => setRatPoisonChallenge(null)}
|
onClose={clearRatPoisonChallenge} // Call the clear function from usePartyKit
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ export function usePartyKit() {
|
|||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
const [gameState, setGameState] = useState<GameState | null>(null);
|
const [gameState, setGameState] = useState<GameState | null>(null);
|
||||||
const [socket, setSocket] = useState<PartySocket | null>(null);
|
const [socket, setSocket] = useState<PartySocket | null>(null);
|
||||||
const [lastMessage, setLastMessage] = useState<string | null>(null); // New state for last message
|
const [lastMessage, setLastMessage] = useState<string | null>(null);
|
||||||
|
// ratPoisonChallengeData will now be derived from gameState
|
||||||
|
const [ratPoisonResultData, setRatPoisonResultData] = useState<{ success: boolean; message: string } | null>(null);
|
||||||
|
const [ratPoisonFeedbackData, setRatPoisonFeedbackData] = useState<string | null>(null);
|
||||||
// Generate a persistent guest ID if the user is not signed in
|
// Generate a persistent guest ID if the user is not signed in
|
||||||
const [guestId] = useState(() => {
|
const [guestId] = useState(() => {
|
||||||
let storedGuestId = localStorage.getItem('bozo_guest_id');
|
let storedGuestId = localStorage.getItem('bozo_guest_id');
|
||||||
@@ -59,8 +62,15 @@ export function usePartyKit() {
|
|||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
if (data.type === 'game-state') {
|
if (data.type === 'game-state') {
|
||||||
setGameState(data.state);
|
setGameState(data.state);
|
||||||
|
} else if (data.type === 'rat-poison-result') {
|
||||||
|
console.log('usePartyKit: Setting ratPoisonResultData:', { success: data.success, message: data.message });
|
||||||
|
setRatPoisonResultData({ success: data.success, message: data.message });
|
||||||
|
} else if (data.type === 'rat-poison-feedback') {
|
||||||
|
console.log('usePartyKit: Setting ratPoisonFeedbackData:', data.message);
|
||||||
|
setRatPoisonFeedbackData(data.message);
|
||||||
}
|
}
|
||||||
setLastMessage(event.data); // Store the raw last message
|
// No longer setting ratPoisonChallengeData directly here, it will be derived from gameState
|
||||||
|
setLastMessage(event.data); // Still store the raw last message for other general purposes if needed
|
||||||
};
|
};
|
||||||
|
|
||||||
setSocket(ws);
|
setSocket(ws);
|
||||||
@@ -161,6 +171,11 @@ export function usePartyKit() {
|
|||||||
}
|
}
|
||||||
}, [socket, isSignedIn, user, getToken]);
|
}, [socket, isSignedIn, user, getToken]);
|
||||||
|
|
||||||
|
const clearRatPoisonChallenge = useCallback(() => {
|
||||||
|
setRatPoisonResultData(null);
|
||||||
|
setRatPoisonFeedbackData(null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
gameState,
|
gameState,
|
||||||
sendClick,
|
sendClick,
|
||||||
@@ -174,5 +189,9 @@ export function usePartyKit() {
|
|||||||
editUser,
|
editUser,
|
||||||
throwRatPoison,
|
throwRatPoison,
|
||||||
sendSolveRatPoison,
|
sendSolveRatPoison,
|
||||||
|
ratPoisonResultData,
|
||||||
|
ratPoisonFeedbackData,
|
||||||
|
ratPoisonChallengeData: gameState?.users[currentUserId || '']?.ratPoisonChallenge || null,
|
||||||
|
clearRatPoisonChallenge,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
"strict": true,
|
"strict": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noFallthroughCasesInSwitch": true
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"types": ["node"]
|
||||||
},
|
},
|
||||||
"include": ["vite.config.ts"]
|
"include": ["vite.config.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user