b6770d394c
Collapse the game UI to one route (`/`): a screen dispatcher renders login/lobby/lobby-create/game from `appScreen`/`activeView` state instead of URL routes. Move screen components to lib/screens & lib/game; the game shell reads the game id from `appScreen.gameId` and re-inits per-game stores via an $effect; in-game views render from `activeView`. Flip ~23 goto/href nav sites to store mutations; drop the `?sidebar=` URL coupling. Auth gate is now state-based. WIP: browser-history (Back→lobby), restore-validation, the return-to-lobby button, push deep-links, and the test migration are follow-ups on this branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
110 lines
2.5 KiB
Svelte
110 lines
2.5 KiB
Svelte
<!--
|
|
Phase 27 Report View — battles section. Each row opens the Battle
|
|
Viewer through `activeView.select("battle", { battleId, turn })`
|
|
where `turn` follows the current report's turn so history-mode views
|
|
land on the right battle. Phase 23 rendered the same rows as inactive
|
|
monospace `<span>`; the rewire here is the one-liner the Phase 23
|
|
decision log called out.
|
|
-->
|
|
<script lang="ts">
|
|
import { getContext } from "svelte";
|
|
import { activeView } from "$lib/app-nav.svelte";
|
|
|
|
import { i18n } from "$lib/i18n/index.svelte";
|
|
import {
|
|
RENDERED_REPORT_CONTEXT_KEY,
|
|
type RenderedReportSource,
|
|
} from "$lib/rendered-report.svelte";
|
|
|
|
const rendered = getContext<RenderedReportSource | undefined>(
|
|
RENDERED_REPORT_CONTEXT_KEY,
|
|
);
|
|
const report = $derived(rendered?.report ?? null);
|
|
const battles = $derived(report?.battles ?? []);
|
|
const turn = $derived(report?.turn ?? 0);
|
|
|
|
function openBattle(battleId: string): void {
|
|
activeView.select("battle", { battleId, turn });
|
|
}
|
|
</script>
|
|
|
|
<section
|
|
id="report-battles"
|
|
class="grid-section"
|
|
data-testid="report-section-battles"
|
|
>
|
|
<h2>{i18n.t("game.report.section.battles.title")}</h2>
|
|
|
|
{#if report === null}
|
|
<p class="status">{i18n.t("game.report.loading")}</p>
|
|
{:else if battles.length === 0}
|
|
<p class="status" data-testid="battles-empty">
|
|
{i18n.t("game.report.section.battles.empty")}
|
|
</p>
|
|
{:else}
|
|
<ul class="ids" data-testid="battles-list">
|
|
{#each battles as b (b.id)}
|
|
<li>
|
|
<span class="label">
|
|
{i18n.t("game.report.section.battles.id_label")}
|
|
</span>
|
|
<button
|
|
type="button"
|
|
class="uuid"
|
|
onclick={() => openBattle(b.id)}
|
|
data-testid="report-battle-row"
|
|
data-id={b.id}
|
|
>{b.id}</button>
|
|
</li>
|
|
{/each}
|
|
</ul>
|
|
{/if}
|
|
</section>
|
|
|
|
<style>
|
|
.grid-section h2 {
|
|
margin: 0 0 0.5rem;
|
|
font-size: 1.05rem;
|
|
color: var(--color-text);
|
|
}
|
|
.status {
|
|
margin: 0;
|
|
color: var(--color-text-muted);
|
|
font-size: 0.9rem;
|
|
}
|
|
.ids {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.3rem;
|
|
font-size: 0.85rem;
|
|
}
|
|
.ids li {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 0.6rem;
|
|
}
|
|
.label {
|
|
color: var(--color-text-muted);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
font-size: 0.7rem;
|
|
}
|
|
.uuid {
|
|
padding: 0;
|
|
border: 0;
|
|
background: transparent;
|
|
font: inherit;
|
|
color: var(--color-accent);
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
text-decoration: underline;
|
|
text-underline-offset: 2px;
|
|
cursor: pointer;
|
|
}
|
|
.uuid:hover {
|
|
color: var(--color-text);
|
|
}
|
|
</style>
|