v0.2.6-pre.007

This commit is contained in:
2026-08-21 10:50:36 +02:00
parent 4c9086e2f0
commit f11f0921b0
25 changed files with 871 additions and 42 deletions

View File

@@ -40,9 +40,9 @@
<div class="d-flex align-items-center justify-content-between mb-4">
<div>
<h1 id="viewTitle" class="h3 mb-1">Dashboard</h1>
<p class="text-body-secondary mb-0">Création native, sessions durables et unlock explicite VIEW/OWNER actifs.</p>
<p class="text-body-secondary mb-0">Identité autorisée et balance SOL via le Transport HTTP composite sont actives.</p>
</div>
<span class="badge text-bg-secondary">0.2.6-pre.006</span>
<span class="badge text-bg-secondary">0.2.6-pre.007</span>
</div>
<section data-view-panel="dashboard">
<div class="row g-3">
@@ -56,6 +56,11 @@
<dt class="col-sm-5">Documents Config enregistrés</dt><dd id="runtimeConfigDocuments" class="col-sm-7"></dd>
<dt class="col-sm-5">Profil composite</dt><dd id="runtimeCompositeProfile" class="col-sm-7"></dd>
<dt class="col-sm-5">Profil Logging</dt><dd id="runtimeLoggingProfile" class="col-sm-7"></dd>
<dt class="col-sm-5">Profil Transport</dt><dd id="runtimeTransportProfile" class="col-sm-7"></dd>
<dt class="col-sm-5">Cluster Transport</dt><dd id="runtimeTransportClusters" class="col-sm-7"></dd>
<dt class="col-sm-5">Provider Transport</dt><dd id="runtimeTransportProviders" class="col-sm-7"></dd>
<dt class="col-sm-5">Endpoints Transport</dt><dd id="runtimeTransportEndpoints" class="col-sm-7"></dd>
<dt class="col-sm-5">Rôle Transport</dt><dd id="runtimeTransportRole" class="col-sm-7"></dd>
<dt class="col-sm-5">Profil Wallet</dt><dd id="runtimeWalletProfile" class="col-sm-7"></dd>
<dt class="col-sm-5">Fallback Logging</dt><dd id="runtimeLoggingFallback" class="col-sm-7"></dd>
<dt class="col-sm-5">Racine Wallet</dt><dd id="runtimeWalletsDirectory" class="col-sm-7 text-break"></dd>
@@ -80,6 +85,7 @@
<dt class="col-5">Pubkey</dt><dd id="currentWalletPubkey" class="col-7 text-break"></dd>
<dt class="col-5">Alias</dt><dd id="currentWalletAlias" class="col-7 text-break"></dd>
<dt class="col-5">Notes</dt><dd id="currentWalletNotes" class="col-7 text-break"></dd>
<dt class="col-5">Balance SOL</dt><dd id="currentWalletBalanceSol" class="col-7"></dd>
</dl>
</div>
</div>
@@ -159,7 +165,29 @@
</div>
</div>
</section>
<section data-view-panel="details" hidden><div class="app-placeholder"><i class="fa-solid fa-id-card"></i><p>Les détails protégés seront affichés uniquement après VIEW/OWNER.</p></div></section>
<section data-view-panel="details" hidden>
<div class="card shadow-sm">
<div class="card-header d-flex align-items-center justify-content-between">
<span class="fw-semibold">Détails autorisés et balance réseau</span>
<button id="refreshWalletBalance" class="btn btn-outline-primary btn-sm" type="button" disabled>
<i class="fa-solid fa-coins me-2" aria-hidden="true"></i>Refresh balance
</button>
</div>
<div class="card-body">
<dl class="row mb-3">
<dt class="col-md-4">Pubkey</dt><dd id="detailsWalletPubkey" class="col-md-8 text-break"></dd>
<dt class="col-md-4">Alias</dt><dd id="detailsWalletAlias" class="col-md-8 text-break"></dd>
<dt class="col-md-4">Notes</dt><dd id="detailsWalletNotes" class="col-md-8 text-break"></dd>
<dt class="col-md-4">Lamports</dt><dd id="detailsBalanceLamports" class="col-md-8"></dd>
<dt class="col-md-4">SOL</dt><dd id="detailsBalanceSol" class="col-md-8"></dd>
<dt class="col-md-4">RPC slot</dt><dd id="detailsBalanceSlot" class="col-md-8"></dd>
<dt class="col-md-4">RPC api_version</dt><dd id="detailsBalanceApiVersion" class="col-md-8"></dd>
<dt class="col-md-4">Transport</dt><dd id="detailsBalanceTransport" class="col-md-8"></dd>
</dl>
<div id="balanceStatus" class="alert alert-secondary mb-0" role="status">Unlock VIEW ou OWNER pour interroger getBalance.</div>
</div>
</div>
</section>
<section data-view-panel="security" hidden>
<div class="row g-3">
<div class="col-xl-7">
@@ -204,13 +232,21 @@
</div>
</div>
</section>
<section data-view-panel="diagnostics" hidden><div class="app-placeholder"><i class="fa-solid fa-stethoscope"></i><p>Les diagnostics Config/Wallet/Transport seront enrichis progressivement.</p></div></section>
<section data-view-panel="diagnostics" hidden>
<div class="card shadow-sm"><div class="card-header fw-semibold">Diagnostics Transport sûrs</div><div class="card-body"><dl class="row mb-0">
<dt class="col-md-4">Profil</dt><dd id="diagnosticsTransportProfile" class="col-md-8"></dd>
<dt class="col-md-4">Clusters</dt><dd id="diagnosticsTransportClusters" class="col-md-8"></dd>
<dt class="col-md-4">Providers</dt><dd id="diagnosticsTransportProviders" class="col-md-8"></dd>
<dt class="col-md-4">Endpoints disponibles</dt><dd id="diagnosticsTransportEndpoints" class="col-md-8"></dd>
<dt class="col-md-4">Rôle balance</dt><dd id="diagnosticsTransportRole" class="col-md-8"></dd>
</dl></div></div>
</section>
</div>
</section>
</div>
</main>
<footer class="app-footer bg-dark text-white-50 d-flex align-items-center px-4">
<span>KSP Wallet Desk · manual/configured unlock pre.006</span>
<span>KSP Wallet Desk · authorized identity + getBalance pre.007</span>
</footer>
<script type="module" src="./ts/main.ts"></script>
</body>

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-wallet-desk/frontend/ts/main.ts
// version: 6
// version: 7
import "bootstrap";
import DataTable from "datatables.net-bs5";
@@ -7,6 +7,7 @@ import "datatables.net-select-bs5";
import ResizeObserver from "resize-observer-polyfill";
import "simplebar";
import { getCurrentWindow } from "@tauri-apps/api/window";
import type { WalletBalanceDto } from "./bindings/ksp_app_wallet_desk/wallet_balance/WalletBalanceDto.ts";
import type { RuntimeStatusDto } from "./bindings/ksp_app_wallet_desk/dto_common/RuntimeStatusDto.ts";
import type { LockedWalletDto } from "./bindings/ksp_app_wallet_desk/wallet_inventory/LockedWalletDto.ts";
import type { WalletInventoryEntryDto } from "./bindings/ksp_app_wallet_desk/wallet_inventory/WalletInventoryEntryDto.ts";
@@ -169,7 +170,7 @@ function initializeWalletTable(): void {
zeroRecords: "Aucun wallet correspondant.",
},
});
frontendDebug("main", "Wallet inventory DataTable initialized", { phase: "pre.006-wallet-unlock" });
frontendDebug("main", "Wallet inventory DataTable initialized", { phase: "pre.007-wallet-balance" });
}
function renderWalletInventory(entries: WalletInventoryEntryDto[]): void {
@@ -193,6 +194,7 @@ function setText(selector: string, value: string): void {
}
function updateSessionActions(): void {
const refreshBalance = document.querySelector<HTMLButtonElement>("#refreshWalletBalance");
const lock = document.querySelector<HTMLButtonElement>('[data-shell-action="lock"]');
const deselect = document.querySelector<HTMLButtonElement>('[data-shell-action="deselect"]');
if (lock) {
@@ -201,6 +203,9 @@ function updateSessionActions(): void {
if (deselect) {
deselect.disabled = activeSessionState === "no_selection" || activeSessionState === "privileged_operation";
}
if (refreshBalance) {
refreshBalance.disabled = activeSessionState !== "view_open" && activeSessionState !== "owner_open";
}
updateUnlockActions();
}
@@ -255,10 +260,24 @@ function clearUnlockSensitiveInputs(): void {
}
}
function clearBalanceProjection(): void {
setText("#currentWalletBalanceSol", "—");
setText("#detailsBalanceLamports", "—");
setText("#detailsBalanceSol", "—");
setText("#detailsBalanceSlot", "—");
setText("#detailsBalanceApiVersion", "—");
setText("#detailsBalanceTransport", "—");
setText("#balanceStatus", "Unlock VIEW ou OWNER pour interroger getBalance.");
}
function clearAuthorizedProjection(): void {
setText("#currentWalletPubkey", "—");
setText("#currentWalletAlias", "—");
setText("#currentWalletNotes", "—");
setText("#detailsWalletPubkey", "—");
setText("#detailsWalletAlias", "—");
setText("#detailsWalletNotes", "—");
clearBalanceProjection();
clearCreateFormSensitiveInputs();
clearUnlockSensitiveInputs();
}
@@ -325,7 +344,13 @@ function renderAuthorizedWallet(wallet: WalletAuthorizedDto): void {
setText("#currentWalletSecretCandidates", configuredSecretCandidateCount.toString());
setText("#currentWalletPubkey", wallet.pubkey);
setText("#currentWalletAlias", wallet.alias ?? "—");
setText("#currentWalletNotes", wallet.notes.length === 0 ? "—" : wallet.notes.map(note => note.text).join(" · "));
const notesText = wallet.notes.length === 0 ? "—" : wallet.notes.map(note => note.text).join(" · ");
setText("#currentWalletNotes", notesText);
setText("#detailsWalletPubkey", wallet.pubkey);
setText("#detailsWalletAlias", wallet.alias ?? "—");
setText("#detailsWalletNotes", notesText);
clearBalanceProjection();
setText("#balanceStatus", "Session autorisée. Refresh balance appelle getBalance avec la Pubkey détenue par Rust.");
setText("#unlockStatus", `${capabilityLabel} ouvert. Lock pour purger le handle autorisé.`);
updateSessionActions();
frontendDebug("main", "Authorized Wallet session rendered", {
@@ -570,10 +595,61 @@ async function deselectCurrentWallet(): Promise<void> {
}
}
function renderWalletBalance(balance: WalletBalanceDto): void {
if (activeWalletId !== balance.walletId || (activeSessionState !== "view_open" && activeSessionState !== "owner_open")) {
frontendWarn("main", "Stale Wallet balance response ignored", { walletId: balance.walletId });
return;
}
setText("#currentWalletBalanceSol", balance.sol);
setText("#detailsBalanceLamports", balance.lamports);
setText("#detailsBalanceSol", balance.sol);
setText("#detailsBalanceSlot", balance.slot.toString());
setText("#detailsBalanceApiVersion", balance.apiVersion ?? "—");
setText("#detailsBalanceTransport", `${balance.transportProfile} / ${balance.transportRole}`);
setText("#balanceStatus", "Balance réseau actualisée.");
frontendInfo("main", "Wallet balance rendered", { lamports: balance.lamports, slot: balance.slot, walletId: balance.walletId });
}
async function refreshWalletBalance(): Promise<void> {
if (!activeWalletId || (activeSessionState !== "view_open" && activeSessionState !== "owner_open")) {
return;
}
const walletId = activeWalletId;
setText("#balanceStatus", "Interrogation getBalance en cours…");
frontendDebug("main", "Wallet balance refresh requested", { walletId });
try {
const balance = await invokeKsp<WalletBalanceDto>("main", "refresh_wallet_balance");
renderWalletBalance(balance);
} catch {
if (activeWalletId === walletId) {
clearBalanceProjection();
setText("#balanceStatus", "Échec getBalance. Consulter le diagnostic et les logs.");
}
frontendWarn("main", "Wallet balance refresh failed", { walletId });
}
}
function bindBalanceActions(): void {
document.querySelector<HTMLButtonElement>("#refreshWalletBalance")?.addEventListener("click", () => {
void refreshWalletBalance();
});
frontendTrace("main", "Wallet balance action handlers installed");
}
function renderRuntimeStatus(status: RuntimeStatusDto): void {
setText("#runtimeVersion", status.applicationVersion);
setText("#runtimeCompositeProfile", status.activeCompositeProfile);
setText("#runtimeLoggingProfile", status.activeLoggingProfile ?? "fallback transitoire");
setText("#runtimeTransportProfile", status.activeTransportProfile);
setText("#runtimeTransportClusters", status.transportClusters.join(", ") || "—");
setText("#runtimeTransportProviders", status.transportProviders.join(", ") || "—");
setText("#runtimeTransportEndpoints", `${status.transportAvailableEndpointCount}/${status.transportEndpointCount} disponibles`);
setText("#runtimeTransportRole", status.transportRole);
setText("#diagnosticsTransportProfile", status.activeTransportProfile);
setText("#diagnosticsTransportClusters", status.transportClusters.join(", ") || "—");
setText("#diagnosticsTransportProviders", status.transportProviders.join(", ") || "—");
setText("#diagnosticsTransportEndpoints", `${status.transportAvailableEndpointCount}/${status.transportEndpointCount}`);
setText("#diagnosticsTransportRole", status.transportRole);
setText("#runtimeWalletProfile", status.activeWalletProfile);
setText("#runtimeLoggingFallback", status.fallbackLoggingActive ? "oui" : "non");
setText("#runtimeConfigDocuments", status.configDocumentCount.toString());
@@ -582,11 +658,13 @@ function renderRuntimeStatus(status: RuntimeStatusDto): void {
setText("#runtimeEffectiveWalletsDirectory", status.effectiveWalletsDirectory);
setText("#runtimeWalletDirectoryCreated", status.effectiveWalletsDirectoryCreatedOnStartup ? "oui" : "non, déjà présent");
setText("#runtimeShellPhase", status.shellPhase);
setText("#shellStatus", "Config résolue ; création et unlock VIEW/OWNER prêts.");
setText("#shellStatus", "Config/Transport résolus ; VIEW/OWNER et getBalance prêts.");
frontendTrace("main", "Wallet Desk runtime status rendered", {
compositeProfile: status.activeCompositeProfile,
fallbackLoggingActive: status.fallbackLoggingActive,
shellPhase: status.shellPhase,
transportEndpointCount: status.transportEndpointCount,
transportProfile: status.activeTransportProfile,
walletDirectoryCreatedOnStartup: status.effectiveWalletsDirectoryCreatedOnStartup,
walletProfile: status.activeWalletProfile,
});
@@ -629,6 +707,7 @@ async function initializeMain(): Promise<void> {
bindWalletTableSelection();
bindCreateWalletForm();
bindUnlockActions();
bindBalanceActions();
bindShellActions();
clearSelectedWallet();
activateView("dashboard", "startup");