b7f3061be3
- 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>
81 lines
1.6 KiB
Svelte
81 lines
1.6 KiB
Svelte
<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>
|