internal cleanups and build config

This commit is contained in:
jarek committed 2026-08-09 10:31:28 +02:00
1 parent 95ee76bf97
commit da8c83e20e
8 files changed
+39 -31

No files matched your search

@@ -1,5 +1,4 @@
<script lang="ts">
import { onMount } from "svelte";
import * as Tooltip from "$lib/components/ui/tooltip/index.js";
import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
@@ -11,9 +10,18 @@
const SIDEBAR_STORAGE_KEY = "sidebar:state";
// Read the persisted open/collapsed state SYNCHRONOUSLY (before first paint) so a
// collapsed sidebar doesn't flash open on every load. onMount ran too late — the
// initial render showed the default-open sidebar until localStorage was read.
function initialOpen(): boolean {
if (typeof localStorage === 'undefined') return true;
const stored = localStorage.getItem(SIDEBAR_STORAGE_KEY);
return stored === null ? true : stored === 'true';
}
let {
ref = $bindable(null),
open = $bindable(true),
open = $bindable(initialOpen()),
onOpenChange = () => {},
class: className,
style,
@@ -37,13 +45,6 @@
},
});
// Load state from localStorage on mount
onMount(() => {
const stored = localStorage.getItem(SIDEBAR_STORAGE_KEY);
if (stored !== null) {
open = stored === 'true';
}
});
</script>
<svelte:window onkeydown={sidebar.handleShortcutKeydown} />
+3 -3
View File
@@ -166,12 +166,12 @@ export const repoSnapshotColumns: ColumnConfig[] = [
// Backup destination grid columns
export const backupDestinationColumns: ColumnConfig[] = [
{ id: 'type', label: 'Type', width: 80, minWidth: 60, resizable: false },
{ id: 'type', label: 'Type', sortable: true, sortField: 'type', width: 130, minWidth: 100, resizable: false },
{ id: 'name', label: 'Name', sortable: true, sortField: 'name', width: 180, minWidth: 120, grow: true },
{ id: 'repository', label: 'Repository', width: 280, minWidth: 150, grow: true },
{ id: 'repository', label: 'Repository', sortable: true, sortField: 'repository', width: 280, minWidth: 150, grow: true },
{ id: 'usage', label: 'Usage', width: 100, minWidth: 70 },
{ id: 'stats', label: 'Stats', width: 140, minWidth: 100 },
{ id: 'status', label: 'Status', width: 110, minWidth: 80 },
{ id: 'status', label: 'Status', sortable: true, sortField: 'status', width: 110, minWidth: 80 },
{ id: 'actions', label: '', fixed: 'end', width: 300, resizable: false }
];
+1 -12
View File
@@ -57,7 +57,7 @@ export type ExecutionStatus = 'queued' | 'running' | 'success' | 'warning' | 'fa
* "failed" filter) speak `failed`. A concurrency reject is flagged by the caller
* via `skipped` and lands as `skipped`. Keeping this in ONE named, testable place
* stops a failed backup from being written as `error` — a value the schedules UI
* doesn't recognise, which made failed backups invisible under its filter.
* filter does not recognise.
*/
export function toExecutionStatus(
terminalStatus: TerminalState['status'],
@@ -72,17 +72,6 @@ export function toExecutionStatus(
}
}
/**
* The startup scrub: any operation still `running` when the process restarted
* could not have completed, so it is rewritten to `stale` (distinct from
* `error` — `error` means it ran and failed, `stale` means the process died and
* it should just be re-run). Returns the status to write, or null to leave the
* row untouched.
*/
export function scrubStatus(current: OperationStatus): OperationStatus | null {
return current === 'running' ? 'stale' : null;
}
/**
* Debounce decision for progress writes. Persisting every restic status line
* would hammer the DB, so we write the first update immediately and then at most
+6 -2
View File
@@ -22,7 +22,7 @@ export interface Environment {
const STORAGE_KEY = 'dockhand:environment';
// Load initial state from localStorage
// Load initial state from localStorage (the last-used environment).
function getInitialEnvironment(): CurrentEnvironment | null {
if (browser) {
const stored = localStorage.getItem(STORAGE_KEY);
@@ -131,7 +131,11 @@ function createEnvironmentsStore() {
name: firstEnv.name
});
} else {
console.log(`[EnvStore] Current env ${currentId} still exists, keeping selection`);
// Keep selection; refresh its name from the list (covers a renamed env).
const match = data.find((e) => Number(e.id) === currentId);
if (match && match.name !== current!.name) {
currentEnvironment.set({ id: match.id, name: match.name });
}
}
} else {
// Clear environments on permission denied or other errors
+8 -2
View File
@@ -3,6 +3,7 @@
import { Button } from '$lib/components/ui/button';
import { Badge } from '$lib/components/ui/badge';
import { Loader2, CheckCircle, XCircle, Archive, Copy, Check, Square } from 'lucide-svelte';
import type { Component } from 'svelte';
import LogConsole from '$lib/components/LogConsole.svelte';
// Backup-only progress window: shown for the Play-on-a-config path, which has no
@@ -15,9 +16,14 @@
logs: string[];
error?: string;
onStop?: () => void;
// Optional title icon (e.g. the repo-type icon for a destination action).
// Defaults to the generic Archive icon when omitted.
icon?: Component;
}
let { open = $bindable(), title, status, progress = 0, logs, error, onStop }: Props = $props();
let { open = $bindable(), title, status, progress = 0, logs, error, onStop, icon }: Props = $props();
const TitleIcon = $derived(icon ?? Archive);
let copied = $state(false);
@@ -33,7 +39,7 @@
<Dialog.Content class="max-w-4xl h-[60vh] flex flex-col overflow-hidden" onOpenAutoFocus={(e) => e.preventDefault()}>
<Dialog.Header>
<Dialog.Title class="flex items-center gap-2">
<Archive class="w-5 h-5" />
<TitleIcon class="w-5 h-5 shrink-0" />
{title}
{#if status === 'running'}
<Badge variant="secondary" class="text-xs gap-1"><Loader2 class="w-3 h-3 animate-spin" />Running</Badge>