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:
@@ -0,0 +1,80 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
/** 0..1, or null for indeterminate. */
|
||||
value: number | null;
|
||||
label: string;
|
||||
valueText?: string;
|
||||
}
|
||||
|
||||
let { value, label, valueText }: Props = $props();
|
||||
|
||||
const percent = $derived(value === null ? null : Math.round(Math.min(1, Math.max(0, value)) * 1000) / 10);
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="bar"
|
||||
class:indeterminate={percent === null}
|
||||
role="progressbar"
|
||||
aria-label={label}
|
||||
aria-valuemin={0}
|
||||
aria-valuemax={100}
|
||||
aria-valuenow={percent ?? undefined}
|
||||
aria-valuetext={valueText}
|
||||
>
|
||||
{#if percent === null}
|
||||
<span class="sweep"></span>
|
||||
{:else}
|
||||
<span class="fill" style:width="{percent}%"></span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.bar {
|
||||
position: relative;
|
||||
height: 4px;
|
||||
overflow: hidden;
|
||||
background: var(--line);
|
||||
}
|
||||
|
||||
.fill {
|
||||
position: absolute;
|
||||
inset: 0 auto 0 0;
|
||||
background: var(--accent);
|
||||
box-shadow: 0 0 14px rgba(91, 140, 255, 0.6);
|
||||
transition: width var(--dur) linear;
|
||||
}
|
||||
.fill::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
background: #d6e2ff;
|
||||
}
|
||||
|
||||
.sweep {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: -35%;
|
||||
width: 35%;
|
||||
background: linear-gradient(90deg, transparent, var(--accent), transparent);
|
||||
animation: sweep 1.8s var(--ease) infinite;
|
||||
}
|
||||
|
||||
@keyframes sweep {
|
||||
to {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.sweep {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
opacity: 0.5;
|
||||
background: repeating-linear-gradient(90deg, var(--accent) 0 10px, transparent 10px 18px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user