ps99-api 2.3.3 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/.github/workflows/release-on-main.yml +1 -2
  2. package/.idea/node-ps99-api.iml +1 -0
  3. package/.idea/runConfigurations/test_changing.xml +1 -1
  4. package/README.md +1 -1
  5. package/debug_currency.json +57 -0
  6. package/debug_goals.json +271 -0
  7. package/dist/ps99-api.d.ts +2 -0
  8. package/dist/ps99-api.js +4 -1
  9. package/dist/ps99-api.js.map +1 -1
  10. package/dist/request-client/axios.js +6 -1
  11. package/dist/request-client/axios.js.map +1 -1
  12. package/dist/responses/collection/achievement.d.ts +2 -0
  13. package/dist/responses/collection/guild-battle.d.ts +2 -4
  14. package/dist/responses/collection/index.d.ts +1 -0
  15. package/dist/responses/collection/index.js +15 -0
  16. package/dist/responses/collection/index.js.map +1 -1
  17. package/dist/responses/collection/rank.d.ts +1 -0
  18. package/dist/responses/collection/rarity.d.ts +1 -0
  19. package/dist/responses/collection/seed.d.ts +1 -0
  20. package/dist/responses/exists.d.ts +2 -0
  21. package/example-web/react/package-lock.json +1504 -1470
  22. package/example-web/react2/package-lock.json +3082 -2759
  23. package/example-web/react2/package.json +6 -1
  24. package/example-web/react2/public/assets/gold_variant_icon.png +0 -0
  25. package/example-web/react2/public/assets/hot_cocoa_egg.png +0 -0
  26. package/example-web/react2/public/index.html +34 -31
  27. package/example-web/react2/src/App.tsx +6 -9
  28. package/example-web/react2/src/assets/guild_placeholder.png +0 -0
  29. package/example-web/react2/src/components/AchievementsComponent.tsx +78 -30
  30. package/example-web/react2/src/components/BoostsComponent.tsx +18 -14
  31. package/example-web/react2/src/components/BoothsComponent.tsx +24 -22
  32. package/example-web/react2/src/components/BoxesComponent.tsx +46 -21
  33. package/example-web/react2/src/components/BuffsComponent.tsx +83 -13
  34. package/example-web/react2/src/components/CharmsComponent.tsx +47 -29
  35. package/example-web/react2/src/components/CollectionConfigIndex.tsx +398 -35
  36. package/example-web/react2/src/components/CollectionsIndex.tsx +132 -23
  37. package/example-web/react2/src/components/CollectionsLayout.tsx +50 -0
  38. package/example-web/react2/src/components/CurrencyComponent.tsx +59 -50
  39. package/example-web/react2/src/components/DynamicCollectionConfigData.tsx +178 -11
  40. package/example-web/react2/src/components/EggsComponent.tsx +77 -44
  41. package/example-web/react2/src/components/EnchantsComponent.tsx +84 -34
  42. package/example-web/react2/src/components/FishingRodsComponent.tsx +38 -31
  43. package/example-web/react2/src/components/Footer.tsx +75 -18
  44. package/example-web/react2/src/components/FruitsComponent.tsx +41 -25
  45. package/example-web/react2/src/components/GenericFetchComponent.tsx +40 -22
  46. package/example-web/react2/src/components/GuildBattlesComponent.tsx +93 -65
  47. package/example-web/react2/src/components/Header.tsx +5 -37
  48. package/example-web/react2/src/components/HomePage.tsx +16 -16
  49. package/example-web/react2/src/components/HoverboardsComponent.tsx +25 -37
  50. package/example-web/react2/src/components/ImageComponent.tsx +255 -45
  51. package/example-web/react2/src/components/ItemCard.tsx +240 -0
  52. package/example-web/react2/src/components/LootboxesComponent.tsx +24 -16
  53. package/example-web/react2/src/components/MasteryComponent.tsx +93 -37
  54. package/example-web/react2/src/components/MerchantsComponent.tsx +46 -28
  55. package/example-web/react2/src/components/MiscItemsComponent.tsx +28 -26
  56. package/example-web/react2/src/components/PetsComponent.tsx +115 -46
  57. package/example-web/react2/src/components/PotionsComponent.tsx +53 -36
  58. package/example-web/react2/src/components/RandomEventsComponent.tsx +39 -35
  59. package/example-web/react2/src/components/RanksComponent.tsx +187 -71
  60. package/example-web/react2/src/components/RarityComponent.tsx +124 -13
  61. package/example-web/react2/src/components/RebirthsComponent.tsx +37 -26
  62. package/example-web/react2/src/components/SecretRoomsComponent.tsx +7 -13
  63. package/example-web/react2/src/components/SeedsComponent.tsx +45 -32
  64. package/example-web/react2/src/components/ShovelsComponent.tsx +23 -18
  65. package/example-web/react2/src/components/Sidebar.tsx +105 -0
  66. package/example-web/react2/src/components/SprinklersComponent.tsx +27 -19
  67. package/example-web/react2/src/components/Tooltip.tsx +36 -0
  68. package/example-web/react2/src/components/UltimatesComponent.tsx +29 -24
  69. package/example-web/react2/src/components/UpgradesComponent.tsx +99 -55
  70. package/example-web/react2/src/components/WateringCansComponent.tsx +23 -21
  71. package/example-web/react2/src/components/WorldsComponent.tsx +27 -24
  72. package/example-web/react2/src/components/XPPotionsComponent.tsx +29 -19
  73. package/example-web/react2/src/components/ZoneFlagsComponent.tsx +27 -23
  74. package/example-web/react2/src/components/ZonesComponent.tsx +56 -73
  75. package/example-web/react2/src/constants/collectionIcons.ts +29 -0
  76. package/example-web/react2/src/context/CollectionDataContext.tsx +62 -0
  77. package/example-web/react2/src/hooks/useExpandableList.ts +38 -0
  78. package/example-web/react2/src/hooks/useItemResolution.ts +351 -0
  79. package/example-web/react2/src/index.css +257 -0
  80. package/example-web/react2/src/index.tsx +2 -1
  81. package/example-web/react2/temp_model.rbxm +0 -0
  82. package/example-web/react2/webpack.config.js +103 -47
  83. package/package.json +11 -11
  84. package/ranks.json +1 -0
  85. package/repro_collection_fetch.ts +33 -0
  86. package/repro_image_fetch.ts +50 -0
  87. package/src/__tests__/__snapshots__/ps99-api-changes.ts.snap +34841 -10439
  88. package/src/__tests__/__snapshots__/ps99-api-live.ts.snap +160667 -67217
  89. package/src/ps99-api.ts +9 -5
  90. package/src/request-client/axios.ts +6 -2
  91. package/src/responses/collection/achievement.ts +2 -0
  92. package/src/responses/collection/guild-battle.ts +2 -4
  93. package/src/responses/collection/index.ts +1 -0
  94. package/src/responses/collection/rank.ts +1 -0
  95. package/src/responses/collection/rarity.ts +1 -0
  96. package/src/responses/collection/seed.ts +1 -0
  97. package/src/responses/exists.ts +2 -0
  98. package/tsconfig.json +1 -1
  99. package/example-web/react2/public/service-worker.js +0 -63
@@ -0,0 +1,351 @@
1
+ import { useState, useEffect } from "react";
2
+ import {
3
+ PetSimulator99API,
4
+ EnchantmentData,
5
+ PotionData,
6
+ MiscItemData,
7
+ CurrencyData,
8
+ FruitData,
9
+ HoverboardData,
10
+ BoothData,
11
+ ZoneFlagData,
12
+ SeedData,
13
+ RandomEventData,
14
+ LootboxData,
15
+ UltimateData,
16
+ RarityData,
17
+ } from "ps99-api";
18
+
19
+ export interface ItemData {
20
+ icon: string | null;
21
+ rarity: any | null;
22
+ }
23
+
24
+ export const useItemResolution = () => {
25
+ const [collections, setCollections] = useState({
26
+ enchants: [] as EnchantmentData[],
27
+ potions: [] as PotionData[],
28
+ miscItems: [] as MiscItemData[],
29
+ currencies: [] as CurrencyData[],
30
+ fruits: [] as FruitData[],
31
+ hoverboards: [] as HoverboardData[],
32
+ booths: [] as BoothData[],
33
+ zoneFlags: [] as ZoneFlagData[],
34
+ seeds: [] as SeedData[],
35
+ randomEvents: [] as RandomEventData[],
36
+ lootboxes: [] as LootboxData[],
37
+ ultimates: [] as UltimateData[],
38
+ rarities: [] as RarityData[],
39
+ pets: [] as any[], // PetData
40
+ eggs: [] as any[], // EggData
41
+ });
42
+ const [loading, setLoading] = useState(true);
43
+
44
+ useEffect(() => {
45
+ const api = new PetSimulator99API();
46
+ const fetchData = async () => {
47
+ try {
48
+ const results = await Promise.allSettled([
49
+ api.getCollection("Enchants"),
50
+ api.getCollection("Potions"),
51
+ api.getCollection("MiscItems"),
52
+ api.getCollection("Currency"),
53
+ api.getCollection("Fruits"),
54
+ api.getCollection("Hoverboards"),
55
+ api.getCollection("Booths"),
56
+ api.getCollection("ZoneFlags"),
57
+ api.getCollection("Seeds"),
58
+ api.getCollection("RandomEvents"),
59
+ api.getCollection("Lootboxes"),
60
+ api.getCollection("Ultimates"),
61
+ api.getCollection("Rarity"),
62
+ api.getCollection("Pets"),
63
+ api.getCollection("Eggs"),
64
+ ]);
65
+
66
+ const getData = (result: PromiseSettledResult<any>) =>
67
+ result.status === 'fulfilled' && result.value.status === 'ok' ? result.value.data : [];
68
+
69
+ const [
70
+ enchants, potions, miscItems, currencies, fruits, hoverboards,
71
+ booths, zoneFlags, seeds, randomEvents, lootboxes, ultimates, rarities,
72
+ pets, eggs
73
+ ] = results.map(getData);
74
+
75
+ // Manual Injection for Missing Items
76
+ miscItems.push({
77
+ category: "MiscItems",
78
+ configName: "Superior Mini Chest",
79
+ configData: {
80
+ id: "76",
81
+ DisplayName: "Superior Mini Chest",
82
+ Icon: "17602729261", // rbxassetid://17602729261
83
+ Rarity: { DisplayName: "Exclusive", RarityNumber: 5 }
84
+ }
85
+ });
86
+
87
+ // Force rebuild check
88
+ console.log("Seeds (v2) loaded:", seeds.length);
89
+ seeds.forEach((s: any) => {
90
+ if (s.configName.includes("Insta") || s.configData.DisplayName.includes("Insta")) {
91
+ console.log("Found Insta Seed:", s.configName, s.configData.DisplayName);
92
+ }
93
+ });
94
+
95
+ setCollections({
96
+ enchants, potions, miscItems, currencies, fruits, hoverboards,
97
+ booths, zoneFlags, seeds, randomEvents, lootboxes, ultimates, rarities,
98
+ pets, eggs
99
+ });
100
+ } catch (e) {
101
+ console.error("Failed to load item collections", e);
102
+ } finally {
103
+ setLoading(false);
104
+ }
105
+ };
106
+ fetchData();
107
+ }, []);
108
+
109
+ const resolveItem = (id: string, tn?: number): ItemData & { name?: string } | null => {
110
+ try {
111
+ const {
112
+ enchants, potions, miscItems, currencies, fruits, hoverboards,
113
+ booths, zoneFlags, seeds, randomEvents, lootboxes, ultimates,
114
+ pets, eggs
115
+ } = collections;
116
+
117
+ const numericId = parseInt(id);
118
+ const isNumeric = !isNaN(numericId);
119
+
120
+ // Manual Mappings for Breakables/Events
121
+ if (id === "76") { // Superior Mini Chest
122
+ // Icon found via investigation: rbxassetid://17602729261
123
+ return { icon: "rbxassetid://17602729261", rarity: null, name: "Superior Mini Chest" };
124
+ }
125
+ if (id === "78" || id === "Hot Cocoa Egg") { // Hot Cocoa Egg
126
+ // Local Asset
127
+ return { icon: "/node-ps99-api/assets/hot_cocoa_egg.png", rarity: null, name: "Hot Cocoa Egg" };
128
+ }
129
+ if (id === "40") { // Lucky Block
130
+ const block = miscItems.find(i => i.configName.includes("Lucky Block")) || randomEvents.find(i => i.configName.includes("Lucky Block"));
131
+ const icon = (block?.configData as any).icon || block?.configData.Icon;
132
+ if (icon) return { icon: icon, rarity: (block?.configData as any).rarity || (block?.configData as any).Rarity, name: "Lucky Block" };
133
+ }
134
+ if (id === "41") { // Piñata
135
+ const pinata = miscItems.find(i => i.configName.includes("Pinata")) || randomEvents.find(i => i.configName.includes("Pinata"));
136
+ const icon = (pinata?.configData as any).icon || pinata?.configData.Icon;
137
+ if (icon) return { icon: icon, rarity: (pinata?.configData as any).rarity || (pinata?.configData as any).Rarity, name: "Piñata" };
138
+ }
139
+
140
+ if (id === "Clan Gift") {
141
+ const box = lootboxes.find(i => i.configName === "Clan Gift");
142
+ if (box?.configData.Icon) return { icon: box.configData.Icon, rarity: (box.configData as any).Rarity, name: "Clan Gift" };
143
+ }
144
+
145
+ // Pets
146
+ // Check exact match, or "Huge [name]", or numeric ID
147
+ const pet = pets.find(i => i.configName === id || i.configData.name === id || (isNumeric && (i.configData as any)._index === numericId));
148
+ if (pet) {
149
+ const data = pet.configData as any;
150
+ let icon = data.thumbnail;
151
+ if (tn === 1 && data.goldenThumbnail) icon = data.goldenThumbnail;
152
+ // if (tn === 2) ... Rainbow usually just uses regular or golden with shader, but for icon usage we might just use thumbnail
153
+
154
+ // If we have specific golden/rainbow thumbnails in data, use them.
155
+ // The PetsComponent used data.goldenThumbnail for pt=1.
156
+
157
+ return {
158
+ icon: icon.startsWith("rbxassetid://") ? icon : `rbxassetid://${icon}`,
159
+ rarity: data.rarity || { DisplayName: "Basic", RarityNumber: 1 }, // Default if missing
160
+ name: data.name
161
+ };
162
+ }
163
+
164
+ // Eggs
165
+ const egg = eggs.find(i => i.configName === id || (i.configData as any).DisplayName === id || (isNumeric && (i.configData as any)._index === numericId));
166
+ if (egg) {
167
+ const data = egg.configData as any;
168
+ const icon = data.icon || data.Icon;
169
+ return {
170
+ icon: icon?.startsWith("rbxassetid://") ? icon : `rbxassetid://${icon}`,
171
+ rarity: data.rarity || data.Rarity,
172
+ name: data.DisplayName || data.name || egg.configName
173
+ };
174
+ }
175
+
176
+
177
+ if (id === "21" || id === "34") {
178
+ console.log(`Resolving special ID: ${id}, numeric: ${numericId}, tn: ${tn}`);
179
+ const sampleCurrency = currencies[0] as any;
180
+ console.log("Sample Currency _index:", sampleCurrency?._index, "Name:", sampleCurrency?.configName);
181
+ const match = currencies.find(i => (i.configData as any)._index === numericId);
182
+ console.log("Found Currency Match:", match?.configName);
183
+ }
184
+
185
+ // Currency
186
+ const currency = currencies.find(i => i.configName === id || i.configName === `Currency | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
187
+ if (currency?.configData.Tiers) {
188
+ const tierIndex = tn ? tn - 1 : 0;
189
+ const tier = currency.configData.Tiers[tierIndex] || currency.configData.Tiers[0];
190
+ if (tier) {
191
+ const icon = tier.tinyImage || tier.orbImage;
192
+ if (icon) return { icon, rarity: null, name: (currency.configData as any).DisplayName || id };
193
+ }
194
+ }
195
+
196
+ // MiscItems
197
+ const misc = miscItems.find(i => i.configName === id || i.configName === `MiscItem | ${id}` || (i.configData as any).DisplayName === id || (isNumeric && (i.configData as any)._index === numericId));
198
+ if (misc) {
199
+ const data = misc.configData as any;
200
+ const icon = data.icon || data.Icon;
201
+ if (icon) return { icon, rarity: data.rarity || data.Rarity, name: data.DisplayName || data.name || misc.configName };
202
+ }
203
+
204
+ // Enchants
205
+ const enchant = enchants.find(i => i.configName === id || i.configName === `Enchant | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
206
+ if (enchant) {
207
+ const data = enchant.configData as any;
208
+ const tier = data.Tiers && data.Tiers[tn ? tn - 1 : 0] ? data.Tiers[tn ? tn - 1 : 0] : data;
209
+ const icon = tier.icon || tier.Icon || data.icon || data.Icon;
210
+ return {
211
+ icon: icon?.startsWith("rbxassetid://") ? icon : `rbxassetid://${icon}`,
212
+ rarity: tier.rarity || tier.Rarity || data.rarity || data.Rarity,
213
+ name: tier.DisplayName || data.DisplayName || data.Name || enchant.configName
214
+ };
215
+ }
216
+
217
+ // Potions
218
+ const potion = potions.find(i => i.configName === id || i.configName === `Potion | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
219
+ if (potion) {
220
+ const data = potion.configData as any;
221
+ const tier = data.Tiers && data.Tiers[tn ? tn - 1 : 0] ? data.Tiers[tn ? tn - 1 : 0] : data;
222
+ const icon = tier.icon || tier.Icon || data.icon || data.Icon;
223
+ return {
224
+ icon: icon?.startsWith("rbxassetid://") ? icon : `rbxassetid://${icon}`,
225
+ rarity: tier.rarity || tier.Rarity || data.rarity || data.Rarity,
226
+ name: tier.DisplayName || data.DisplayName || data.Name || potion.configName
227
+ };
228
+ }
229
+
230
+ // Fruits
231
+ const fruit = fruits.find(i => i.configName === id || i.configName === `Fruit | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
232
+ if (fruit?.configData.Icon) return { icon: fruit.configData.Icon, rarity: (fruit.configData as any).Rarity, name: (fruit.configData as any).DisplayName };
233
+
234
+ // Hoverboards
235
+ const hoverboard = hoverboards.find(i => i.configName === id || i.configName === `Hoverboard | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
236
+ if (hoverboard?.configData.Icon) return { icon: hoverboard.configData.Icon, rarity: (hoverboard.configData as any).Rarity, name: (hoverboard.configData as any).DisplayName };
237
+
238
+ // Booths
239
+ const booth = booths.find(i => i.configName === id || i.configName === `Booth | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
240
+ if (booth?.configData.Icon) return { icon: booth.configData.Icon, rarity: (booth.configData as any).Rarity, name: (booth.configData as any).DisplayName };
241
+
242
+ // ZoneFlags
243
+ const flag = zoneFlags.find(i => i.configName === id || i.configName === `ZoneFlag | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
244
+ if (flag?.configData.Icon) return { icon: flag.configData.Icon, rarity: (flag.configData as any).Rarity, name: (flag.configData as any).DisplayName };
245
+
246
+ // Seeds
247
+ const seed = seeds.find(i => i.configName === id || i.configName === `Seed | ${id}` || (i.configData as any).DisplayName === id || (isNumeric && (i.configData as any)._index === numericId));
248
+ if (seed?.configData.Icon) return { icon: seed.configData.Icon, rarity: (seed.configData as any).Rarity, name: (seed.configData as any).DisplayName };
249
+
250
+ // RandomEvents
251
+ const event = randomEvents.find(i => i.configName === id || i.configName === `RandomEvent | ${id}` || i.configData.Name === id || (isNumeric && (i.configData as any)._index === numericId));
252
+ if (event?.configData.Icon) return { icon: event.configData.Icon, rarity: null, name: event.configData.Name };
253
+
254
+ // Lootboxes
255
+ const box = lootboxes.find(i => i.configName === id || i.configName === `Lootbox | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
256
+ if (box) {
257
+ const data = box.configData as any;
258
+ const icon = data.icon || data.Icon;
259
+ if (icon) return { icon, rarity: data.rarity || data.Rarity, name: data.DisplayName || data.name };
260
+ }
261
+
262
+ // Ultimates
263
+ const ult = ultimates.find(i => i.configName === id || i.configName === `Ultimate | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
264
+ if (ult?.configData.Icon) return { icon: ult.configData.Icon, rarity: (ult.configData as any).Rarity, name: (ult.configData as any).DisplayName };
265
+
266
+ return null;
267
+ } catch (e) {
268
+ console.error("resolveItem crashed:", e);
269
+ return null;
270
+ }
271
+ };
272
+
273
+ const getRarityColor = (rarityData: any) => {
274
+ if (!rarityData) return null;
275
+ if (rarityData.Color) return rarityData.Color;
276
+ if (rarityData._id) {
277
+ const r = collections.rarities.find(r => r.configData._id === rarityData._id || r.configData.DisplayName === rarityData._id);
278
+ if (r?.configData.Color) return r.configData.Color;
279
+ }
280
+ return null;
281
+ };
282
+
283
+ const resolveIcon = (itemData: any): string | null => {
284
+ try {
285
+ if (!itemData) return null;
286
+
287
+ if (itemData.name === "Hot Cocoa Egg" || itemData.DisplayName === "Hot Cocoa Egg") {
288
+ return "/node-ps99-api/assets/hot_cocoa_egg.png";
289
+ }
290
+
291
+ // Check for nested Tiers/Icons common in Enchants, Potions, Currency, Boxes
292
+ if (itemData.Tiers && Array.isArray(itemData.Tiers) && itemData.Tiers.length > 0) {
293
+ const firstTier = itemData.Tiers[0];
294
+ if (firstTier.Icon) return firstTier.Icon;
295
+ if (firstTier.icon) return firstTier.icon;
296
+ if (firstTier.orbImage) return firstTier.orbImage;
297
+ if (firstTier.tinyImage) return firstTier.tinyImage;
298
+ if (firstTier.imageOutline) return firstTier.imageOutline;
299
+ }
300
+
301
+ if (itemData.Icons && Array.isArray(itemData.Icons) && itemData.Icons.length > 0) {
302
+ const firstIcon = itemData.Icons[0];
303
+ if (firstIcon.Icon) return firstIcon.Icon;
304
+ if (firstIcon.icon) return firstIcon.icon;
305
+ }
306
+
307
+ // Handle Buffs (AssociatedItemID)
308
+ if (itemData.AssociatedItemID) {
309
+ const associatedId = itemData.AssociatedItemID;
310
+ // Check MiscItems first (most common for buffs like Toy Ball, Squeaky Toy)
311
+ const misc = collections.miscItems.find(i => i.configName === associatedId);
312
+ if (misc) {
313
+ const miscIcon = resolveIcon(misc.configData); // Recursive resolve
314
+ if (miscIcon) return miscIcon;
315
+ }
316
+ // Could check other collections if needed
317
+ }
318
+
319
+ return (
320
+ itemData.icon ||
321
+ itemData.Icon ||
322
+ itemData.thumbnail ||
323
+ itemData.image ||
324
+ itemData.texture ||
325
+ itemData.orbImage ||
326
+ itemData.titanicIcon ||
327
+ itemData.petIcon ||
328
+ itemData.eggIcon ||
329
+ itemData.enchantIcon ||
330
+ itemData.potionIcon ||
331
+ itemData.fruitIcon ||
332
+ itemData.toyIcon ||
333
+ itemData.charmIcon ||
334
+ itemData.boothIcon ||
335
+ itemData.flagIcon ||
336
+ itemData.keyIcon ||
337
+ itemData.seedIcon ||
338
+ itemData.bookIcon ||
339
+ itemData.giftIcon ||
340
+ itemData.currencyIcon ||
341
+ itemData.miscIcon ||
342
+ null
343
+ );
344
+ } catch (e) {
345
+ console.error("resolveIcon crashed:", e);
346
+ return null;
347
+ }
348
+ };
349
+
350
+ return { loading, resolveItem, getRarityColor, resolveIcon };
351
+ };
@@ -0,0 +1,257 @@
1
+ /* Import a cute, rounded font */
2
+ @import url('https://fonts.googleapis.com/css2?family=Fredoka:wght@300;400;500;600;700&display=swap');
3
+
4
+ :root {
5
+ --bg-color: #e0f7fa;
6
+ --primary-color: #4fc3f7;
7
+ --secondary-color: #ff8a65;
8
+ --accent-color: #ffd54f;
9
+ --text-color: #37474f;
10
+ --card-bg: #ffffff;
11
+ --border-radius: 16px;
12
+ --shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
13
+ --border-width: 3px;
14
+ --border-color: #263238;
15
+ }
16
+
17
+ html,
18
+ body,
19
+ #root {
20
+ height: 100%;
21
+ margin: 0;
22
+ overflow: hidden;
23
+ /* Prevent body scroll, let apps handle it */
24
+ }
25
+
26
+ #root {
27
+ display: flex;
28
+ flex-direction: column;
29
+ }
30
+
31
+ body {
32
+ margin: 0;
33
+ font-family: 'Fredoka', sans-serif;
34
+ background-color: var(--bg-color);
35
+ color: var(--text-color);
36
+ -webkit-font-smoothing: antialiased;
37
+ -moz-osx-font-smoothing: grayscale;
38
+ }
39
+
40
+ h1,
41
+ h2,
42
+ h3,
43
+ h4,
44
+ h5,
45
+ h6 {
46
+ color: var(--text-color);
47
+ text-transform: uppercase;
48
+ letter-spacing: 1px;
49
+ text-shadow: 2px 2px 0px white;
50
+ }
51
+
52
+ a {
53
+ color: var(--primary-color);
54
+ text-decoration: none;
55
+ font-weight: bold;
56
+ }
57
+
58
+ a:hover {
59
+ text-decoration: underline;
60
+ }
61
+
62
+ /* Button Styles */
63
+ .game-button {
64
+ display: inline-block;
65
+ padding: 12px 24px;
66
+ background-color: var(--primary-color);
67
+ color: white;
68
+ font-size: 1.2rem;
69
+ font-weight: bold;
70
+ border: var(--border-width) solid var(--border-color);
71
+ border-radius: var(--border-radius);
72
+ box-shadow: 4px 4px 0px var(--border-color);
73
+ transition: all 0.1s ease;
74
+ cursor: pointer;
75
+ text-transform: uppercase;
76
+ text-decoration: none !important;
77
+ }
78
+
79
+ .game-button:hover {
80
+ transform: translate(-2px, -2px);
81
+ box-shadow: 6px 6px 0px var(--border-color);
82
+ background-color: #29b6f6;
83
+ }
84
+
85
+ .game-button:active {
86
+ transform: translate(2px, 2px);
87
+ box-shadow: 0px 0px 0px var(--border-color);
88
+ }
89
+
90
+ .game-button.secondary {
91
+ background-color: var(--secondary-color);
92
+ }
93
+
94
+ .game-button.accent {
95
+ background-color: var(--accent-color);
96
+ color: var(--text-color);
97
+ }
98
+
99
+ /* Grids */
100
+ .item-grid {
101
+ display: grid;
102
+ grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
103
+ gap: 16px;
104
+ padding: 20px;
105
+ }
106
+
107
+ .collection-grid {
108
+ display: grid;
109
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
110
+ gap: 20px;
111
+ padding: 20px;
112
+ }
113
+
114
+ /* Cards */
115
+ .game-card {
116
+ background: var(--card-bg);
117
+ border: var(--border-width) solid var(--border-color);
118
+ border-radius: var(--border-radius);
119
+ padding: 16px;
120
+ text-align: center;
121
+ box-shadow: 4px 4px 0px rgba(0, 0, 0, 0.1);
122
+ transition: transform 0.2s;
123
+ display: flex;
124
+ flex-direction: column;
125
+ align-items: center;
126
+ justify-content: center;
127
+ position: relative;
128
+ }
129
+
130
+ .game-card:hover {
131
+ transform: translateY(-5px);
132
+ box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.15);
133
+ }
134
+
135
+ .game-card img {
136
+ max-width: 100%;
137
+ height: auto;
138
+ border-radius: 8px;
139
+ margin-bottom: 8px;
140
+ }
141
+
142
+ /* Tooltips */
143
+ .tooltip-container {
144
+ position: relative;
145
+ display: inline-block;
146
+ }
147
+
148
+ .tooltip-popover {
149
+ position: absolute;
150
+ bottom: 120%;
151
+ left: 50%;
152
+ transform: translateX(-50%);
153
+ background-color: rgba(255, 255, 255, 0.95);
154
+ border: 2px solid var(--border-color);
155
+ border-radius: 8px;
156
+ padding: 10px;
157
+ min-width: 200px;
158
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
159
+ z-index: 1000;
160
+ pointer-events: none;
161
+ /* Prevent tooltip from blocking interaction */
162
+ font-size: 0.9rem;
163
+ line-height: 1.4;
164
+ text-align: left;
165
+ }
166
+
167
+ .tooltip-popover h4 {
168
+ margin: 0 0 5px 0;
169
+ font-size: 1rem;
170
+ color: var(--secondary-color);
171
+ }
172
+
173
+
174
+ .badge {
175
+ background: #e0f2f1;
176
+ color: #00695c;
177
+ padding: 4px 8px;
178
+ border-radius: 12px;
179
+ font-size: 0.8em;
180
+ font-weight: bold;
181
+ border: 1px solid #b2dfdb;
182
+ }
183
+
184
+ /* Animations */
185
+ @keyframes sheen {
186
+ 0% {
187
+ transform: translateX(-100%) rotate(45deg);
188
+ }
189
+
190
+ 20%,
191
+ 100% {
192
+ transform: translateX(100%) rotate(45deg);
193
+ }
194
+ }
195
+
196
+ .sheen-effect {
197
+ position: relative;
198
+ overflow: hidden;
199
+ }
200
+
201
+ .sheen-effect::after {
202
+ content: '';
203
+ position: absolute;
204
+ top: 0;
205
+ left: 0;
206
+ width: 100%;
207
+ height: 100%;
208
+ background: linear-gradient(120deg, transparent, rgba(255, 255, 255, 0.6), transparent);
209
+ transform: translateX(-100%);
210
+ animation: sheen 3s infinite;
211
+ }
212
+
213
+ @keyframes shine {
214
+ 0% {
215
+ left: -100%;
216
+ }
217
+
218
+ 100% {
219
+ left: 100%;
220
+ }
221
+ }
222
+
223
+ /* Header & Footer */
224
+ .game-header {
225
+ padding: 1rem;
226
+ background-color: var(--primary-color);
227
+ border-bottom: var(--border-width) solid var(--border-color);
228
+ display: flex;
229
+ justify-content: space-between;
230
+ align-items: center;
231
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
232
+ }
233
+
234
+ .game-header a {
235
+ color: white;
236
+ text-decoration: none;
237
+ text-shadow: 2px 2px 0px var(--border-color);
238
+ }
239
+
240
+ .game-header ol {
241
+ display: flex;
242
+ list-style: none;
243
+ padding: 0;
244
+ margin: 0;
245
+ }
246
+
247
+ .game-header ol li {
248
+ margin: 0 0.5em;
249
+ }
250
+
251
+ .game-footer {
252
+ text-align: center;
253
+ padding: 1rem;
254
+ margin-top: 2rem;
255
+ background-color: var(--card-bg);
256
+ border-top: var(--border-width) solid var(--border-color);
257
+ }
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import "./index.css";
2
3
  import { createRoot } from "react-dom/client";
3
4
  import App from "./App";
4
5
  import * as serviceWorkerRegistration from "./serviceWorkerRegistration";
@@ -8,4 +9,4 @@ const root = createRoot(container!);
8
9
  root.render(<App />);
9
10
 
10
11
  // Register the service worker
11
- serviceWorkerRegistration.register();
12
+ serviceWorkerRegistration.unregister();