type logos
Browse files- classes/aquatic.png +2 -2
- classes/beast.png +2 -2
- classes/bug.png +2 -2
- classes/cuisine.png +2 -2
- classes/culture.png +2 -2
- classes/flora.png +2 -2
- classes/machina.png +2 -2
- classes/mineral.png +2 -2
- classes/space.png +2 -2
- classes/structure.png +2 -2
- src/lib/components/Pages/Pictuary.svelte +58 -1
- src/lib/components/UI/TypeBadge.svelte +49 -6
- src/lib/utils/typeMigration.ts +88 -0
classes/aquatic.png
CHANGED
![]() |
Git LFS Details
|
![]() |
Git LFS Details
|
classes/beast.png
CHANGED
![]() |
Git LFS Details
|
![]() |
Git LFS Details
|
classes/bug.png
CHANGED
![]() |
Git LFS Details
|
![]() |
Git LFS Details
|
classes/cuisine.png
CHANGED
![]() |
Git LFS Details
|
![]() |
Git LFS Details
|
classes/culture.png
CHANGED
![]() |
Git LFS Details
|
![]() |
Git LFS Details
|
classes/flora.png
CHANGED
![]() |
Git LFS Details
|
![]() |
Git LFS Details
|
classes/machina.png
CHANGED
![]() |
Git LFS Details
|
![]() |
Git LFS Details
|
classes/mineral.png
CHANGED
![]() |
Git LFS Details
|
![]() |
Git LFS Details
|
classes/space.png
CHANGED
![]() |
Git LFS Details
|
![]() |
Git LFS Details
|
classes/structure.png
CHANGED
![]() |
Git LFS Details
|
![]() |
Git LFS Details
|
src/lib/components/Pages/Pictuary.svelte
CHANGED
@@ -11,6 +11,8 @@
|
|
11 |
import PicletDetail from '../Piclets/PicletDetail.svelte';
|
12 |
import AddToRosterDialog from '../Piclets/AddToRosterDialog.svelte';
|
13 |
import ViewAll from './ViewAll.svelte';
|
|
|
|
|
14 |
|
15 |
let rosterPiclets: PicletInstance[] = $state([]);
|
16 |
let storagePiclets: PicletInstance[] = $state([]);
|
@@ -31,9 +33,43 @@
|
|
31 |
});
|
32 |
return map;
|
33 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
|
35 |
async function loadPiclets() {
|
36 |
try {
|
|
|
|
|
|
|
37 |
const allInstances = await getAllPicletInstances();
|
38 |
|
39 |
// Filter based on rosterPosition instead of isInRoster
|
@@ -139,7 +175,7 @@
|
|
139 |
onBack={() => viewAllMode = null}
|
140 |
/>
|
141 |
{:else}
|
142 |
-
<div class="pictuary-page">
|
143 |
{#if isLoading}
|
144 |
<div class="loading-state">
|
145 |
<div class="spinner"></div>
|
@@ -247,6 +283,23 @@
|
|
247 |
overflow-y: auto;
|
248 |
-webkit-overflow-scrolling: touch;
|
249 |
background: white;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
250 |
}
|
251 |
|
252 |
|
@@ -259,6 +312,8 @@
|
|
259 |
height: calc(100% - 100px);
|
260 |
padding: 2rem;
|
261 |
text-align: center;
|
|
|
|
|
262 |
}
|
263 |
|
264 |
.spinner {
|
@@ -290,6 +345,8 @@
|
|
290 |
|
291 |
.content {
|
292 |
padding: 0 1rem 100px;
|
|
|
|
|
293 |
}
|
294 |
|
295 |
section {
|
|
|
11 |
import PicletDetail from '../Piclets/PicletDetail.svelte';
|
12 |
import AddToRosterDialog from '../Piclets/AddToRosterDialog.svelte';
|
13 |
import ViewAll from './ViewAll.svelte';
|
14 |
+
import { migratePicletTypes } from '$lib/utils/typeMigration';
|
15 |
+
import { PicletType } from '$lib/types/picletTypes';
|
16 |
|
17 |
let rosterPiclets: PicletInstance[] = $state([]);
|
18 |
let storagePiclets: PicletInstance[] = $state([]);
|
|
|
33 |
});
|
34 |
return map;
|
35 |
});
|
36 |
+
|
37 |
+
// Get the most common type in the roster for background theming
|
38 |
+
let dominantType = $derived(() => {
|
39 |
+
if (rosterPiclets.length === 0) {
|
40 |
+
return PicletType.BEAST; // Default fallback
|
41 |
+
}
|
42 |
+
|
43 |
+
// Count type occurrences
|
44 |
+
const typeCounts = new Map<PicletType, number>();
|
45 |
+
rosterPiclets.forEach(piclet => {
|
46 |
+
if (piclet.primaryType) {
|
47 |
+
const count = typeCounts.get(piclet.primaryType) || 0;
|
48 |
+
typeCounts.set(piclet.primaryType, count + 1);
|
49 |
+
}
|
50 |
+
});
|
51 |
+
|
52 |
+
// Find the most common type
|
53 |
+
let maxCount = 0;
|
54 |
+
let mostCommonType = PicletType.BEAST;
|
55 |
+
typeCounts.forEach((count, type) => {
|
56 |
+
if (count > maxCount) {
|
57 |
+
maxCount = count;
|
58 |
+
mostCommonType = type;
|
59 |
+
}
|
60 |
+
});
|
61 |
+
|
62 |
+
return mostCommonType;
|
63 |
+
});
|
64 |
+
|
65 |
+
// Get background image path for the dominant type
|
66 |
+
let backgroundImagePath = $derived(`/classes/${dominantType}.png`);
|
67 |
|
68 |
async function loadPiclets() {
|
69 |
try {
|
70 |
+
// Run type migration first time to fix any invalid types
|
71 |
+
await migratePicletTypes();
|
72 |
+
|
73 |
const allInstances = await getAllPicletInstances();
|
74 |
|
75 |
// Filter based on rosterPosition instead of isInRoster
|
|
|
175 |
onBack={() => viewAllMode = null}
|
176 |
/>
|
177 |
{:else}
|
178 |
+
<div class="pictuary-page" style="--bg-image: url('{backgroundImagePath}')">
|
179 |
{#if isLoading}
|
180 |
<div class="loading-state">
|
181 |
<div class="spinner"></div>
|
|
|
283 |
overflow-y: auto;
|
284 |
-webkit-overflow-scrolling: touch;
|
285 |
background: white;
|
286 |
+
position: relative;
|
287 |
+
}
|
288 |
+
|
289 |
+
.pictuary-page::before {
|
290 |
+
content: '';
|
291 |
+
position: fixed;
|
292 |
+
top: 0;
|
293 |
+
left: 0;
|
294 |
+
right: 0;
|
295 |
+
bottom: 0;
|
296 |
+
background-image: var(--bg-image);
|
297 |
+
background-size: 300px 300px;
|
298 |
+
background-repeat: no-repeat;
|
299 |
+
background-position: center bottom;
|
300 |
+
opacity: 0.03;
|
301 |
+
pointer-events: none;
|
302 |
+
z-index: 0;
|
303 |
}
|
304 |
|
305 |
|
|
|
312 |
height: calc(100% - 100px);
|
313 |
padding: 2rem;
|
314 |
text-align: center;
|
315 |
+
position: relative;
|
316 |
+
z-index: 1;
|
317 |
}
|
318 |
|
319 |
.spinner {
|
|
|
345 |
|
346 |
.content {
|
347 |
padding: 0 1rem 100px;
|
348 |
+
position: relative;
|
349 |
+
z-index: 1;
|
350 |
}
|
351 |
|
352 |
section {
|
src/lib/components/UI/TypeBadge.svelte
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
<script lang="ts">
|
2 |
import type { PicletType } from '$lib/types/picletTypes';
|
3 |
-
import { TYPE_DATA } from '$lib/types/picletTypes';
|
4 |
|
5 |
interface Props {
|
6 |
-
type: PicletType;
|
7 |
size?: 'small' | 'medium' | 'large';
|
8 |
showIcon?: boolean;
|
9 |
showLabel?: boolean;
|
@@ -11,16 +11,59 @@
|
|
11 |
|
12 |
let { type, size = 'medium', showIcon = true, showLabel = false }: Props = $props();
|
13 |
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
const sizeClass = $derived(`size-${size}`);
|
16 |
</script>
|
17 |
|
18 |
-
<div class="type-badge {sizeClass}" style="--type-color: {typeInfo.color}">
|
19 |
{#if showIcon}
|
20 |
-
<span class="type-icon">{typeInfo.icon}</span>
|
21 |
{/if}
|
22 |
{#if showLabel}
|
23 |
-
<span class="type-label">{typeInfo.name}</span>
|
24 |
{/if}
|
25 |
</div>
|
26 |
|
|
|
1 |
<script lang="ts">
|
2 |
import type { PicletType } from '$lib/types/picletTypes';
|
3 |
+
import { TYPE_DATA, PicletType as PicletTypeEnum } from '$lib/types/picletTypes';
|
4 |
|
5 |
interface Props {
|
6 |
+
type: PicletType | string | undefined;
|
7 |
size?: 'small' | 'medium' | 'large';
|
8 |
showIcon?: boolean;
|
9 |
showLabel?: boolean;
|
|
|
11 |
|
12 |
let { type, size = 'medium', showIcon = true, showLabel = false }: Props = $props();
|
13 |
|
14 |
+
// Safely get type info with fallback for invalid types
|
15 |
+
const typeInfo = $derived(() => {
|
16 |
+
// Handle undefined or null
|
17 |
+
if (!type) {
|
18 |
+
return TYPE_DATA[PicletTypeEnum.BEAST]; // Default fallback
|
19 |
+
}
|
20 |
+
|
21 |
+
// Check if type exists in TYPE_DATA
|
22 |
+
const typeData = TYPE_DATA[type as PicletType];
|
23 |
+
if (typeData) {
|
24 |
+
return typeData;
|
25 |
+
}
|
26 |
+
|
27 |
+
// Handle legacy string types by mapping them
|
28 |
+
const legacyTypeMap: Record<string, PicletType> = {
|
29 |
+
'normal': PicletTypeEnum.BEAST,
|
30 |
+
'fire': PicletTypeEnum.BEAST,
|
31 |
+
'water': PicletTypeEnum.AQUATIC,
|
32 |
+
'electric': PicletTypeEnum.MACHINA,
|
33 |
+
'grass': PicletTypeEnum.FLORA,
|
34 |
+
'ice': PicletTypeEnum.MINERAL,
|
35 |
+
'fighting': PicletTypeEnum.BEAST,
|
36 |
+
'poison': PicletTypeEnum.FLORA,
|
37 |
+
'ground': PicletTypeEnum.MINERAL,
|
38 |
+
'flying': PicletTypeEnum.BEAST,
|
39 |
+
'psychic': PicletTypeEnum.SPACE,
|
40 |
+
'bug': PicletTypeEnum.BUG,
|
41 |
+
'rock': PicletTypeEnum.MINERAL,
|
42 |
+
'ghost': PicletTypeEnum.SPACE,
|
43 |
+
'dragon': PicletTypeEnum.BEAST,
|
44 |
+
'dark': PicletTypeEnum.SPACE,
|
45 |
+
'steel': PicletTypeEnum.MACHINA,
|
46 |
+
'fairy': PicletTypeEnum.CULTURE
|
47 |
+
};
|
48 |
+
|
49 |
+
const mappedType = legacyTypeMap[type as string];
|
50 |
+
if (mappedType) {
|
51 |
+
return TYPE_DATA[mappedType];
|
52 |
+
}
|
53 |
+
|
54 |
+
// Final fallback
|
55 |
+
return TYPE_DATA[PicletTypeEnum.BEAST];
|
56 |
+
});
|
57 |
+
|
58 |
const sizeClass = $derived(`size-${size}`);
|
59 |
</script>
|
60 |
|
61 |
+
<div class="type-badge {sizeClass}" style="--type-color: {typeInfo().color}">
|
62 |
{#if showIcon}
|
63 |
+
<span class="type-icon">{typeInfo().icon}</span>
|
64 |
{/if}
|
65 |
{#if showLabel}
|
66 |
+
<span class="type-label">{typeInfo().name}</span>
|
67 |
{/if}
|
68 |
</div>
|
69 |
|
src/lib/utils/typeMigration.ts
ADDED
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { db } from '../db/index';
|
2 |
+
import { PicletType, getTypeFromConcept } from '../types/picletTypes';
|
3 |
+
import type { PicletInstance } from '../db/schema';
|
4 |
+
|
5 |
+
// Migration function to fix piclets with invalid or legacy types
|
6 |
+
export async function migratePicletTypes(): Promise<void> {
|
7 |
+
console.log('Starting piclet type migration...');
|
8 |
+
|
9 |
+
try {
|
10 |
+
// Get all piclet instances
|
11 |
+
const allPiclets = await db.picletInstances.toArray();
|
12 |
+
let migratedCount = 0;
|
13 |
+
|
14 |
+
for (const piclet of allPiclets) {
|
15 |
+
let needsUpdate = false;
|
16 |
+
let newType: PicletType | undefined;
|
17 |
+
|
18 |
+
// Check if primaryType is invalid or missing
|
19 |
+
if (!piclet.primaryType || !isValidPicletType(piclet.primaryType)) {
|
20 |
+
// Try to determine type from concept/caption
|
21 |
+
if (piclet.concept) {
|
22 |
+
newType = getTypeFromConcept(piclet.concept, piclet.imageCaption);
|
23 |
+
needsUpdate = true;
|
24 |
+
} else {
|
25 |
+
// Fallback to Beast type
|
26 |
+
newType = PicletType.BEAST;
|
27 |
+
needsUpdate = true;
|
28 |
+
}
|
29 |
+
}
|
30 |
+
|
31 |
+
// Check for legacy string types that need mapping
|
32 |
+
else if (typeof piclet.primaryType === 'string' && isLegacyType(piclet.primaryType)) {
|
33 |
+
newType = mapLegacyType(piclet.primaryType);
|
34 |
+
needsUpdate = true;
|
35 |
+
}
|
36 |
+
|
37 |
+
if (needsUpdate && newType && piclet.id) {
|
38 |
+
await db.picletInstances.update(piclet.id, {
|
39 |
+
primaryType: newType
|
40 |
+
});
|
41 |
+
migratedCount++;
|
42 |
+
console.log(`Migrated piclet ${piclet.nickname || piclet.typeId} from "${piclet.primaryType}" to "${newType}"`);
|
43 |
+
}
|
44 |
+
}
|
45 |
+
|
46 |
+
console.log(`Migration complete: Updated ${migratedCount} piclets`);
|
47 |
+
} catch (error) {
|
48 |
+
console.error('Error during piclet type migration:', error);
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
function isValidPicletType(type: any): type is PicletType {
|
53 |
+
return Object.values(PicletType).includes(type);
|
54 |
+
}
|
55 |
+
|
56 |
+
function isLegacyType(type: string): boolean {
|
57 |
+
const legacyTypes = [
|
58 |
+
'normal', 'fire', 'water', 'electric', 'grass', 'ice',
|
59 |
+
'fighting', 'poison', 'ground', 'flying', 'psychic', 'bug',
|
60 |
+
'rock', 'ghost', 'dragon', 'dark', 'steel', 'fairy'
|
61 |
+
];
|
62 |
+
return legacyTypes.includes(type.toLowerCase());
|
63 |
+
}
|
64 |
+
|
65 |
+
function mapLegacyType(legacyType: string): PicletType {
|
66 |
+
const legacyTypeMap: Record<string, PicletType> = {
|
67 |
+
'normal': PicletType.BEAST,
|
68 |
+
'fire': PicletType.BEAST,
|
69 |
+
'water': PicletType.AQUATIC,
|
70 |
+
'electric': PicletType.MACHINA,
|
71 |
+
'grass': PicletType.FLORA,
|
72 |
+
'ice': PicletType.MINERAL,
|
73 |
+
'fighting': PicletType.BEAST,
|
74 |
+
'poison': PicletType.FLORA,
|
75 |
+
'ground': PicletType.MINERAL,
|
76 |
+
'flying': PicletType.BEAST,
|
77 |
+
'psychic': PicletType.SPACE,
|
78 |
+
'bug': PicletType.BUG,
|
79 |
+
'rock': PicletType.MINERAL,
|
80 |
+
'ghost': PicletType.SPACE,
|
81 |
+
'dragon': PicletType.BEAST,
|
82 |
+
'dark': PicletType.SPACE,
|
83 |
+
'steel': PicletType.MACHINA,
|
84 |
+
'fairy': PicletType.CULTURE
|
85 |
+
};
|
86 |
+
|
87 |
+
return legacyTypeMap[legacyType.toLowerCase()] || PicletType.BEAST;
|
88 |
+
}
|