feat: improve board review and connection optimization assumptions
Publish GitHub Pages output / publish (push) Successful in 45s

This commit is contained in:
dal4segno committed 2026-10-06 12:12:09 +09:00
1 parent 0b794edc80
commit d18972382a
8 files changed
+277 -29

No files matched your search

+23 -7
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { page } from '$app/stores';
import { boardMemberOptions, unplacedOwnedCards } from '$lib/engine/board-editor-options.ts';
import { onMount, tick } from 'svelte';
import cardSummary from '$lib/data/cards-summary.json';
import { leaderOutfitCards } from '$lib/engine/leader-outfits.ts';
@@ -113,6 +114,8 @@
let boardDragStart = { x: 0, y: 0, left: 0, top: 0 };
let selectedSocketGroupId = '';
let boardCardSearch = '';
let boardMemberSort: 'default' | 'most' | 'least' = 'default';
let unplacedOwnedOnly = false;
let automaticPlacementSnapshot: BoardCardPlacements | null = null;
let automaticPlacementResult: AutomaticPlacementResult[] = [];
let savedAnalysis: SavedAnalysisState | null = null;
@@ -136,6 +139,7 @@
$: filteredFiveStarCards = filteredCards.filter((card) => card.rarity === 5);
$: filteredFourStarCards = filteredCards.filter((card) => card.rarity === 4);
$: selectedBoardMember = boardMembers.find((member) => member.id === selectedMemberId) ?? null;
$: sortedBoardMembers = boardMemberOptions(boardMembers, boardModels, userData.memberBoards, boardMemberSort);
$: selectedActiveBoardNodes = userData.memberBoards[selectedMemberId] ?? [];
$: selectedActiveBoardNodeCount = new Set(selectedActiveBoardNodes.filter((nodeId) => nodeId !== centerNodeId())).size;
$: selectedBoardNodeCount = selectedBoardMember
@@ -148,7 +152,7 @@
selectedActiveBoardNodes,
selectedBoardMember?.positionGroupId ?? ''
);
$: filteredConnectCards = connectCards.filter((card) => {
$: filteredConnectCards = unplacedOwnedCards(connectCards, userData.collection, userData.boardCards, unplacedOwnedOnly).filter((card) => {
return cardMatchesSearch(card, boardCardSearch);
});
$: allBoardMaterialTotals = boardMaterialTotals(userData.memberBoards);
@@ -381,10 +385,10 @@
}
function selectAdjacentBoardMember(offset: -1 | 1) {
if (boardMembers.length < 2) return;
const currentIndex = boardMembers.findIndex((member) => member.id === selectedMemberId);
const nextIndex = (Math.max(0, currentIndex) + offset + boardMembers.length) % boardMembers.length;
selectedMemberId = boardMembers[nextIndex].id;
if (sortedBoardMembers.length < 2) return;
const currentIndex = sortedBoardMembers.findIndex((member) => member.id === selectedMemberId);
const nextIndex = (Math.max(0, currentIndex) + offset + sortedBoardMembers.length) % sortedBoardMembers.length;
selectedMemberId = sortedBoardMembers[nextIndex].id;
finishBoardMemberChange();
}
@@ -1400,11 +1404,19 @@
<label class="member-select">
<span>멤버 선택</span>
<select bind:value={selectedMemberId} on:change={finishBoardMemberChange}>
{#each boardMembers as member}<option value={member.id}>{member.name}</option>{/each}
{#each sortedBoardMembers as member}<option value={member.id}>{member.name} · {member.openedCount}개 개방</option>{/each}
</select>
<small><kbd>A</kbd> 이전 멤버 · <kbd>D</kbd> 다음 멤버</small>
</label>
<div class="member-navigation" aria-label="멤버 보드 이동">
<label class="member-select">
<span>멤버 정렬</span>
<select bind:value={boardMemberSort}>
<option value="default">기본순</option>
<option value="most">보드 개방 많은 순</option>
<option value="least">보드 개방 적은 순</option>
</select>
</label>
<button type="button" on:click={() => selectAdjacentBoardMember(-1)} aria-label="이전 멤버 보드">‹ 이전</button>
<button type="button" on:click={() => selectAdjacentBoardMember(1)} aria-label="다음 멤버 보드">다음 ›</button>
</div>
@@ -1502,11 +1514,13 @@
</div>
<p class="card-picker-note">배치한 카드의 범위에 포함된 활성 노드만 강화됩니다. 같은 카드를 다시 선택하면 기존 위치에서 이곳으로 이동합니다.</p>
<input class="card-picker-search" type="search" bind:value={boardCardSearch} placeholder="멤버 또는 버전 검색" aria-label="연결 카드 검색" />
<label class="card-picker-filter"><input type="checkbox" bind:checked={unplacedOwnedOnly} /> 미배치 보유 카드만</label>
{#if selectedBoardCardPlacements[selectedSocketGroupId]}
{@const currentCard = connectCardById.get(selectedBoardCardPlacements[selectedSocketGroupId])}
<div class="current-placement"><span>현재 배치</span><strong>{currentCard ? cardLabel(currentCard) : selectedBoardCardPlacements[selectedSocketGroupId]}</strong><button type="button" on:click={removeBoardCard}>배치 해제</button></div>
{/if}
<div class="connect-card-list">
{#if filteredConnectCards.length === 0}<p class="card-picker-note">조건에 맞는 연결 카드가 없습니다.</p>{/if}
{#each filteredConnectCards as card}
{@const effect = selectedConnectEffect(card)}
{@const placement = cardPlacementLabel(card.id)}
@@ -1645,13 +1659,14 @@
.board-member-settings { order: 1; margin-bottom: 0; }
.board-tools { order: 0; }
.board-heading { display: flex; align-items: flex-end; justify-content: space-between; gap: 18px; }
.card-picker-filter { display: flex; align-items: center; gap: 8px; margin: 12px 0; color: #c9d0dc; font-size: 13px; }
.board-guidance { margin: 0 0 12px; padding: 12px 14px; border: 1px solid #4a3b46; border-radius: 10px; color: #c8b7c1; background: rgb(239 139 182 / 7%); font-size: 13px; line-height: 1.6; }
.member-select { display: grid; gap: 5px; width: min(360px, 100%); }
.member-select span { color: #818b9c; font-size: 12px; font-weight: 750; }
.member-select select { height: 38px; padding: 0 10px; border: 1px solid #394252; border-radius: 8px; color: #dfe3ea; background: #12171f; }
.member-select small { color: #6f7888; font-size: 11px; }
.member-select kbd { display: inline-grid; min-width: 18px; height: 18px; padding: 0 4px; place-items: center; border: 1px solid #465064; border-bottom-width: 2px; border-radius: 4px; color: #c9d0dc; background: #171d27; font: 700 10px/1 Pretendard, "Noto Sans KR", sans-serif; }
.member-navigation { display: flex; gap: 7px; }
.member-navigation { display: flex; align-items: flex-end; gap: 7px; }
.member-navigation button { min-width: 82px; height: 38px; padding: 0 12px; border: 1px solid #465064; border-radius: 8px; color: #d5dbe4; background: #202733; font-size: 13px; font-weight: 850; cursor: pointer; }
.member-navigation button:hover { border-color: #d96b9a; color: #f4b0cc; }
.board-toolbar { display: flex; align-items: center; justify-content: space-between; margin: 20px 0 10px; }
@@ -1852,6 +1867,7 @@
.board-heading { align-items: stretch; flex-direction: column; }
.member-select { width: 100%; }
.member-navigation { display: grid; grid-template-columns: 1fr 1fr; }
.member-navigation .member-select { grid-column: 1 / -1; }
.member-navigation button { width: 100%; min-height: 44px; }
.board-toolbar { align-items: flex-start; gap: 10px; }
.automatic-placement { align-items: stretch; flex-direction: column; gap: 10px; }