v0.1.4-pre.018
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-config-desk/frontend/ts/documents.ts
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
//! Documents panel backed exclusively by ksp-config-lib inventory, validation and management APIs.
|
||||
|
||||
@@ -13,6 +13,7 @@ import type { ConfigDocumentSaveResultDto } from "./bindings/ksp_app_config_desk
|
||||
import type { ConfigDocumentSummaryDto } from "./bindings/ksp_app_config_desk/documents/ConfigDocumentSummaryDto";
|
||||
import { frontendDebug, frontendTrace, frontendWarn } from "./frontend_log";
|
||||
import { invokeKsp } from "./invoke";
|
||||
import { requestViewActivation, specializedEditorForFileId } from "./shell_registry";
|
||||
|
||||
interface DocumentsDataTable {
|
||||
destroy(): unknown;
|
||||
@@ -84,6 +85,12 @@ function renderDetail(detail: ConfigDocumentDetailDto): void {
|
||||
if (saveButton) {
|
||||
saveButton.disabled = detail.source === null;
|
||||
}
|
||||
const specializedEditor = specializedEditorForFileId(detail.summary.fileId);
|
||||
const specializedButton = document.querySelector<HTMLButtonElement>("#openSpecializedEditor");
|
||||
if (specializedButton) {
|
||||
specializedButton.hidden = specializedEditor === null;
|
||||
specializedButton.textContent = specializedEditor?.label ?? "Ouvrir l'éditeur spécialisé";
|
||||
}
|
||||
const detailPanel = document.querySelector<HTMLElement>("#documentDetailPanel");
|
||||
if (detailPanel) {
|
||||
detailPanel.hidden = false;
|
||||
@@ -270,6 +277,15 @@ function bindDocumentActions(): void {
|
||||
frontendTrace("main", "Document save button clicked");
|
||||
void saveActiveDocument();
|
||||
});
|
||||
document.querySelector<HTMLButtonElement>("#openSpecializedEditor")?.addEventListener("click", () => {
|
||||
const fileId = activeDocument?.summary.fileId ?? "";
|
||||
const descriptor = specializedEditorForFileId(fileId);
|
||||
if (!descriptor) {
|
||||
return;
|
||||
}
|
||||
frontendDebug("main", "Specialized Config editor requested", { fileId, viewId: descriptor.viewId });
|
||||
requestViewActivation(descriptor.viewId);
|
||||
});
|
||||
frontendTrace("main", "Documents panel handlers installed");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-config-desk/frontend/ts/main.ts
|
||||
// version: 12
|
||||
// version: 13
|
||||
|
||||
import "bootstrap";
|
||||
import ResizeObserver from "resize-observer-polyfill";
|
||||
@@ -12,60 +12,32 @@ import { initializeEnvironmentPanel } from "./environment";
|
||||
import { initializeLoggingPanel } from "./logging";
|
||||
import { initializeProfilesPanel } from "./profiles";
|
||||
import { invokeKsp } from "./invoke";
|
||||
import { isViewId, registeredViewIds, viewRegistry, type ViewId } from "./shell_registry";
|
||||
import { clearTransientSecretReveal } from "./secret_reveal";
|
||||
|
||||
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
||||
installFrontendConsoleBridge("main");
|
||||
|
||||
type ViewId = "overview" | "documents" | "profiles" | "environment" | "logging";
|
||||
|
||||
const viewCopy: Record<ViewId, { title: string; description: string }> = {
|
||||
overview: {
|
||||
title: "Vue d'ensemble",
|
||||
description: "État sûr du bootstrap Config Desk et du runtime Logging actuellement actif.",
|
||||
},
|
||||
documents: {
|
||||
title: "Documents",
|
||||
description: "Inventaire, diagnostics et réparation validée des documents Config enregistrés.",
|
||||
},
|
||||
profiles: {
|
||||
title: "Profils",
|
||||
description: "Sélection default/explicite, vues global/profil/effective sûre et provenance Config.",
|
||||
},
|
||||
environment: {
|
||||
title: "Environnement / .env",
|
||||
description: "Rapport sûr desired/effective/source/shadow et management atomique des variables .env KSP/KSPB.",
|
||||
},
|
||||
logging: {
|
||||
title: "Logging",
|
||||
description: "Édition typée, hot reload runtime, profils actifs et panneau de test du routing Logging KSP.",
|
||||
},
|
||||
};
|
||||
|
||||
function isViewId(value: string): value is ViewId {
|
||||
return Object.prototype.hasOwnProperty.call(viewCopy, value);
|
||||
}
|
||||
|
||||
function activateView(viewId: ViewId, source: "startup" | "user"): void {
|
||||
function activateView(viewId: ViewId, source: "startup" | "user" | "registry"): void {
|
||||
if (viewId !== "environment") {
|
||||
clearTransientSecretReveal();
|
||||
}
|
||||
if (source === "user") {
|
||||
frontendDebug("main", "Main navigation activated", { viewId });
|
||||
if (source !== "startup") {
|
||||
frontendDebug("main", "Main navigation activated", { viewId, source });
|
||||
}
|
||||
const copy = viewCopy[viewId];
|
||||
const descriptor = viewRegistry[viewId];
|
||||
const title = document.querySelector<HTMLElement>("#viewTitle");
|
||||
const headerTitle = document.querySelector<HTMLElement>("#headerViewTitle");
|
||||
const description = document.querySelector<HTMLElement>("#viewDescription");
|
||||
if (title) {
|
||||
title.textContent = copy.title;
|
||||
title.textContent = descriptor.title;
|
||||
}
|
||||
if (headerTitle) {
|
||||
headerTitle.textContent = copy.title;
|
||||
headerTitle.textContent = descriptor.title;
|
||||
}
|
||||
document.title = `Config Desk — ${copy.title}`;
|
||||
document.title = `Config Desk — ${descriptor.title}`;
|
||||
if (description) {
|
||||
description.textContent = copy.description;
|
||||
description.textContent = descriptor.description;
|
||||
}
|
||||
document.querySelectorAll<HTMLButtonElement>("[data-view]").forEach(button => {
|
||||
const active = button.dataset.view === viewId;
|
||||
@@ -73,25 +45,11 @@ function activateView(viewId: ViewId, source: "startup" | "user"): void {
|
||||
button.setAttribute("aria-current", active ? "page" : "false");
|
||||
});
|
||||
frontendTrace("main", "Main navigation tab state changed", { viewId, source });
|
||||
const overview = document.querySelector<HTMLElement>("#overviewPanel");
|
||||
if (overview) {
|
||||
overview.hidden = viewId !== "overview";
|
||||
}
|
||||
const documents = document.querySelector<HTMLElement>("#documentsPanel");
|
||||
if (documents) {
|
||||
documents.hidden = viewId !== "documents";
|
||||
}
|
||||
const profiles = document.querySelector<HTMLElement>("#profilesPanel");
|
||||
if (profiles) {
|
||||
profiles.hidden = viewId !== "profiles";
|
||||
}
|
||||
const environment = document.querySelector<HTMLElement>("#environmentPanel");
|
||||
if (environment) {
|
||||
environment.hidden = viewId !== "environment";
|
||||
}
|
||||
const logging = document.querySelector<HTMLElement>("#loggingPanel");
|
||||
if (logging) {
|
||||
logging.hidden = viewId !== "logging";
|
||||
for (const registeredViewId of registeredViewIds()) {
|
||||
const panel = document.querySelector<HTMLElement>(`#${viewRegistry[registeredViewId].panelId}`);
|
||||
if (panel) {
|
||||
panel.hidden = registeredViewId !== viewId;
|
||||
}
|
||||
}
|
||||
frontendTrace("main", "Main view DOM updated", { viewId, source });
|
||||
}
|
||||
@@ -106,6 +64,13 @@ function bindNavigation(): void {
|
||||
}
|
||||
});
|
||||
});
|
||||
window.addEventListener("ksp:activate-view", event => {
|
||||
const detail = event instanceof CustomEvent ? event.detail : null;
|
||||
const requestedView = typeof detail === "object" && detail !== null && "viewId" in detail ? String(detail.viewId) : "";
|
||||
if (isViewId(requestedView)) {
|
||||
activateView(requestedView, "registry");
|
||||
}
|
||||
});
|
||||
frontendTrace("main", "Main navigation handlers installed");
|
||||
}
|
||||
|
||||
|
||||
70
crates/ksp-app-config-desk/frontend/ts/shell_registry.ts
Normal file
70
crates/ksp-app-config-desk/frontend/ts/shell_registry.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
// file: crates/ksp-app-config-desk/frontend/ts/shell_registry.ts
|
||||
// version: 1
|
||||
|
||||
//! Declarative shell and specialized-editor registry for Config Desk frontend extensibility.
|
||||
|
||||
export type ViewId = "overview" | "documents" | "profiles" | "environment" | "logging";
|
||||
|
||||
export interface ViewDescriptor {
|
||||
title: string;
|
||||
description: string;
|
||||
panelId: string;
|
||||
}
|
||||
|
||||
export interface SpecializedEditorDescriptor {
|
||||
fileId: string;
|
||||
viewId: ViewId;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export const viewRegistry: Record<ViewId, ViewDescriptor> = {
|
||||
overview: {
|
||||
title: "Vue d'ensemble",
|
||||
description: "État sûr du bootstrap Config Desk et du runtime Logging actuellement actif.",
|
||||
panelId: "overviewPanel",
|
||||
},
|
||||
documents: {
|
||||
title: "Documents",
|
||||
description: "Inventaire, diagnostics et réparation validée des documents Config enregistrés.",
|
||||
panelId: "documentsPanel",
|
||||
},
|
||||
profiles: {
|
||||
title: "Profils",
|
||||
description: "Sélection default/explicite, vues global/profil/effective sûre et provenance Config.",
|
||||
panelId: "profilesPanel",
|
||||
},
|
||||
environment: {
|
||||
title: "Environnement / .env",
|
||||
description: "Rapport sûr desired/effective/source/shadow et management atomique des variables .env KSP/KSPB.",
|
||||
panelId: "environmentPanel",
|
||||
},
|
||||
logging: {
|
||||
title: "Logging",
|
||||
description: "Édition typée, hot reload runtime, profils actifs et panneau de test du routing Logging KSP.",
|
||||
panelId: "loggingPanel",
|
||||
},
|
||||
};
|
||||
|
||||
const specializedEditorRegistry: SpecializedEditorDescriptor[] = [
|
||||
{
|
||||
fileId: "cfg.std.logging",
|
||||
viewId: "logging",
|
||||
label: "Ouvrir l'éditeur Logging",
|
||||
},
|
||||
];
|
||||
|
||||
export function registeredViewIds(): ViewId[] {
|
||||
return Object.keys(viewRegistry) as ViewId[];
|
||||
}
|
||||
|
||||
export function isViewId(value: string): value is ViewId {
|
||||
return Object.prototype.hasOwnProperty.call(viewRegistry, value);
|
||||
}
|
||||
|
||||
export function specializedEditorForFileId(fileId: string): SpecializedEditorDescriptor | null {
|
||||
return specializedEditorRegistry.find(descriptor => descriptor.fileId === fileId) ?? null;
|
||||
}
|
||||
|
||||
export function requestViewActivation(viewId: ViewId): void {
|
||||
window.dispatchEvent(new CustomEvent("ksp:activate-view", { detail: { viewId } }));
|
||||
}
|
||||
Reference in New Issue
Block a user