feat: release version 1.0.4

This commit is contained in:
2026-08-19 18:56:00 +09:00
parent bd9d5a6ba6
commit f910db8901
11 changed files with 506 additions and 105 deletions
+82
View File
@@ -0,0 +1,82 @@
import type { CardListSortKey, SortDirection } from './user-data.ts';
export type CardType = '큐트' | '퓨어' | '해피';
export type HighestStat = '퍼포먼스' | '테크닉' | '센스';
export type SkillLevel = {
level: number;
description: string;
coolTimeSeconds: number | null;
probabilityPercent: number | null;
durationSeconds: number | null;
effect?: string;
condition?: string;
};
export type CardDetails = {
title: string;
passive: SkillLevel[];
active?: SkillLevel[];
special: SkillLevel[];
costume: { name: string; description: string } | null;
};
export type CardSummary = {
id: string;
rarity: number;
type: CardType;
name: string;
versionName: string;
generation: string;
highestStat: HighestStat;
details: CardDetails;
};
export function skillAtLevel(
levels: readonly SkillLevel[] | undefined,
selectedLevel: number
): SkillLevel | undefined {
return levels?.find((level) => level.level === selectedLevel) ?? levels?.at(-1);
}
export function activeEffectPercent(skill: SkillLevel | undefined): number | null {
const match = (skill?.effect || skill?.description)?.match(/스코어(?:가|\s+서포트\s+효과)\s*(\d+(?:\.\d+)?)%/);
return match ? Number(match[1]) : null;
}
export function activeSortValue(
card: CardSummary,
sortKey: CardListSortKey,
selectedLevel: number
): number | null {
const active = skillAtLevel(card.details.active, selectedLevel);
if (!active) return null;
const value = {
coolTime: active.coolTimeSeconds,
probability: active.probabilityPercent,
duration: active.durationSeconds,
effect: activeEffectPercent(active)
}[sortKey];
return typeof value === 'number' && Number.isFinite(value) ? value : null;
}
export function sortCardsByActive(
cards: readonly CardSummary[],
sortKey: CardListSortKey | null,
sortDirection: SortDirection,
selectedLevel: number
): CardSummary[] {
if (!sortKey) return [...cards];
const direction = sortDirection === 'asc' ? 1 : -1;
return cards
.map((card, index) => ({ card, index, value: activeSortValue(card, sortKey, selectedLevel) }))
.sort((left, right) => {
if (left.value === null) return right.value === null ? left.index - right.index : 1;
if (right.value === null) return -1;
return (left.value - right.value) * direction || left.index - right.index;
})
.map(({ card }) => card);
}
+7
View File
@@ -12,6 +12,10 @@ export type SavedDeck = {
updatedAt: string;
};
export type CardListSortKey = 'coolTime' | 'probability' | 'duration' | 'effect';
export type SortDirection = 'asc' | 'desc';
export type CardSkillLevel = 1 | 2;
export type CardListPreferences = {
types: string[];
stats: string[];
@@ -20,6 +24,9 @@ export type CardListPreferences = {
activeProbabilities: string[];
cardVariants: string[];
generations: string[];
sortKey: CardListSortKey | null;
sortDirection: SortDirection;
skillLevel: CardSkillLevel;
};
export type BoardCardPlacements = Record<string, Record<string, string>>;