feat: streamline search results and open captured analyses
Publish GitHub Pages output / publish (push) Successful in 54s
Publish GitHub Pages output / publish (push) Successful in 54s
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { difficultyLabel } from '$lib/data/difficulty.ts';
|
||||
import { ANALYSIS_TRANSFER_KEY, type AnalysisTransfer } from '$lib/models/analysis-transfer.ts';
|
||||
import type { AnalysisCapture } from '$lib/models/analysis-history.ts';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import type catalog from '$lib/data/exact-chart-catalog.json';
|
||||
import { loadExactSongChart } from '$lib/data/chart-loader.ts';
|
||||
@@ -9,9 +11,9 @@
|
||||
import { createScoreOptimizerWorker } from '$lib/workers/create-score-optimizer-worker.ts';
|
||||
|
||||
type Chart = (typeof catalog.charts)[number];
|
||||
type Context = { cards: OptimizerCard[]; overallPower: number; directSongScoreBonusPercent: number };
|
||||
type Context = { cards: OptimizerCard[]; overallPower: number; directSongScoreBonusPercent: number; capture: AnalysisCapture };
|
||||
type Evaluation = { result: ExactOptimizationResult; achievementProbabilities: ScoreAchievementProbabilities };
|
||||
type Row = Chart & Evaluation & { overallPower: number; theoreticalMaxScore: number; expectedScore: number };
|
||||
type Row = Chart & Evaluation & { transfer: AnalysisTransfer; overallPower: number; theoreticalMaxScore: number; expectedScore: number };
|
||||
export let charts: Chart[];
|
||||
export let leaderId: string;
|
||||
export let leaderName: string;
|
||||
@@ -19,7 +21,6 @@
|
||||
export let inputKey: string;
|
||||
export let disabled = false;
|
||||
export let getContext: (chart: Chart) => Context;
|
||||
export let onSelect: (chart: Chart, result: ExactOptimizationResult, overallPower: number, probabilities: ScoreAchievementProbabilities) => void;
|
||||
|
||||
const difficulty = 'all';
|
||||
let running = false;
|
||||
@@ -30,6 +31,7 @@
|
||||
let reportedCores = 1;
|
||||
let activeJobs: Record<number, { title: string; fraction: number }> = {};
|
||||
let message = '';
|
||||
let transferError = '';
|
||||
let failures: string[] = [];
|
||||
let snapshotKey = '';
|
||||
let snapshotLeader = '';
|
||||
@@ -128,6 +130,8 @@
|
||||
});
|
||||
if (id !== runId) return;
|
||||
rows = [...rows, { ...summary, result, achievementProbabilities, overallPower: context.overallPower,
|
||||
transfer: { schemaVersion: 1, source: 'rating', capture: context.capture, cards: context.cards, chart,
|
||||
result, achievementProbabilities, directSongScoreBonusPercent: context.directSongScoreBonusPercent },
|
||||
theoreticalMaxScore: result.theoreticalMaxScore, expectedScore: result.expectedScore }];
|
||||
} catch (error) {
|
||||
if (id !== runId) return;
|
||||
@@ -159,6 +163,20 @@
|
||||
}
|
||||
|
||||
const format = (value: number) => Math.round(value).toLocaleString('ko-KR');
|
||||
function openInAnalysis(event: MouseEvent, transfer: AnalysisTransfer): void {
|
||||
event.preventDefault();
|
||||
transferError = '';
|
||||
try {
|
||||
sessionStorage.setItem(ANALYSIS_TRANSFER_KEY, JSON.stringify(transfer));
|
||||
const opened = window.open((event.currentTarget as HTMLAnchorElement).href, '_blank');
|
||||
if (!opened) throw new Error('새 탭이 차단되었습니다. 팝업을 허용한 뒤 다시 열어 주세요.');
|
||||
opened.opener = null;
|
||||
} catch (error) {
|
||||
transferError = error instanceof Error ? error.message : '배치 분석을 열지 못했습니다.';
|
||||
} finally {
|
||||
try { sessionStorage.removeItem(ANALYSIS_TRANSFER_KEY); } catch { /* Storage may be blocked. */ }
|
||||
}
|
||||
}
|
||||
function probabilityLabel(value: number): string {
|
||||
if (value <= 0) return '0%';
|
||||
if (value < 0.000001) return '<0.0001%';
|
||||
@@ -173,6 +191,7 @@
|
||||
}
|
||||
</script>
|
||||
<section class="ranking" aria-labelledby="leader-ranking-title">
|
||||
{#if transferError}<p role="alert">{transferError}</p>{/if}
|
||||
<div class="heading">
|
||||
<div class="section-heading"><span class="section-number">02</span><div><h2 id="leader-ranking-title">리더 레이팅 곡 추천</h2><p>지금 편성으로 도전할 곡을 찾아보세요.</p></div></div>
|
||||
</div>
|
||||
@@ -242,7 +261,7 @@
|
||||
<div title={`${format(Math.ceil(row.theoreticalMaxScore * .99))}점 이상 달성 확률`}><span>99% 이상</span><strong>{probabilityLabel(row.achievementProbabilities.atLeast99Percent)}</strong></div>
|
||||
<div title={`${format(Math.ceil(row.theoreticalMaxScore * .95))}점 이상 달성 확률`}><span>95% 이상</span><strong>{probabilityLabel(row.achievementProbabilities.atLeast95Percent)}</strong></div>
|
||||
</div></td>
|
||||
<td class="row-action"><button type="button" disabled={stale || disabled || running} on:click={() => onSelect(row, row.result, row.overallPower, row.achievementProbabilities)}>배치 보기 <span aria-hidden="true">→</span></button></td>
|
||||
<td class="row-action"><a href="/analysis" on:click={(event) => openInAnalysis(event, row.transfer)}>배치 분석에서 보기 <span aria-hidden="true">↗</span></a></td>
|
||||
</tr>{/each}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -376,8 +395,8 @@
|
||||
.achievement-grid span { color: #9690a6; font-size: 10px; }
|
||||
.achievement-grid strong { color: #cbb4db; font-size: 12px; font-weight: 650; }
|
||||
.achievement-grid > div:first-child strong { color: #f1b0ce; }
|
||||
.row-action button { padding: 9px 11px; background: transparent; white-space: nowrap; font-size: 11px; }
|
||||
.row-action button > span { margin-left: 8px; color: #bd8ea8; }
|
||||
.row-action a { display: inline-block; padding: 9px 11px; color: inherit; background: transparent; white-space: nowrap; font-size: 11px; }
|
||||
.row-action a > span { margin-left: 8px; color: #bd8ea8; }
|
||||
.mobile-label { display: none; }
|
||||
.list-footer { justify-content: space-between; gap: 12px; margin-top: 15px; }
|
||||
.range-label { color: #c4bdcd; font-size: 11px; }
|
||||
@@ -452,8 +471,8 @@
|
||||
.average-score { grid-column: 2; grid-row: 3; text-align: left; font-size: 11px; }
|
||||
.mobile-label { display: inline-block; min-width: 48px; margin-right: 6px; color: #828e9f; font-size: 10px; }
|
||||
.row-action { grid-column: 3; grid-row: 2 / 4; align-self: end; }
|
||||
.row-action button { padding: 7px 8px; font-size: 10px; }
|
||||
.row-action button > span { display: none; }
|
||||
.row-action a { padding: 7px 8px; font-size: 10px; }
|
||||
.row-action a > span { display: none; }
|
||||
.probability-cell { grid-column: 1 / -1; grid-row: 4; padding-top: 11px; margin-top: 5px; border-top: 1px solid #3c3342; }
|
||||
.achievement-grid { gap: 12px; text-align: center; }
|
||||
.achievement-grid > div { gap: 5px; }
|
||||
|
||||
Reference in New Issue
Block a user