// file: crates/ksp-app-backfill-desk/frontend/ts/main.ts // version: 2 import "bootstrap"; import ResizeObserver from "resize-observer-polyfill"; import "simplebar"; import { getCurrentWindow } from "@tauri-apps/api/window"; import type { BackfillDeskOptionsDto } from "./bindings/ksp_app_backfill_desk/dto_common/BackfillDeskOptionsDto.ts"; import type { BackfillHttpRouteOptionDto } from "./bindings/ksp_app_backfill_desk/dto_common/BackfillHttpRouteOptionDto.ts"; import type { ShellStatusDto } from "./bindings/ksp_app_backfill_desk/dto_common/ShellStatusDto.ts"; import { frontendDebug, frontendInfo, frontendTrace, frontendWarn, installFrontendConsoleBridge } from "./frontend_log"; import { invokeKsp } from "./invoke"; (window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; installFrontendConsoleBridge("main"); type ViewId = "backfill" | "diagnostics"; const viewTitles: Record = { backfill: "Backfill", diagnostics: "Diagnostics", }; function isViewId(value: string | undefined): value is ViewId { return value === "backfill" || value === "diagnostics"; } function activateView(viewId: ViewId, source: "startup" | "user"): void { document.querySelectorAll("[data-view-panel]").forEach(panel => { panel.hidden = panel.dataset.viewPanel !== viewId; }); document.querySelectorAll("[data-view]").forEach(button => { const active = button.dataset.view === viewId; button.classList.toggle("active", active); button.setAttribute("aria-current", active ? "page" : "false"); }); const header = document.querySelector("#headerViewTitle"); if (header) { header.textContent = viewTitles[viewId]; } document.title = `Backfill Desk — ${viewTitles[viewId]}`; frontendTrace("main", "Backfill Desk view DOM updated", { viewId, source }); } function bindFrontendInteractions(): void { document.addEventListener( "click", event => { const source = event.target; if (!(source instanceof Element)) { return; } const control = source.closest('button, a, input, select, textarea, [role="button"], [role="tab"], [data-view]'); if (!control) { return; } frontendTrace("main", "Backfill Desk frontend control clicked", { controlId: control.id || null, role: control.getAttribute("role"), tagName: control.tagName.toLowerCase(), viewId: control.dataset.view ?? null, }); }, true, ); frontendTrace("main", "Backfill Desk frontend control interaction logger installed"); } function bindNavigation(): void { document.querySelectorAll("[data-view]").forEach(button => { button.addEventListener("click", () => { const viewId = button.dataset.view; frontendDebug("main", "Backfill Desk navigation tab clicked", { viewId: viewId ?? "unknown" }); if (isViewId(viewId)) { activateView(viewId, "user"); } }); }); frontendTrace("main", "Backfill Desk navigation handlers installed"); } function routeLabel(route: BackfillHttpRouteOptionDto): string { if (route.pooled) { return `Pool multi-provider — ${route.providers.join(" + ")}`; } return route.providers.length === 1 ? `Provider — ${route.providers[0]}` : route.role; } function renderBackfillOptions(options: BackfillDeskOptionsDto): void { const select = document.querySelector("#backfillHttpRoute"); if (select) { const previous = select.value; select.replaceChildren(); for (const route of options.httpRoutes) { const option = document.createElement("option"); option.value = route.role; option.textContent = routeLabel(route); select.append(option); } select.disabled = !options.transportReady || options.httpRoutes.length === 0; const reusable = options.httpRoutes.some(route => route.role === previous); if (reusable) { select.value = previous; } } const values: Record = { backfillNetwork: options.configuredNetworks.length === 1 ? options.configuredNetworks[0] : options.configuredNetworks.join(", ") || "indisponible", backfillTransportReady: options.transportReady ? "ready" : "non ready", backfillStoreReady: options.storeReady ? "ready" : "non ready", backfillCompositionReady: options.compositionReady ? "ready" : "non ready", }; for (const [id, value] of Object.entries(values)) { const element = document.querySelector(`#${id}`); if (element) { element.textContent = value; } } const diagnostic = document.querySelector("#backfillOptionsDiagnostic"); if (diagnostic) { const error = options.transportDiagnostic ?? options.storeDiagnostic; diagnostic.hidden = error === null; diagnostic.textContent = error === null ? "" : `${error.domain}/${error.code}: ${error.message}`; } frontendTrace("main", "Backfill Desk HTTP route options rendered", { compositionReady: options.compositionReady, routeCount: options.httpRoutes.length, transportReady: options.transportReady, }); } async function loadBackfillOptions(source: "startup" | "user"): Promise { frontendDebug("main", "Backfill Desk HTTP route options load started", { source }); const options = await invokeKsp("main", "backfill_options"); renderBackfillOptions(options); frontendDebug("main", "Backfill Desk HTTP route options load completed", { source, routeCount: options.httpRoutes.length }); } function bindBackfillRouteSelection(): void { const select = document.querySelector("#backfillHttpRoute"); if (!select) { return; } select.addEventListener("change", () => { frontendDebug("main", "Backfill Desk HTTP route selection changed", { role: select.value || "none" }); }); frontendTrace("main", "Backfill Desk HTTP route selection handler installed"); } function renderRuntimeStatus(status: ShellStatusDto): void { const values: Record = { runtimeVersion: status.applicationVersion, runtimeShellPhase: status.shellPhase, runtimeConfigDocuments: status.configDocumentCount.toString(), runtimeLoggingProfile: status.activeLoggingProfile ?? "fallback transitoire", runtimeLoggingFallback: status.fallbackLoggingActive ? "oui" : "non", }; for (const [id, value] of Object.entries(values)) { const element = document.querySelector(`#${id}`); if (element) { element.textContent = value; } } const badge = document.querySelector("#appVersionBadge"); if (badge) { badge.textContent = status.applicationVersion; } const diagnostic = document.querySelector("#runtimeDiagnostic"); if (diagnostic) { diagnostic.hidden = status.startupDiagnostic === null; diagnostic.textContent = status.startupDiagnostic === null ? "" : `${status.startupDiagnostic.domain}/${status.startupDiagnostic.code}: ${status.startupDiagnostic.message}`; } frontendTrace("main", "Backfill Desk runtime status rendered", { fallbackLoggingActive: status.fallbackLoggingActive, configDocumentCount: status.configDocumentCount, }); } async function loadRuntimeStatus(source: "startup" | "user"): Promise { frontendDebug("main", "Backfill Desk runtime status load started", { source }); const status = await invokeKsp("main", "get_runtime_status"); renderRuntimeStatus(status); frontendDebug("main", "Backfill Desk runtime status load completed", { source }); } function bindRuntimeStatusRefresh(): void { const button = document.querySelector("#refreshRuntimeStatus"); if (!button) { return; } button.addEventListener("click", () => { frontendDebug("main", "Backfill Desk runtime status refresh button clicked"); void loadRuntimeStatus("user").catch(() => frontendWarn("main", "Backfill Desk runtime status refresh failed")); }); frontendTrace("main", "Backfill Desk runtime status refresh handler installed"); } async function initializeMain(): Promise { const windowLabel = getCurrentWindow().label; frontendInfo("main", "Backfill Desk main frontend loaded", { windowLabel }); bindFrontendInteractions(); bindNavigation(); bindRuntimeStatusRefresh(); bindBackfillRouteSelection(); activateView("backfill", "startup"); try { await Promise.all([loadRuntimeStatus("startup"), loadBackfillOptions("startup")]); } catch { frontendWarn("main", "Backfill Desk startup runtime status load failed"); } } document.addEventListener("DOMContentLoaded", () => { void initializeMain(); });