fixed Dev mode

This commit is contained in:
Kelorastus
2026-09-29 15:54:26 +02:00
parent afbb597044
commit 0252f95e35
9 changed files with 266 additions and 141 deletions
+198 -10
View File
@@ -97,7 +97,24 @@
// Said out loud whenever this run is not the installed ODIAN. A dev client looks exactly
// like the real one on screen, and mistaking the two is how a bug gets chased in the
// wrong tree - or a demo gets given from an unbuilt branch.
const devRun = $derived((s?.dev ?? null) !== null && (launcher.settings?.dev_mode ?? false));
const devOn = $derived(launcher.devOn);
const devRun = $derived(launcher.devReady);
const devRoot = $derived(launcher.settings?.dev_root ?? "");
const devTarget = $derived(launcher.settings?.dev_target ?? "client");
const devBuild = $derived(launcher.settings?.dev_build ?? false);
const savingDev = $derived(busy === "settings");
// One stack either way: both clients talk to the same API and the same database, which is
// the only way a .exe/web divergence is visible at all.
const TARGETS = [
{ key: "client", name: "ODIAN", note: "Odian.exe" },
{ key: "web", name: "Web", note: ":5173" },
{ key: "both", name: "Both", note: "one stack" },
] as const;
async function browseCheckout() {
const path = await launcher.pickFolder();
if (path) await launcher.setDev({ dev_root: path });
}
// Named only when it is someone ELSE: running as yourself is not worth a warning.
const devUser = $derived.by(() => {
const asked = launcher.settings?.dev_user?.trim() ?? "";
@@ -221,16 +238,79 @@
<button class="btn-link" disabled={blocked} onclick={() => launcher.openSync()}>Sync</button>
</p>
{/if}
{#if devRun}
<p class="secondary attention">
<Icon name="alert" size={14} />
<span>
Dev mode: ODIAN runs from your checkout, not from the installed
<strong>{s?.installed ?? "version"}</strong>.{#if devUser}
Running as <strong>{devUser}</strong> — sync is off.{/if}
</span>
</p>
{#if devOn}
<!-- Dev mode's own controls, where the launching happens. Everything that decides
what Launch does is here rather than behind Settings: the target, the checkout,
and whether it builds first. -->
<section class="dev" aria-label="Dev mode">
<header class="dev-head">
<Icon name="code" size={13} />
<span class="dev-title">DEV</span>
<!-- What Launch will actually start, in three words. The folder is not repeated
here: it is in the field two rows down. -->
<span class="dev-from mono">{devRun ? TARGETS.find((o) => o.key === devTarget)?.name ?? "" : "no checkout"}</span>
</header>
<div class="dev-targets" role="radiogroup" aria-label="Launch starts">
{#each TARGETS as option (option.key)}
<label class="dev-target" class:on={devTarget === option.key}>
<input
type="radio"
name="dev-target"
value={option.key}
checked={devTarget === option.key}
disabled={blocked || savingDev}
onchange={() => launcher.setDev({ dev_target: option.key })}
/>
<span class="dev-target-name">{option.name}</span>
<span class="dev-target-note mono">{option.note}</span>
</label>
{/each}
</div>
<div class="dev-row">
<input
class="dev-path"
type="text"
spellcheck="false"
placeholder="Folder holding ODIAN_Engine"
value={devRoot}
disabled={blocked || savingDev}
onchange={(e) => launcher.setDev({ dev_root: e.currentTarget.value.trim() })}
/>
<button type="button" class="btn" disabled={blocked || savingDev} onclick={browseCheckout}>Browse…</button>
</div>
<div class="dev-row wrap">
<label class="dev-check">
<input
type="checkbox"
checked={devBuild}
disabled={blocked || savingDev}
onchange={(e) => launcher.setDev({ dev_build: e.currentTarget.checked })}
/>
<span>Build first</span>
</label>
{#if s?.online}
<!-- The stack is the install's, so its tiles and its data are still worth
refreshing from the DHP while there is a line to it. -->
<button class="btn-link" disabled={blocked} onclick={() => launcher.syncTiles()}>Sync tiles</button>
<button class="btn-link" disabled={blocked} onclick={() => launcher.syncNow()}>Sync database</button>
{:else}
<span class="dev-offline mono">offline — tiles and data stay as they are</span>
{/if}
</div>
{#if !devRun}
<p class="dev-note">Name the folder that holds <code>ODIAN_Engine</code> and Launch runs from it.</p>
{:else if devUser}
<p class="dev-note attention">
Running as <strong>{devUser}</strong> — this PC will not sync with the DHP.
</p>
{/if}
</section>
{/if}
{#if unfinished}
<p class="secondary attention">
<Icon name="alert" size={14} />
@@ -376,6 +456,114 @@
</section>
<style>
/* Dev mode's controls. Deliberately compact and deliberately here: what Launch will do
is decided in the same place you press it, not two drawers away. */
.dev {
margin-top: 0.75rem;
padding: 0.7rem 0.75rem;
border: 1px solid color-mix(in srgb, #46d17f 32%, transparent);
border-radius: 8px;
background: color-mix(in srgb, #46d17f 7%, transparent);
display: flex;
flex-direction: column;
gap: 0.55rem;
}
.dev-head {
display: flex;
align-items: center;
gap: 0.4rem;
color: #46d17f;
}
.dev-title {
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.12em;
}
.dev-from {
margin-left: auto;
font-size: 0.72rem;
color: var(--text-dim, #9aa3ad);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 55%;
direction: rtl; /* keep the END of a long path, which is the part that differs */
text-align: right;
}
.dev-targets {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.35rem;
}
.dev-target {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.1rem;
padding: 0.35rem 0.25rem;
border: 1px solid var(--line, #2a3139);
border-radius: 6px;
cursor: pointer;
line-height: 1.2;
}
.dev-target.on {
border-color: color-mix(in srgb, #46d17f 60%, transparent);
background: color-mix(in srgb, #46d17f 14%, transparent);
}
.dev-target:has(input:disabled) {
opacity: 0.55;
cursor: default;
}
.dev-target input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.dev-target-name {
font-size: 0.82rem;
font-weight: 600;
}
.dev-target-note {
font-size: 0.68rem;
color: var(--text-dim, #9aa3ad);
}
.dev-row {
display: flex;
align-items: center;
gap: 0.4rem;
}
.dev-row.wrap {
flex-wrap: wrap;
row-gap: 0.3rem;
}
.dev-path {
flex: 1 1 auto;
min-width: 0;
padding: 0.3rem 0.45rem;
font-size: 0.78rem;
font-family: inherit;
color: inherit;
background: rgba(0, 0, 0, 0.25);
border: 1px solid var(--line, #2a3139);
border-radius: 6px;
}
.dev-check {
display: inline-flex;
align-items: center;
gap: 0.35rem;
font-size: 0.8rem;
white-space: nowrap;
}
.dev-offline,
.dev-note {
font-size: 0.75rem;
color: var(--text-dim, #9aa3ad);
margin: 0;
}
.dev-note code {
font-size: 0.72rem;
}
.action {
display: grid;
gap: 14px;