// file: kb-app-demo-desktop/frontend/ts/demo_execution_metadata.ts // version: 12 import * as bootstrap from "bootstrap"; import "simplebar"; import { invoke } from "@tauri-apps/api/core"; import { listen } from "@tauri-apps/api/event"; import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log.ts"; import { renderJsonViewer } from "./json_viewer.ts"; import type { DemoExecutionMetadataMetaplexTokenMetadataScenarioPayload } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataMetaplexTokenMetadataScenarioPayload.ts"; import type { DemoExecutionMetadataProgressPayload } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataProgressPayload.ts"; import type { DemoExecutionMetadataMetaplexTokenMetadataPreparedStepPayload } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataMetaplexTokenMetadataPreparedStepPayload.ts"; import type { DemoExecutionMetadataMetaplexTokenMetadataRequest } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataMetaplexTokenMetadataRequest.ts"; import type { DemoExecutionMetadataMetaplexTokenMetadataSummaryPayload } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataMetaplexTokenMetadataSummaryPayload.ts"; import type { DemoExecutionMetadataSolanaProgramCampaignRequest } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataSolanaProgramCampaignRequest.ts"; import type { DemoExecutionMetadataSolanaProgramCampaignSummaryPayload } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataSolanaProgramCampaignSummaryPayload.ts"; import type { DemoExecutionMetadataSolanaProgramScenarioPayload } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataSolanaProgramScenarioPayload.ts"; import type { DemoExecutionDevnetStoreReadinessPayload } from "./bindings/kb_app_demo_desktop/demo_execution_solana_core/DemoExecutionDevnetStoreReadinessPayload.ts"; import type { DemoExecutionSolanaCoreOptionsPayload } from "./bindings/kb_app_demo_desktop/demo_execution_solana_core/DemoExecutionSolanaCoreOptionsPayload.ts"; import type { DemoExecutionSolanaCoreProfileOption } from "./bindings/kb_app_demo_desktop/demo_execution_solana_core/DemoExecutionSolanaCoreProfileOption.ts"; const storageKey = "kb-app-demo-desktop.metadata.selected-scenario"; const solanaProgramMetadataStorageKey = "kb-app-demo-desktop.metadata.solana-program.selected-scenario"; const profileStorageKey = "kb-app-demo-desktop.metadata.selected-profile"; (window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap; let scenarios: DemoExecutionMetadataMetaplexTokenMetadataScenarioPayload[] = []; let solanaProgramMetadataScenarios: DemoExecutionMetadataSolanaProgramScenarioPayload[] = []; let selectedProfileName: string | null = null; let executionRunning = false; let profileStoreReady = false; let profileOptions: DemoExecutionSolanaCoreProfileOption[] = []; let preparedScenarioId: string | null = null; let preparedStepIndex: number | null = null; const metadataLogLines: string[] = []; const maximumMetadataLogLines = 1200; type MetadataProgressInput = Omit & { signature?: string | null }; function appendMetadataLog(payload: MetadataProgressInput): void { const signature = payload.signature ? ` signature=${payload.signature}` : ""; metadataLogLines.push(`${payload.timestamp} ${payload.level.toUpperCase()} [${payload.stage}] ${payload.message}${signature}`); if (metadataLogLines.length > maximumMetadataLogLines) { metadataLogLines.splice(0, metadataLogLines.length - maximumMetadataLogLines); } const output = element("#metadataExecutionLogOutput"); output.value = metadataLogLines.join("\n"); output.scrollTop = output.scrollHeight; } function titled(title: string, value: unknown): unknown { return { title, value }; } function element(selector: string): T { const value = document.querySelector(selector); if (value === null) { throw new Error(`Élément introuvable : ${selector}`); } return value; } const profileSelectSelectors = ["#metadataProfileSelect", "#solanaProgramMetadataProfileSelect"] as const; const profileHelpSelectors = ["#metadataProfileHelp", "#solanaProgramMetadataProfileHelp"] as const; const profileStatusSelectors = ["#metadataProfileStatusBadge", "#solanaProgramMetadataProfileStatusBadge"] as const; const profileOutputSelectors = ["#metadataProfileOutput", "#solanaProgramMetadataProfileOutput"] as const; function synchronizeProfileSelections(profileName: string): void { for (const selector of profileSelectSelectors) { const select = element(selector); if (Array.from(select.options).some(option => option.value === profileName)) { select.value = profileName; } } selectedProfileName = profileName.length === 0 ? null : profileName; } function updateProfileStatus(text: string, className: string): void { for (const selector of profileStatusSelectors) { const badge = element(selector); badge.textContent = text; badge.className = className; } } function activeSelect(): HTMLSelectElement { return document.querySelector("#metaplexDevnetCollapse.show") !== null ? element("#metadataDevnetScenarioSelect") : element("#metadataSyntheticScenarioSelect"); } function selectedScenario(): DemoExecutionMetadataMetaplexTokenMetadataScenarioPayload | null { const id = activeSelect().value; return scenarios.find(scenario => scenario.id === id) ?? null; } function fillSelect(selector: string, mode: string): void { const select = element(selector); select.replaceChildren(); for (const scenario of scenarios.filter(candidate => candidate.mode === mode)) { const option = document.createElement("option"); option.value = scenario.id; option.textContent = `${scenario.assetFamily} — ${scenario.label}`; select.append(option); } } function operationName(operationCode: string): string { const parts = operationCode.split("."); return parts.length === 0 ? operationCode : parts[parts.length - 1] ?? operationCode; } function renderScenarioSteps(scenario: DemoExecutionMetadataMetaplexTokenMetadataScenarioPayload): void { const select = element("#metadataStepSelect"); const previousValue = Number.parseInt(select.value, 10); select.replaceChildren(); scenario.operationCodes.forEach((operationCode, index) => { const option = document.createElement("option"); option.value = String(index); option.textContent = `${index + 1}. ${operationName(operationCode)}`; select.append(option); }); if (Number.isInteger(previousValue) && previousValue >= 0 && previousValue < scenario.operationCodes.length) { select.value = String(previousValue); } const index = Number.parseInt(select.value, 10); element("#previousMetadataStepButton").disabled = !Number.isInteger(index) || index <= 0; element("#nextMetadataStepButton").disabled = !Number.isInteger(index) || index >= scenario.operationCodes.length - 1; element("#prepareMetadataScenarioStepButton").textContent = Number.isInteger(index) && scenario.operationCodes[index] !== undefined ? `Préparer l’étape ${index + 1} — ${operationName(scenario.operationCodes[index])}` : "Préparer l’étape courante"; } function renderSelectedScenario(): void { const scenario = selectedScenario(); if (scenario === null) { return; } window.localStorage.setItem(storageKey, scenario.id); const networkMode = scenario.mode !== "synthetic"; renderScenarioSteps(scenario); preparedScenarioId = null; preparedStepIndex = null; element("#metadataOperationJson").value = ""; element("#metadataPreflightReadsJson").value = "[]"; element("#metadataPostconditionReadsJson").value = "[]"; element("#metadataRequirements").innerHTML = `
Parcours
${scenario.label}
Famille
${scenario.assetFamily}
Fixture
${scenario.fixtureKind}
État initial
${scenario.initialState}
État attendu
${scenario.resultingState}
Mode
${scenario.mode}
Profil
${networkMode ? selectedProfileName ?? "non chargé" : "non applicable"}
Collection
${scenario.requiresCollection ? "requise" : "non requise"}
Rule set
${scenario.requiresProgrammableRules ? "requis" : "non requis"}
Validation
${scenario.validationStatus}
`; renderJsonViewer("#metadataPreflightOutput", titled("Préflight et opérations", { scenarioId: scenario.id, operationCodes: scenario.operationCodes, checks: ["program_id", "owner", "pda_and_seeds", "required_accounts", "authorities", "stateful_correlations"], networkExecutionPerformed: false, })); renderJsonViewer("#metadataSimulationOutput", titled("Simulation et confirmation", { mode: scenario.mode, policy: "simulation_first", networkExecutionPerformed: false, automaticSubmission: false, operatorConfirmationRequired: true, expectedState: scenario.validationStatus, note: networkMode ? "Renseignez un intent typé puis lancez une simulation RPC réelle." : "Scénario synthétique déterministe.", })); renderJsonViewer("#metadataPostconditionsOutput", titled("Postconditions et preuves", { required: scenario.requiresPostcondition, evidenceKinds: scenario.requiredEvidence, networkEvidenceCollected: false, materializationTargets: scenario.materializationTargets, rule: "Aucune validation réseau n'est déclarée sans simulation RPC réelle, signature éventuelle, slot et postconditions.", })); } async function prepareScenarioStep(): Promise { const profileName = element("#metadataProfileSelect").value; const scenario = selectedScenario(); const stepIndex = Number.parseInt(element("#metadataStepSelect").value, 10); if (profileName.length === 0) { throw new Error("Sélectionnez un profil Devnet."); } if (scenario === null || scenario.mode === "synthetic") { throw new Error("Sélectionnez un scénario Devnet."); } if (!Number.isInteger(stepIndex) || stepIndex < 0) { throw new Error("Sélectionnez une étape valide."); } appendMetadataLog({ timestamp: new Date().toISOString(), level: "info", stage: "fixture", message: `Préparation de ${scenario.id} étape ${stepIndex + 1}` }); const prepared = await invoke("demo_execution_metadata_metaplex_token_metadata_prepare_step", { profileName, scenarioId: scenario.id, stepIndex, previousOperationJson: element("#metadataOperationJson").value.trim().length === 0 ? null : element("#metadataOperationJson").value, }); element("#metadataOperationJson").value = prepared.operationJson; element("#metadataPreflightReadsJson").value = prepared.preflightReadsJson; element("#metadataPostconditionReadsJson").value = prepared.postconditionReadsJson; preparedScenarioId = prepared.scenarioId; preparedStepIndex = prepared.stepIndex; renderJsonViewer("#metadataPreflightOutput", titled("Étape de scénario préparée", { scenarioId: prepared.scenarioId, stepIndex: prepared.stepIndex, operation: prepared.operation, fixtureState: prepared.fixtureState, preflightReads: JSON.parse(prepared.preflightReadsJson), postconditionReads: JSON.parse(prepared.postconditionReadsJson), simulationReady: prepared.simulationReady, submissionReady: prepared.submissionReady, })); appendMetadataLog({ timestamp: new Date().toISOString(), level: "info", stage: "fixture", message: `${prepared.label} prête (${prepared.fixtureState})` }); setExecutionRunning(executionRunning); } function moveScenarioStep(offset: number): void { const select = element("#metadataStepSelect"); const current = Number.parseInt(select.value, 10); const next = current + offset; if (Number.isInteger(next) && next >= 0 && next < select.options.length) { select.value = String(next); const scenario = selectedScenario(); if (scenario !== null) { renderScenarioSteps(scenario); } } } function parseJsonText(value: string, label: string): unknown { try { return JSON.parse(value); } catch (caught) { const message = caught instanceof Error ? caught.message : String(caught); throw new Error(`${label} invalide : ${message}`); } } function selectedProfile(): DemoExecutionSolanaCoreProfileOption | null { if (selectedProfileName === null) { return null; } return profileOptions.find(profile => profile.name === selectedProfileName) ?? null; } function updateProfileHelp(): void { const profile = selectedProfile(); const text = profile === null ? "Aucun profil Devnet compatible." : `wallet=${profile.walletAlias}, spendMax=${profile.maxSpendLamports}, airdropMax=${profile.maxAirdropLamports}, send=${profile.sendEnabled ? "enabled" : "disabled"}`; for (const selector of profileHelpSelectors) { element(selector).textContent = text; } } function setExecutionRunning(value: boolean): void { executionRunning = value; const profile = selectedProfile(); const missingOperation = element("#metadataOperationJson").value.trim().length === 0; const unavailable = value || profile === null || !profileStoreReady || missingOperation; element("#simulateMetadataButton").disabled = unavailable; element("#submitMetadataButton").disabled = unavailable || !profile?.sendEnabled; element("#prepareMetadataScenarioStepButton").disabled = value || profile === null || !profileStoreReady; const solanaProgramConfirmed = element("#solanaProgramMetadataOperatorConfirmed").checked; element("#executeSolanaProgramMetadataCampaignButton").disabled = value || profile === null || !profileStoreReady || !profile.sendEnabled || !solanaProgramConfirmed; } async function executeMetadata(submit: boolean): Promise { if (executionRunning) { return; } const profileName = element("#metadataProfileSelect").value; if (profileName.length === 0 || !profileStoreReady) { throw new Error("Sélectionnez et préparez un profil Devnet."); } const operationJson = element("#metadataOperationJson").value; const scenario = selectedScenario(); const stepIndex = Number.parseInt(element("#metadataStepSelect").value, 10); if (operationJson.trim().length === 0) { throw new Error("Préparez l’étape courante avant l’exécution."); } if (scenario === null || preparedScenarioId !== scenario.id || preparedStepIndex !== stepIndex) { throw new Error("Préparez de nouveau l’étape après toute modification du scénario ou de l’étape."); } const preflightReadsJson = element("#metadataPreflightReadsJson").value; const postconditionReadsJson = element("#metadataPostconditionReadsJson").value; parseJsonText(operationJson, "Intent Metaplex"); parseJsonText(preflightReadsJson, "Lectures de préflight"); parseJsonText(postconditionReadsJson, "Lectures de postcondition"); const operatorConfirmed = element("#metadataOperatorConfirmed").checked; if (submit && !operatorConfirmed) { throw new Error("La soumission exige la confirmation explicite de l’opérateur."); } const request: DemoExecutionMetadataMetaplexTokenMetadataRequest = { profileName, intentId: `desktop-metaplex-${Date.now()}`, operationJson, preflightReadsJson, postconditionReadsJson, submit, operatorConfirmed, materializeAfterConfirmation: element("#metadataMaterializeInput").checked, }; setExecutionRunning(true); const badge = element("#metadataStatusBadge"); badge.textContent = submit ? "Soumission" : "Simulation"; badge.className = "badge text-bg-warning"; appendMetadataLog({ timestamp: new Date().toISOString(), level: "info", stage: submit ? "submit" : "simulation", message: submit ? "Démarrage du parcours Devnet complet." : "Démarrage de la simulation RPC exacte." }); try { const summary = await invoke("demo_execution_metadata_metaplex_token_metadata_execute", { request }); renderJsonViewer("#metadataPreflightOutput", titled("Préflight et opérations", JSON.parse(summary.preflightJson))); renderJsonViewer("#metadataSimulationOutput", titled("Simulation et confirmation", { profileName: summary.profileName, cluster: summary.cluster, genesisHash: summary.genesisHash, walletPublicKey: summary.walletPublicKey, balanceLamports: summary.balanceLamports, simulationSuccess: summary.simulationSuccess, transactionSignature: summary.transactionSignature, confirmationStatus: summary.confirmationStatus, materializationRequested: summary.materializationRequested, materializedSnapshotCount: summary.materializedSnapshotCount, plan: JSON.parse(summary.planJson), simulation: JSON.parse(summary.simulationJson), })); renderJsonViewer("#metadataPostconditionsOutput", titled("Postconditions et preuves", JSON.parse(summary.evidenceJson))); badge.textContent = summary.transactionSignature === null ? "Simulé" : "Confirmé"; badge.className = summary.simulationSuccess ? "badge text-bg-success" : "badge text-bg-danger"; } finally { setExecutionRunning(false); } } function selectedSolanaProgramMetadataScenario(): DemoExecutionMetadataSolanaProgramScenarioPayload | null { const id = element("#solanaProgramMetadataScenarioSelect").value; return solanaProgramMetadataScenarios.find(scenario => scenario.id === id) ?? null; } function renderActiveMetadataSelection(): void { if (document.querySelector("#solanaProgramMetadataCollapse.show") !== null) { renderSolanaProgramMetadataScenario(); return; } renderSelectedScenario(); } function renderSolanaProgramMetadataScenario(): void { const scenario = selectedSolanaProgramMetadataScenario(); if (scenario === null) { return; } window.localStorage.setItem(solanaProgramMetadataStorageKey, scenario.id); renderJsonViewer("#solanaProgramMetadataScenarioOutput", titled("Parcours Solana Program Metadata", { scenarioId: scenario.id, label: scenario.label, fixtureAccount: scenario.fixtureAccount, steps: scenario.steps, })); const allSteps = solanaProgramMetadataScenarios.flatMap(candidate => candidate.steps); element("#metadataRequirements").innerHTML = `
Famille
metadata.solana_program_metadata
Program ID
ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S
Parcours affiché
${scenario.label}
Étapes totales
${allSteps.length}
Transactions
11 minimum
Profil
${selectedProfileName ?? "non chargé"}
`; renderJsonViewer("#metadataPreflightOutput", titled("Préflight et opérations", { family: "metadata.solana_program_metadata", journeys: solanaProgramMetadataScenarios, operationCount: allSteps.length, fixtureAccounts: ["buffer", "metadata"], prefundingTransactionCount: 2, })); renderJsonViewer("#metadataSimulationOutput", titled("Simulation et confirmation", { policy: "simulation_first", submission: "required", operatorConfirmationRequired: true, stopOnFirstFailure: true, networkExecutionPerformed: false, })); renderJsonViewer("#metadataPostconditionsOutput", titled("Postconditions et preuves", { requiredEvidence: ["stateful_preflight", "rpc_simulation", "confirmed_signature", "stateful_postcondition", "materialized_snapshot_or_account_absence"], currentStatus: "not_run", networkEvidenceCollected: false, })); } async function executeSolanaProgramMetadataCampaign(): Promise { if (executionRunning) { return; } const profile = selectedProfile(); if (profile === null || selectedProfileName === null || !profileStoreReady) { throw new Error("Sélectionnez et préparez un profil Devnet."); } if (!profile.sendEnabled) { throw new Error("Le profil sélectionné n’autorise pas les soumissions Devnet."); } const operatorConfirmed = element("#solanaProgramMetadataOperatorConfirmed").checked; if (!operatorConfirmed) { throw new Error("La campagne exige la confirmation explicite du préfinancement et des neuf mutations."); } const request: DemoExecutionMetadataSolanaProgramCampaignRequest = { profileName: selectedProfileName, operatorConfirmed, }; setExecutionRunning(true); const badge = element("#metadataStatusBadge"); badge.textContent = "Campagne ProgM6"; badge.className = "badge text-bg-warning"; appendMetadataLog({ timestamp: new Date().toISOString(), level: "info", stage: "spm_campaign", message: "Démarrage de la campagne complète : 2 préfinancements et 9 opérations." }); try { const summary = await invoke("demo_execution_metadata_solana_program_execute_campaign", { request }); renderJsonViewer("#metadataPreflightOutput", titled("Fixture et opérations préparées", JSON.parse(summary.fixtureJson))); renderJsonViewer("#metadataSimulationOutput", titled("Synthèse de campagne", { profileName: summary.profileName, authority: summary.authority, describedProgram: summary.describedProgram, buffer: summary.buffer, bufferPrefundSignature: summary.bufferPrefundSignature, metadata: summary.metadata, metadataPrefundSignature: summary.metadataPrefundSignature, stepCount: summary.stepCount, confirmedStepCount: summary.confirmedStepCount, materializedSnapshotCount: summary.materializedSnapshotCount, completed: summary.completed, })); renderJsonViewer("#metadataPostconditionsOutput", titled("Preuves ordonnées des neuf opérations", JSON.parse(summary.stepsJson))); element("#metadataRequirements").innerHTML = `
Famille
metadata.solana_program_metadata
Profil
${summary.profileName}
Buffer
${summary.buffer}
Metadata
${summary.metadata}
Confirmées
${summary.confirmedStepCount}/${summary.stepCount}
Snapshots
${summary.materializedSnapshotCount}
`; badge.textContent = summary.completed ? "Campagne confirmée" : "Campagne incomplète"; badge.className = summary.completed ? "badge text-bg-success" : "badge text-bg-danger"; appendMetadataLog({ timestamp: new Date().toISOString(), level: summary.completed ? "info" : "error", stage: "spm_campaign", message: `Campagne terminée : confirmed=${summary.confirmedStepCount}/${summary.stepCount}, snapshots=${summary.materializedSnapshotCount}` }); } finally { setExecutionRunning(false); } } async function loadProfiles(): Promise { const options = await invoke("demo_execution_metadata_options"); profileOptions = options.profiles; for (const selector of profileSelectSelectors) { const select = element(selector); select.replaceChildren(); for (const profile of options.profiles) { const option = document.createElement("option"); option.value = profile.name; option.textContent = `${profile.name} — ${profile.walletAlias}`; select.append(option); } } const restored = window.localStorage.getItem(profileStorageKey); const availableNames = options.profiles.map(profile => profile.name); const selected = restored !== null && availableNames.includes(restored) ? restored : options.defaultProfileName ?? availableNames[0] ?? ""; synchronizeProfileSelections(selected); updateProfileHelp(); if (options.profiles.length === 0) { const message = "Aucun profil Devnet compatible n’a été retourné par la configuration active."; appendMetadataLog({ timestamp: new Date().toISOString(), level: "error", stage: "profile_options", message }); updateProfileStatus("Aucun profil", "badge text-bg-danger"); renderActiveMetadataSelection(); return; } appendMetadataLog({ timestamp: new Date().toISOString(), level: "info", stage: "profile_options", message: `${options.profiles.length} profil(s) Devnet chargé(s).` }); renderActiveMetadataSelection(); await prepareProfile(); } async function prepareProfile(): Promise { const profileName = selectedProfileName ?? ""; if (profileName.length === 0) { updateProfileStatus("Aucun profil", "badge text-bg-danger"); return; } profileStoreReady = false; setExecutionRunning(executionRunning); updateProfileStatus("Préparation", "badge text-bg-warning"); try { const readiness = await invoke("demo_execution_devnet_prepare_profile", { profileName }); synchronizeProfileSelections(profileName); window.localStorage.setItem(profileStorageKey, profileName); for (const selector of profileOutputSelectors) { renderJsonViewer(selector, readiness); } profileStoreReady = true; updateProfileStatus("Base prête", "badge text-bg-success"); setExecutionRunning(executionRunning); renderActiveMetadataSelection(); } catch (caught) { const message = caught instanceof Error ? caught.message : String(caught); profileStoreReady = false; updateProfileStatus("Erreur", "badge text-bg-danger"); setExecutionRunning(executionRunning); frontendError("kb-app-demo-desktop.frontend.execution_metadata", message); } } async function loadScenarios(): Promise { const badge = element("#metadataStatusBadge"); badge.textContent = "Chargement"; badge.className = "badge text-bg-warning"; try { scenarios = await invoke("demo_execution_metadata_metaplex_token_metadata_scenarios"); solanaProgramMetadataScenarios = await invoke("demo_execution_metadata_solana_program_scenarios"); fillSelect("#metadataSyntheticScenarioSelect", "synthetic"); fillSelect("#metadataDevnetScenarioSelect", "network_simulation"); const restored = window.localStorage.getItem(storageKey); for (const selector of ["#metadataSyntheticScenarioSelect", "#metadataDevnetScenarioSelect"]) { const select = element(selector); if (restored !== null && Array.from(select.options).some(option => option.value === restored)) { select.value = restored; } } const solanaSelect = element("#solanaProgramMetadataScenarioSelect"); solanaSelect.replaceChildren(); for (const scenario of solanaProgramMetadataScenarios) { const option = document.createElement("option"); option.value = scenario.id; option.textContent = `${scenario.fixtureAccount} — ${scenario.label}`; solanaSelect.append(option); } const restoredSolana = window.localStorage.getItem(solanaProgramMetadataStorageKey); if (restoredSolana !== null && Array.from(solanaSelect.options).some(option => option.value === restoredSolana)) { solanaSelect.value = restoredSolana; } renderActiveMetadataSelection(); badge.textContent = `${scenarios.length + solanaProgramMetadataScenarios.length} contrats`; badge.className = "badge text-bg-success"; } catch (caught) { const message = caught instanceof Error ? caught.message : String(caught); profileStoreReady = false; badge.textContent = "Erreur"; badge.className = "badge text-bg-danger"; setExecutionRunning(executionRunning); frontendError("kb-app-demo-desktop.frontend.execution_metadata", message); } } document.addEventListener("DOMContentLoaded", () => { installFrontendConsoleBridge("kb-app-demo-desktop.frontend.execution_metadata"); for (const selector of ["#metadataSyntheticScenarioSelect", "#metadataDevnetScenarioSelect"]) { element(selector).addEventListener("change", renderSelectedScenario); } for (const id of ["metaplexSyntheticCollapse", "metaplexDevnetCollapse"]) { element(`#${id}`).addEventListener("shown.bs.collapse", renderSelectedScenario); } element("#solanaProgramMetadataCollapse").addEventListener("shown.bs.collapse", renderSolanaProgramMetadataScenario); element("#solanaProgramMetadataScenarioSelect").addEventListener("change", renderSolanaProgramMetadataScenario); for (const selector of profileSelectSelectors) { element(selector).addEventListener("change", event => { const target = event.currentTarget as HTMLSelectElement; synchronizeProfileSelections(target.value); profileStoreReady = false; updateProfileHelp(); setExecutionRunning(executionRunning); renderActiveMetadataSelection(); void prepareProfile(); }); } element("#prepareMetadataProfileButton").addEventListener("click", () => void prepareProfile()); element("#prepareSolanaProgramMetadataProfileButton").addEventListener("click", () => void prepareProfile()); element("#refreshMetadataScenariosButton").addEventListener("click", () => void loadScenarios()); element("#metadataStepSelect").addEventListener("change", () => { const scenario = selectedScenario(); if (scenario !== null) { renderScenarioSteps(scenario); } }); element("#previousMetadataStepButton").addEventListener("click", () => moveScenarioStep(-1)); element("#nextMetadataStepButton").addEventListener("click", () => moveScenarioStep(1)); element("#metadataOperationJson").addEventListener("input", () => { preparedScenarioId = null; preparedStepIndex = null; setExecutionRunning(executionRunning); }); element("#prepareMetadataScenarioStepButton").addEventListener("click", () => { void prepareScenarioStep().catch(caught => frontendError("kb-app-demo-desktop.frontend.execution_metadata", caught instanceof Error ? caught.message : String(caught))); }); element("#metadataExecutionLogOutput").addEventListener("app-log-clear", () => { metadataLogLines.length = 0; }); void listen("demo-execution-metadata-progress", event => appendMetadataLog(event.payload)); element("#simulateMetadataButton").addEventListener("click", () => { void executeMetadata(false).catch(caught => frontendError("kb-app-demo-desktop.frontend.execution_metadata", caught instanceof Error ? caught.message : String(caught))); }); element("#submitMetadataButton").addEventListener("click", () => { void executeMetadata(true).catch(caught => frontendError("kb-app-demo-desktop.frontend.execution_metadata", caught instanceof Error ? caught.message : String(caught))); }); element("#solanaProgramMetadataOperatorConfirmed").addEventListener("change", () => setExecutionRunning(executionRunning)); element("#executeSolanaProgramMetadataCampaignButton").addEventListener("click", () => { void executeSolanaProgramMetadataCampaign().catch(caught => frontendError("kb-app-demo-desktop.frontend.execution_metadata", caught instanceof Error ? caught.message : String(caught))); }); setExecutionRunning(false); frontendDebug("kb-app-demo-desktop.frontend.execution_metadata", "Metadata execution window loaded"); void loadProfiles().catch(caught => frontendError("kb-app-demo-desktop.frontend.execution_metadata", caught instanceof Error ? caught.message : String(caught))); void loadScenarios(); });