This commit is contained in:
Kelorastus
2026-09-30 15:01:58 +02:00
parent 3863809ae8
commit 8988ad8b06
7 changed files with 448 additions and 48 deletions
+16
View File
@@ -209,6 +209,14 @@
<p class="secondary">
ODIAN <strong>{s?.available}</strong> from {site}, into <span class="mono path">{s?.install_dir}</span>.
</p>
<!-- A licensed PC with nothing installed should be running from its checkout against the
DHP. When it cannot, say which single thing is in the way - this screen used to give
no clue at all, which sent people looking at the install folder every time. -->
{#if s?.dhp_blocked}
<p class="secondary small">
<strong>Dev mode:</strong> a run straight against {site} needs no install, but {s.dhp_blocked}
</p>
{/if}
{@render driveLink("Have an ODIAN drive?")}
{:else if action === "offline_unavailable"}
{@render primary("Connect to your DHP to install", "globe", () => {}, true)}
@@ -229,6 +237,14 @@
<p class="secondary">
{#if checking}
Checking {site}…
{:else if !s?.installed}
Nothing is installed here: the client comes from your checkout and runs on
<strong>{site}</strong>'s data. You sign in inside ODIAN, not here.
{:else if s?.update_required}
<!-- Only reachable on a dev run: any other PC would be on the "update" action. -->
ODIAN <strong>{s.installed}</strong> is installed, <strong>{s.available}</strong> is on {site}. A dev run starts
your checkout, so it does not need it.
<button class="btn-link" disabled={blocked} onclick={() => launcher.install()}>Update anyway</button>
{:else if s?.online}
ODIAN <strong>{s.installed}</strong> is up to date.
{:else}
+7 -1
View File
@@ -72,10 +72,16 @@ export function deriveAction(s: Status | null): ActionKind {
}
// idle or checking
if (s.installed === null) {
// Nothing installed, but a dev checkout and a reachable DHP are enough on their own:
// the client runs against the DHP and signs itself in, so neither an install nor a
// launcher sign-in stands in the way.
if (s.dhp_direct) return "launch";
if (!s.online) return "offline_unavailable";
return s.signed_in ? "install" : "sign_in";
}
if (s.update_required) return s.signed_in ? "update" : "sign_in";
// A dev run starts the checkout, so a newer pack is worth saying but not worth
// stopping for; the hub keeps Launch and mentions the update beside it.
if (s.update_required && !s.dev_run) return s.signed_in ? "update" : "sign_in";
// Launching runs ODIAN as the remembered operator; after Sign out there is none.
if (s.operator === null) return s.online ? "sign_in" : "signed_out_offline";
return "launch";
+23 -3
View File
@@ -226,7 +226,8 @@ function derive(s: Status): Status {
available,
signed_in: s.online && s.signed_in,
update_required,
can_launch: s.installed !== null && !update_required && s.phase === "idle",
// A dev run launches the checkout, so an available pack does not hold it back.
can_launch: (s.installed !== null || s.dhp_direct) && (!update_required || s.dev_run) && s.phase === "idle",
};
}
@@ -240,6 +241,9 @@ function initialStatus(scenario: MockScenario): Status {
available: CURRENT,
update_required: false,
can_launch: false,
dev_run: false,
dhp_direct: false,
dhp_blocked: null,
phase: "idle",
error: null,
install_dir: DEFAULT_DIR,
@@ -285,9 +289,25 @@ function initialStatus(scenario: MockScenario): Status {
// A licensed PC, already set up to run from a checkout, so the Dev section and the
// dev-run marker can be seen without a licence file.
Object.assign(s, {
installed: CURRENT,
// Deliberately BEHIND what the DHP has: this is the case that used to refuse to
// launch at all, so the scenario should show it working.
installed: "0.71.0",
operator: OPERATOR,
signed_in: true,
dev_run: true,
dev: { machine: "ODIAN-DEV-01", install_id: null, expires_at: "2027-01-31", note: "test kit" },
});
break;
case "dev-dhp":
// A licensed dev PC with NOTHING installed, on a reachable DHP: the case that used to
// offer only Install.
Object.assign(s, {
installed: null,
operator: OPERATOR,
signed_in: true,
dev_run: true,
dhp_direct: true,
dhp_blocked: null,
dev: { machine: "ODIAN-DEV-01", install_id: null, expires_at: "2027-01-31", note: "test kit" },
});
break;
@@ -349,7 +369,7 @@ class MockBackend implements Backend {
this.settings = { site_url: SITE_URL, mcp_url: MCP_URL, ai_url: "", install_dir: DEFAULT_DIR, dev_root: "", dev_mode: false, dev_user: "", dev_build: false, dev_target: "client" };
// Already pointed at a checkout and switched on, so the dev controls and the dev-run
// marker show as they do on the real thing.
if (scenario === "dev") Object.assign(this.settings, { dev_root: "E:" + String.fromCharCode(92) + "ODIAN_Dev", dev_mode: true });
if (scenario === "dev" || scenario === "dev-dhp") Object.assign(this.settings, { dev_root: "E:" + String.fromCharCode(92) + "ODIAN_Dev", dev_mode: true });
if (this.status.installed) {
// An installed mock PC has the basemap, Dark V2 and a stopped satellite download.
for (const item of MOCK_MARKET) if (item.required) this.marketPresent.set(item.key, "installed");
+7
View File
@@ -36,6 +36,12 @@ export interface Status {
update_required: boolean;
/** installed && !update_required && phase is idle. */
can_launch: boolean;
/** This launch would come from the dev checkout, so an available pack does not gate it. */
dev_run: boolean;
/** Nothing is installed, but a dev run can still start the client against the DHP. */
dhp_direct: boolean;
/** On a licensed PC with nothing installed, why a DHP run is not on offer. */
dhp_blocked: string | null;
phase: Phase;
/** Last error, human readable. */
error: string | null;
@@ -331,6 +337,7 @@ export const MOCK_SCENARIOS = [
"sync-conflicts",
"error",
"dev",
"dev-dhp",
] as const;
export type MockScenario = (typeof MOCK_SCENARIOS)[number];