{#snippet primary(label: string, icon: IconName, onclick: () => void, disabled: boolean, pending = false)}
{/snippet} {#snippet driveLink(lead: string)}

{lead}

{/snippet}

{head.label}

{#if head.meta}{head.meta}{/if}
{#if action === "loading"} {@render primary("Loading", "play", () => {}, true, true)} {:else if action === "sign_in"} {@render primary( s?.update_required ? "Sign in to update" : s?.installed ? "Sign in" : "Sign in to install", "key", () => launcher.signIn(), blocked, busy === "sign_in", )}

{#if s?.update_required} ODIAN {s.available} is required before launch while connected. Sign in with your {site} account to download it. {:else if s?.installed} Sign in with your {site} account. This PC then remembers you, online and offline, until you sign out. {:else} Sign in with your {site} account to download ODIAN{#if s?.available} {s.available}{/if}. {/if}

{#if !s?.installed}{@render driveLink("No access to the DHP?")}{/if} {:else if action === "signing_in"} {@render primary("Waiting for sign-in", "key", () => {}, true, true)}

Complete the Keycloak sign-in in your browser, then come back here.

{:else if action === "install"} {@render primary("Install", "download", () => launcher.install(), blocked, busy === "install")}

ODIAN {s?.available} from {site}, into {s?.install_dir}.

{@render driveLink("Have an ODIAN drive?")} {:else if action === "offline_unavailable"} {@render primary("Connect to your DHP to install", "globe", () => {}, true)} {@render driveLink("Installing needs your DHP, or an ODIAN drive.")} {:else if action === "signed_out_offline"} {@render primary("Connect to your DHP to sign in", "globe", () => {}, true)}

This PC doesn't remember an operator anymore. Sign in once while connected to {site}, and ODIAN launches offline again after that.

{:else if action === "update"} {@render primary(`Update to ${s?.available}`, "update", () => launcher.install(), blocked, busy === "install")}

Updating is required before launch while connected to {site}. Everything already verified is reused.

{:else if action === "launch"} {@render primary("Launch", "play", () => launcher.launch(), blocked || !s?.can_launch, busy === "launch")}

{#if checking} Checking {site}… {:else if s?.online} ODIAN {s.installed} is up to date. {:else} Offline: ODIAN {s?.installed} runs on local data and map tiles. {/if}

{#if launcher.syncAttention > 0}

{launcher.syncAttention} {launcher.syncAttention === 1 ? "change needs" : "changes need"} your decision.

{:else if s?.online || s?.sync}

Operator data syncs with {site} before every launch.

{/if} {#if devOn}
DEV {devRun ? TARGETS.find((o) => o.key === devTarget)?.name ?? "" : "no checkout"}
{#each TARGETS as option (option.key)} {/each}
launcher.setDev({ dev_root: e.currentTarget.value.trim() })} />
launcher.setDev({ dev_user: e.currentTarget.value.trim() })} />
{#if releasing} {:else} {/if} {#if hasOutput && !releasing} {:else if !s?.online} needs the DHP {/if}
{#if !devRun}

Name the folder that holds ODIAN_Engine and Launch runs from it.

{:else if devUser}

Running as {devUser} — this PC will not sync with the DHP.

{/if}
{/if} {#if unfinished}

{unfinished.items} {unfinished.items === 1 ? "chosen map is" : "chosen maps are"} only partly downloaded {#if unfinishedNames}({unfinishedNames}){/if}.

{/if} {#if tilesOffered}

{tilesCount} new map tiles on {site} ({formatBytes(tiles?.bytes ?? 0)}).

{/if} {:else if action === "installing"}
{p ? STAGE_LABEL[p.stage] : "Preparing"} {#if fraction !== null}{Math.floor(fraction * 100)}%{/if}
{#if p}
Transferred
{transferred}
Speed
{launcher.speed > 0 ? formatSpeed(launcher.speed) : "—"}
Stage ETA
{launcher.eta !== null ? formatDuration(launcher.eta) : "—"}
{#if p.message}

{p.message}

{/if} {/if}

Cancelling keeps every file already verified; the next install resumes from there.

{:else if action === "restoring"}
Loading the database snapshot
{#if p?.stage === "restore" && p.message}

{p.message}

{/if}

This can take a few minutes. Keep the launcher open until it finishes.

{:else if action === "syncing"}
Exchanging operator data

Sending this PC's changes to {site} and taking in the other operators' work.

{:else if action === "building"}
Compiling ODIAN from your checkout

The build's own window has its output. A first build takes a while.

{:else if action === "removing"}
Removing {rp?.title ?? "what you unticked"} {#if removeFraction !== null}{Math.floor(removeFraction * 100)}%{/if}
{#if rp}

{formatCount(rp.done)} / {formatCount(rp.total)} files

{/if}

Deleting a map is hundreds of thousands of files. Nothing is downloaded meanwhile.

{:else if action === "pulling_tiles"}
Taking map tiles from {site} {#if tp}{formatBytes(tp.bytes)}{/if}

Tiles the other operators baked while this PC was away. It can be stopped and resumed.

{:else if action === "stack"} {#if s?.map_from_site === false}

{site}'s map service is unreachable: drawing from this PC's own tiles. {#if s?.map_problem} ({s.map_problem}){/if}

{/if} {:else if action === "error"} {@render primary("Retry", "refresh", () => launcher.retry(), blocked, busy !== null)}

for the full log.

{/if} {#if showNotice} {/if}