ODIAN Launcher: hub, installer, local stack and remembered sign-in (M2)

- Tauri 2 + Svelte 5 hub in ODIAN dark: patch notes and roadmap, install and
  update progress, local stack status, settings with DHP and MCP addresses.
- crates/odian-packs: Rust installer for pack format v1 (signed manifests,
  resumable verified downloads from a DHP or an ODIAN drive), byte-compatible
  with ODIAN_MCP_Server's Python odian_packs.
- Local stack on 127.0.0.1: Postgres -> Engine API -> tile server -> Odian.exe,
  one Job Object, clean shutdown; database snapshot restore on a fresh PC only.
- Keycloak sign-in through the DHP's MCP, remembered until Sign out with a
  launcher session that renews the Engine token silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kelorastus
2026-09-15 15:23:17 +02:00
commit b7f3061be3
102 changed files with 47611 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
<script lang="ts" module>
export type IconName =
| "minimize"
| "maximize"
| "restore"
| "close"
| "settings"
| "refresh"
| "play"
| "download"
| "update"
| "user"
| "stop"
| "folder"
| "drive"
| "activity"
| "alert"
| "check"
| "signout"
| "key"
| "globe";
</script>
<script lang="ts">
interface Props {
name: IconName;
size?: number;
strokeWidth?: number;
}
let { name, size = 16, strokeWidth = 1.5 }: Props = $props();
</script>
<!-- Original 16px line icons; decorative, so hidden from assistive tech. -->
<svg
class="icon"
width={size}
height={size}
viewBox="0 0 16 16"
fill="none"
stroke="currentColor"
stroke-width={strokeWidth}
stroke-linecap="square"
aria-hidden="true"
focusable="false"
>
{#if name === "minimize"}
<path d="M3.5 8.5h9" />
{:else if name === "maximize"}
<rect x="3.5" y="3.5" width="9" height="9" />
{:else if name === "restore"}
<path d="M6 5.5V3.5h6.5V10h-2" />
<rect x="3.5" y="6" width="7" height="6.5" />
{:else if name === "close"}
<path d="M4 4l8 8M12 4l-8 8" />
{:else if name === "settings"}
<path d="M2.5 4.5h6M12.5 4.5h1M2.5 11.5h1M7.5 11.5h6" />
<rect x="8.5" y="3" width="3" height="3" />
<rect x="4.5" y="10" width="3" height="3" />
{:else if name === "refresh"}
<path d="M13 8a5 5 0 1 1-1.5-3.55" />
<path d="M12 2v3h-3" />
{:else if name === "play"}
<path d="M5 3.25l7.5 4.75L5 12.75z" fill="currentColor" stroke="none" />
{:else if name === "download"}
<path d="M8 2.5v8M4.75 7.25L8 10.5l3.25-3.25M3 13.5h10" />
{:else if name === "update"}
<path d="M8 13.5v-8M4.75 8.75L8 5.5l3.25 3.25M3 2.5h10" />
{:else if name === "user"}
<circle cx="8" cy="5.5" r="2.5" />
<path d="M3 13.5c.6-2.3 2.6-3.75 5-3.75s4.4 1.45 5 3.75" />
{:else if name === "stop"}
<rect x="4" y="4" width="8" height="8" fill="currentColor" stroke="none" />
{:else if name === "folder"}
<path d="M2.5 4h4l1.5 1.5h5.5v7h-11z" />
{:else if name === "drive"}
<rect x="2.5" y="5" width="11" height="6.5" />
<path d="M5 8.25h.5M10 8.25h1.5" />
{:else if name === "activity"}
<path d="M1.5 8.5h3l2-5 3 9 2-4h3" />
{:else if name === "alert"}
<path d="M8 2.5l6 11H2z" />
<path d="M8 6.75v3M8 11.5v.25" />
{:else if name === "check"}
<path d="M3.5 8.5l3 3 6-7" />
{:else if name === "signout"}
<path d="M9.5 3h-6v10h6M7 8h6.5M11 5.5L13.5 8 11 10.5" />
{:else if name === "key"}
<circle cx="5.25" cy="10.75" r="2.5" />
<path d="M7 9l5.5-5.5M10.5 5.5L12 7" />
{:else if name === "globe"}
<circle cx="8" cy="8" r="5.5" />
<ellipse cx="8" cy="8" rx="2.4" ry="5.5" />
<path d="M2.5 8h11" />
{/if}
</svg>
<style>
.icon {
display: block;
flex: none;
}
</style>