This commit is contained in:
2026-05-01 00:29:32 +02:00
parent b3b0e882b2
commit c542aa9d32
17 changed files with 2347 additions and 49 deletions

View File

@@ -0,0 +1,255 @@
<!-- file: kb_app/frontend/demo_pipeline.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Khadhroony-BoBoBot — Demo Pipeline</title>
<link rel="stylesheet" href="sass/main.scss" />
</head>
<body class="bg-body-tertiary">
<header class="app-header">
<nav class="navbar navbar-expand-lg h-100 py-0 bg-light text-dark">
<div class="container my-0">
<a class="navbar-brand d-flex align-items-center" href="/">
<img alt="Logo" src="imgs/logo.png" class="app-logo" />
<span class="ps-2 fs-4 fw-bold text-primary font-logo">Demo Pipeline</span>
</a>
</div>
</nav>
</header>
<main class="app-main">
<div class="osb-scrollable pt-1 pb-4" data-simplebar>
<div class="container vcentered sketchy-translucid py-4">
<div class="row g-4">
<div class="col-12 col-xxl-4">
<div class="card shadow-sm border-0 h-100">
<div class="card-body">
<h1 class="h4 mb-3">Inspection par signature</h1>
<p class="text-body-secondary mb-3">
Cette fenêtre inspecte la donnée déjà persistée dans <code>kb_lib</code> pour une
signature donnée et affiche létat du pipeline <code>0.7.x</code>.
</p>
<div class="mb-3">
<label for="demoPipelineSignatureInput" class="form-label">Signature</label>
<input id="demoPipelineSignatureInput" type="text" class="form-control font-monospace" spellcheck="false" placeholder="Signature Solana déjà présente dans la base locale" />
</div>
<div class="mb-3">
<label for="demoPipelineCustomTimeframeInput" class="form-label">
Timeframe custom optionnel (secondes)
</label>
<input id="demoPipelineCustomTimeframeInput" type="number" min="1" step="1" class="form-control" placeholder="Ex: 120" />
<div class="form-text">
Les timeframes matérialisés restent chargés depuis la base. Une valeur custom déclenche
une régénération à la demande.
</div>
</div>
<div class="d-flex flex-wrap gap-2 mb-4">
<button id="demoPipelineInspectButton" type="button" class="btn btn-primary">
Inspecter
</button>
<button id="demoPipelineClearButton" type="button" class="btn btn-outline-secondary">
Vider
</button>
</div>
<hr class="my-4" />
<div class="mb-3">
<label for="demoPipelineTokenMintInput" class="form-label">Token mint</label>
<input id="demoPipelineTokenMintInput" type="text" class="form-control font-monospace" spellcheck="false" placeholder="Mint SPL déjà présent dans la base locale" />
</div>
<div class="d-flex flex-wrap gap-2 mb-4">
<button id="demoPipelineInspectTokenButton" type="button" class="btn btn-outline-primary">
Inspecter token
</button>
</div>
<hr class="my-4" />
<div class="mb-3">
<label for="demoPipelinePairIdInput" class="form-label">Pair id</label>
<input
id="demoPipelinePairIdInput"
type="number"
min="1"
step="1"
class="form-control"
placeholder="Identifiant interne de la paire"
/>
</div>
<div class="d-flex flex-wrap gap-2 mb-3">
<button id="demoPipelineInspectPairButton" type="button" class="btn btn-outline-primary">
Inspecter pair
</button>
</div>
<div class="mb-3">
<label for="demoPipelinePoolAddressInput" class="form-label">Pool address</label>
<input
id="demoPipelinePoolAddressInput"
type="text"
class="form-control font-monospace"
spellcheck="false"
placeholder="Adresse du pool déjà présent dans la base locale"
/>
</div>
<div class="d-flex flex-wrap gap-2 mb-4">
<button id="demoPipelineInspectPoolButton" type="button" class="btn btn-outline-primary">
Inspecter pool
</button>
</div>
<hr class="my-4" />
<div class="small text-body-secondary">
<div><strong>But :</strong> vérifier rapidement la cohérence du pipeline <code>0.7.x</code>.</div>
<div><strong>Portée :</strong> lecture seule depuis SQLite via <code>kb_lib</code>.</div>
</div>
</div>
</div>
</div>
<div class="col-12 col-xxl-8">
<div class="card shadow-sm border-0 mb-4">
<div class="card-body">
<h2 class="h5 mb-3">Résumé</h2>
<textarea id="demoPipelineSummaryTextarea" class="form-control font-monospace" rows="10" readonly spellcheck="false"></textarea>
</div>
</div>
<div class="accordion" id="demoPipelineAccordion">
<div class="accordion-item">
<h2 class="accordion-header" id="headingTransaction">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTransaction" aria-expanded="true" aria-controls="collapseTransaction">
Transaction résolue
</button>
</h2>
<div id="collapseTransaction" class="accordion-collapse collapse show" aria-labelledby="headingTransaction" data-bs-parent="#demoPipelineAccordion">
<div class="accordion-body">
<textarea id="demoPipelineTransactionTextarea" class="form-control font-monospace" rows="14" readonly spellcheck="false"></textarea>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="headingDecodedEvents">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseDecodedEvents" aria-expanded="false" aria-controls="collapseDecodedEvents">
Decoded events
</button>
</h2>
<div id="collapseDecodedEvents" class="accordion-collapse collapse" aria-labelledby="headingDecodedEvents" data-bs-parent="#demoPipelineAccordion">
<div class="accordion-body">
<textarea id="demoPipelineDecodedEventsTextarea" class="form-control font-monospace" rows="14" readonly spellcheck="false"></textarea>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="headingPools">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapsePools" aria-expanded="false" aria-controls="collapsePools">
Pools / pairs / origins
</button>
</h2>
<div id="collapsePools" class="accordion-collapse collapse" aria-labelledby="headingPools" data-bs-parent="#demoPipelineAccordion">
<div class="accordion-body">
<div class="mb-3">
<label class="form-label">Pools</label>
<textarea id="demoPipelinePoolsTextarea" class="form-control font-monospace" rows="10" readonly spellcheck="false"></textarea>
</div>
<div class="mb-3">
<label class="form-label">Pairs</label>
<textarea id="demoPipelinePairsTextarea" class="form-control font-monospace" rows="10" readonly spellcheck="false"></textarea>
</div>
<div class="mb-3">
<label class="form-label">Launch attributions</label>
<textarea id="demoPipelineLaunchAttributionsTextarea" class="form-control font-monospace" rows="10" readonly spellcheck="false"></textarea>
</div>
<div>
<label class="form-label">Pool origins</label>
<textarea id="demoPipelinePoolOriginsTextarea" class="form-control font-monospace" rows="10" readonly spellcheck="false"></textarea>
</div>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="headingWallets">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseWallets" aria-expanded="false" aria-controls="collapseWallets">
Wallets / participations / holdings
</button>
</h2>
<div id="collapseWallets" class="accordion-collapse collapse" aria-labelledby="headingWallets" data-bs-parent="#demoPipelineAccordion">
<div class="accordion-body">
<textarea id="demoPipelineWalletsTextarea" class="form-control font-monospace" rows="16" readonly spellcheck="false"></textarea>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="headingTrades">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTrades" aria-expanded="false" aria-controls="collapseTrades">
Trades / metrics / candles / analytic signals
</button>
</h2>
<div id="collapseTrades" class="accordion-collapse collapse" aria-labelledby="headingTrades" data-bs-parent="#demoPipelineAccordion">
<div class="accordion-body">
<div class="mb-3">
<label class="form-label">Trade events</label>
<textarea id="demoPipelineTradeEventsTextarea" class="form-control font-monospace" rows="10" readonly spellcheck="false"></textarea>
</div>
<div class="mb-3">
<label class="form-label">Pair metrics</label>
<textarea id="demoPipelinePairMetricsTextarea" class="form-control font-monospace" rows="10" readonly spellcheck="false"></textarea>
</div>
<div class="mb-3">
<label class="form-label">Pair candles</label>
<textarea id="demoPipelinePairCandlesTextarea" class="form-control font-monospace" rows="12" readonly spellcheck="false"></textarea>
</div>
<div>
<label class="form-label">Pair analytic signals</label>
<textarea id="demoPipelinePairAnalyticSignalsTextarea" class="form-control font-monospace" rows="12" readonly spellcheck="false"></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="card shadow-sm border-0 mt-4">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<h2 class="h5 mb-0">Log UI</h2>
<button id="demoPipelineClearLogButton" type="button" class="btn btn-outline-secondary btn-sm">Clear log</button>
</div>
<textarea id="demoPipelineLogTextarea" class="form-control font-monospace" rows="10" readonly spellcheck="false"></textarea>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
<footer class="app-footer bg-dark text-light">
<div class="container h-100 d-flex align-items-center">
<div class="row flex-grow-1 align-items-center">
<div class="col-12 col-md-6 text-center text-small my-1 my-md-0">
&copy; 2026 SASEDEV — Demo Pipeline
</div>
</div>
</div>
</footer>
<script type="module" src="ts/demo_pipeline.ts" defer></script>
</body>
</html>

View File

@@ -28,6 +28,9 @@
<button id="openDemoWsManagerButton" type="button" class="btn btn-outline-primary">
Demo Ws Manager
</button>
<button id="openDemoPipelineButton" type="button" class="btn btn-primary">
Ouvrir Demo Pipeline
</button>
</div>
</div>
</nav>
@@ -72,6 +75,9 @@
<button id="openDemoWsManagerButtonSecondary" type="button" class="btn btn-primary">
Ouvrir Demo Ws Manager
</button>
<button id="openDemoPipelineButtonSecondary" type="button" class="btn btn-primary">
Ouvrir Demo Pipeline
</button>
</div>
<hr />

View File

@@ -0,0 +1,420 @@
// file: kb_app/frontend/ts/demo_pipeline.ts
import * as bootstrap from "bootstrap";
import "simplebar";
import ResizeObserver from "resize-observer-polyfill";
import { invoke } from "@tauri-apps/api/core";
import { debug, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
interface DemoPipelineInspectRequest {
signature: string;
customTimeframeSeconds: number | null;
}
interface DemoPipelineInspectPayload {
signature: string;
summaryJson: string;
transactionJson: string;
decodedEventsJson: string;
poolsJson: string;
pairsJson: string;
launchAttributionsJson: string;
poolOriginsJson: string;
walletsJson: string;
tradeEventsJson: string;
pairMetricsJson: string;
pairCandlesJson: string;
pairAnalyticSignalsJson: string;
}
interface DemoPipelineInspectTokenRequest {
tokenMint: string;
customTimeframeSeconds: number | null;
}
interface DemoPipelineInspectPairRequest {
pairId: number;
customTimeframeSeconds: number | null;
}
interface DemoPipelineInspectPoolRequest {
poolAddress: string;
customTimeframeSeconds: number | null;
}
function appendLogLine(textarea: HTMLTextAreaElement, line: string): void {
const now = new Date();
const timestamp = now.toLocaleTimeString("fr-CH", { hour12: false });
const lines = textarea.value === "" ? [] : textarea.value.split("\n");
lines.push(`[${timestamp}] ${line}`);
const maxLines = 300;
textarea.value = lines.slice(-maxLines).join("\n");
textarea.scrollTop = textarea.scrollHeight;
}
function clearInspection(
summaryTextarea: HTMLTextAreaElement,
transactionTextarea: HTMLTextAreaElement,
decodedEventsTextarea: HTMLTextAreaElement,
poolsTextarea: HTMLTextAreaElement,
pairsTextarea: HTMLTextAreaElement,
launchAttributionsTextarea: HTMLTextAreaElement,
poolOriginsTextarea: HTMLTextAreaElement,
walletsTextarea: HTMLTextAreaElement,
tradeEventsTextarea: HTMLTextAreaElement,
pairMetricsTextarea: HTMLTextAreaElement,
pairCandlesTextarea: HTMLTextAreaElement,
pairAnalyticSignalsTextarea: HTMLTextAreaElement,
): void {
summaryTextarea.value = "";
transactionTextarea.value = "";
decodedEventsTextarea.value = "";
poolsTextarea.value = "";
pairsTextarea.value = "";
launchAttributionsTextarea.value = "";
poolOriginsTextarea.value = "";
walletsTextarea.value = "";
tradeEventsTextarea.value = "";
pairMetricsTextarea.value = "";
pairCandlesTextarea.value = "";
pairAnalyticSignalsTextarea.value = "";
}
function readCustomTimeframeSeconds(
input: HTMLInputElement,
logTextarea: HTMLTextAreaElement,
): number | null | undefined {
const customTimeframeText = input.value.trim();
if (customTimeframeText === "") {
return null;
}
const parsed = Number.parseInt(customTimeframeText, 10);
if (Number.isNaN(parsed) || parsed <= 0) {
appendLogLine(logTextarea, `[ui] invalid custom timeframe '${customTimeframeText}'`);
return undefined;
}
return parsed;
}
document.addEventListener("DOMContentLoaded", async () => {
void takeoverConsole();
debug("demo_pipeline window loaded");
const sidebarToggle = document.querySelector<HTMLButtonElement>('#sidebarToggle');
if (sidebarToggle) {
// restaurer létat depuis localStorage
if (localStorage.getItem('sidebar-toggle') === 'true') {
document.body.classList.add('sidenav-toggled');
}
sidebarToggle.addEventListener('click', (event) => {
event.preventDefault();
document.body.classList.toggle('sidenav-toggled');
localStorage.setItem('sidebar-toggle', document.body.classList.contains('sidenav-toggled') ? 'true' : 'false');
});
}
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
Array.from(tooltipTriggerList).map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
const toastElList = document.querySelectorAll('.toast');
Array.from(toastElList).map(toastEl => new bootstrap.Toast(toastEl));
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
Array.from(popoverTriggerList).map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl));
const gobackto = location.pathname + location.search;
document.querySelectorAll<HTMLAnchorElement>('a[data-setlang]').forEach((a) => {
const href = a.getAttribute("href");
if (!href) return; // pas de href => on ignore
const url = new URL(href, location.origin);
url.searchParams.set("gobackto", gobackto);
// conserve une URL relative (path + query)
a.setAttribute("href", url.pathname + "?" + url.searchParams.toString());
});
const signatureInput = document.querySelector<HTMLInputElement>("#demoPipelineSignatureInput");
const customTimeframeInput = document.querySelector<HTMLInputElement>("#demoPipelineCustomTimeframeInput");
const inspectButton = document.querySelector<HTMLButtonElement>("#demoPipelineInspectButton");
const clearButton = document.querySelector<HTMLButtonElement>("#demoPipelineClearButton");
const clearLogButton = document.querySelector<HTMLButtonElement>("#demoPipelineClearLogButton");
const summaryTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelineSummaryTextarea");
const transactionTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelineTransactionTextarea");
const decodedEventsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelineDecodedEventsTextarea");
const poolsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelinePoolsTextarea");
const pairsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelinePairsTextarea");
const launchAttributionsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelineLaunchAttributionsTextarea");
const poolOriginsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelinePoolOriginsTextarea");
const walletsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelineWalletsTextarea");
const tradeEventsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelineTradeEventsTextarea");
const pairMetricsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelinePairMetricsTextarea");
const pairCandlesTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelinePairCandlesTextarea");
const pairAnalyticSignalsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelinePairAnalyticSignalsTextarea");
const logTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelineLogTextarea");
const tokenMintInput = document.querySelector<HTMLInputElement>("#demoPipelineTokenMintInput");
const inspectTokenButton = document.querySelector<HTMLButtonElement>("#demoPipelineInspectTokenButton");
const pairIdInput = document.querySelector<HTMLInputElement>("#demoPipelinePairIdInput");
const inspectPairButton = document.querySelector<HTMLButtonElement>("#demoPipelineInspectPairButton");
const poolAddressInput = document.querySelector<HTMLInputElement>("#demoPipelinePoolAddressInput");
const inspectPoolButton = document.querySelector<HTMLButtonElement>("#demoPipelineInspectPoolButton");
if (
!pairIdInput ||
!inspectPairButton ||
!poolAddressInput ||
!inspectPoolButton ||
!tokenMintInput ||
!inspectTokenButton ||
!signatureInput ||
!customTimeframeInput ||
!inspectButton ||
!clearButton ||
!clearLogButton ||
!summaryTextarea ||
!transactionTextarea ||
!decodedEventsTextarea ||
!poolsTextarea ||
!pairsTextarea ||
!launchAttributionsTextarea ||
!poolOriginsTextarea ||
!walletsTextarea ||
!tradeEventsTextarea ||
!pairMetricsTextarea ||
!pairCandlesTextarea ||
!pairAnalyticSignalsTextarea ||
!logTextarea
) {
console.error("demo_pipeline DOM is incomplete");
return;
}
clearButton.addEventListener("click", () => {
clearInspection(
summaryTextarea,
transactionTextarea,
decodedEventsTextarea,
poolsTextarea,
pairsTextarea,
launchAttributionsTextarea,
poolOriginsTextarea,
walletsTextarea,
tradeEventsTextarea,
pairMetricsTextarea,
pairCandlesTextarea,
pairAnalyticSignalsTextarea,
);
signatureInput.value = "";
customTimeframeInput.value = "";
tokenMintInput.value = "";
pairIdInput.value = "";
poolAddressInput.value = "";
appendLogLine(logTextarea, "[ui] inspection state cleared");
});
clearLogButton.addEventListener("click", () => {
logTextarea.value = "";
});
inspectButton.addEventListener("click", async () => {
const signature = signatureInput.value.trim();
if (signature === "") {
appendLogLine(logTextarea, "[ui] signature is required");
return;
}
let customTimeframeSeconds: number | null = null;
const customTimeframeText = customTimeframeInput.value.trim();
if (customTimeframeText !== "") {
const parsed = Number.parseInt(customTimeframeText, 10);
if (Number.isNaN(parsed) || parsed <= 0) {
appendLogLine(logTextarea, `[ui] invalid custom timeframe '${customTimeframeText}'`);
return;
}
customTimeframeSeconds = parsed;
}
appendLogLine(
logTextarea,
`[ui] inspecting signature '${signature}'${customTimeframeSeconds === null ? "" : ` with custom timeframe ${customTimeframeSeconds}s`}`,
);
const request: DemoPipelineInspectRequest = {
signature,
customTimeframeSeconds,
};
try {
const payload = await invoke<DemoPipelineInspectPayload>("demo_pipeline_inspect_signature", { request });
summaryTextarea.value = payload.summaryJson;
transactionTextarea.value = payload.transactionJson;
decodedEventsTextarea.value = payload.decodedEventsJson;
poolsTextarea.value = payload.poolsJson;
pairsTextarea.value = payload.pairsJson;
launchAttributionsTextarea.value = payload.launchAttributionsJson;
poolOriginsTextarea.value = payload.poolOriginsJson;
walletsTextarea.value = payload.walletsJson;
tradeEventsTextarea.value = payload.tradeEventsJson;
pairMetricsTextarea.value = payload.pairMetricsJson;
pairCandlesTextarea.value = payload.pairCandlesJson;
pairAnalyticSignalsTextarea.value = payload.pairAnalyticSignalsJson;
appendLogLine(logTextarea, `[ui] inspection completed for '${payload.signature}'`);
} catch (error) {
appendLogLine(logTextarea, `[ui] inspect error: ${String(error)}`);
}
});
inspectTokenButton.addEventListener("click", async () => {
const tokenMint = tokenMintInput.value.trim();
if (tokenMint === "") {
appendLogLine(logTextarea, "[ui] token mint is required");
return;
}
let customTimeframeSeconds: number | null = null;
const customTimeframeText = customTimeframeInput.value.trim();
if (customTimeframeText !== "") {
const parsed = Number.parseInt(customTimeframeText, 10);
if (Number.isNaN(parsed) || parsed <= 0) {
appendLogLine(logTextarea, `[ui] invalid custom timeframe '${customTimeframeText}'`);
return;
}
customTimeframeSeconds = parsed;
}
appendLogLine(
logTextarea,
`[ui] inspecting token mint '${tokenMint}'${customTimeframeSeconds === null ? "" : ` with custom timeframe ${customTimeframeSeconds}s`}`,
);
const request: DemoPipelineInspectTokenRequest = {
tokenMint,
customTimeframeSeconds,
};
try {
const payload = await invoke<DemoPipelineInspectPayload>("demo_pipeline_inspect_token_mint", { request });
summaryTextarea.value = payload.summaryJson;
transactionTextarea.value = payload.transactionJson;
decodedEventsTextarea.value = payload.decodedEventsJson;
poolsTextarea.value = payload.poolsJson;
pairsTextarea.value = payload.pairsJson;
launchAttributionsTextarea.value = payload.launchAttributionsJson;
poolOriginsTextarea.value = payload.poolOriginsJson;
walletsTextarea.value = payload.walletsJson;
tradeEventsTextarea.value = payload.tradeEventsJson;
pairMetricsTextarea.value = payload.pairMetricsJson;
pairCandlesTextarea.value = payload.pairCandlesJson;
pairAnalyticSignalsTextarea.value = payload.pairAnalyticSignalsJson;
appendLogLine(logTextarea, `[ui] token inspection completed for '${payload.signature}'`);
} catch (error) {
appendLogLine(logTextarea, `[ui] token inspect error: ${String(error)}`);
}
});
inspectPairButton.addEventListener("click", async () => {
const pairIdText = pairIdInput.value.trim();
if (pairIdText === "") {
appendLogLine(logTextarea, "[ui] pair id is required");
return;
}
const parsedPairId = Number.parseInt(pairIdText, 10);
if (Number.isNaN(parsedPairId) || parsedPairId <= 0) {
appendLogLine(logTextarea, `[ui] invalid pair id '${pairIdText}'`);
return;
}
const customTimeframeSeconds = readCustomTimeframeSeconds(customTimeframeInput, logTextarea);
if (customTimeframeSeconds === undefined) {
return;
}
appendLogLine(
logTextarea,
`[ui] inspecting pair id '${parsedPairId}'${customTimeframeSeconds === null ? "" : ` with custom timeframe ${customTimeframeSeconds}s`}`,
);
const request: DemoPipelineInspectPairRequest = {
pairId: parsedPairId,
customTimeframeSeconds,
};
try {
const payload = await invoke<DemoPipelineInspectPayload>("demo_pipeline_inspect_pair_id", { request });
summaryTextarea.value = payload.summaryJson;
transactionTextarea.value = payload.transactionJson;
decodedEventsTextarea.value = payload.decodedEventsJson;
poolsTextarea.value = payload.poolsJson;
pairsTextarea.value = payload.pairsJson;
launchAttributionsTextarea.value = payload.launchAttributionsJson;
poolOriginsTextarea.value = payload.poolOriginsJson;
walletsTextarea.value = payload.walletsJson;
tradeEventsTextarea.value = payload.tradeEventsJson;
pairMetricsTextarea.value = payload.pairMetricsJson;
pairCandlesTextarea.value = payload.pairCandlesJson;
pairAnalyticSignalsTextarea.value = payload.pairAnalyticSignalsJson;
appendLogLine(logTextarea, `[ui] pair inspection completed for '${payload.signature}'`);
} catch (error) {
appendLogLine(logTextarea, `[ui] pair inspect error: ${String(error)}`);
}
});
inspectPoolButton.addEventListener("click", async () => {
const poolAddress = poolAddressInput.value.trim();
if (poolAddress === "") {
appendLogLine(logTextarea, "[ui] pool address is required");
return;
}
const customTimeframeSeconds = readCustomTimeframeSeconds(customTimeframeInput, logTextarea);
if (customTimeframeSeconds === undefined) {
return;
}
appendLogLine(
logTextarea,
`[ui] inspecting pool '${poolAddress}'${customTimeframeSeconds === null ? "" : ` with custom timeframe ${customTimeframeSeconds}s`}`,
);
const request: DemoPipelineInspectPoolRequest = {
poolAddress,
customTimeframeSeconds,
};
try {
const payload = await invoke<DemoPipelineInspectPayload>("demo_pipeline_inspect_pool_address", { request });
summaryTextarea.value = payload.summaryJson;
transactionTextarea.value = payload.transactionJson;
decodedEventsTextarea.value = payload.decodedEventsJson;
poolsTextarea.value = payload.poolsJson;
pairsTextarea.value = payload.pairsJson;
launchAttributionsTextarea.value = payload.launchAttributionsJson;
poolOriginsTextarea.value = payload.poolOriginsJson;
walletsTextarea.value = payload.walletsJson;
tradeEventsTextarea.value = payload.tradeEventsJson;
pairMetricsTextarea.value = payload.pairMetricsJson;
pairCandlesTextarea.value = payload.pairCandlesJson;
pairAnalyticSignalsTextarea.value = payload.pairAnalyticSignalsJson;
appendLogLine(logTextarea, `[ui] pool inspection completed for '${payload.signature}'`);
} catch (error) {
appendLogLine(logTextarea, `[ui] pool inspect error: ${String(error)}`);
}
});
});

View File

@@ -31,8 +31,19 @@ async function openDemoWsManagerWindow(): Promise<void> {
console.error("open_demo_ws_manager_window failed:", error);
}
}
async function openDemoPipelineWindow(): Promise<void> {
try {
await invoke("open_demo_pipeline_window");
} catch (error) {
console.error("open_demo_pipeline_window failed:", error);
}
}
document.addEventListener("DOMContentLoaded", async () => {
void takeoverConsole();
debug("main window loaded");
const sidebarToggle = document.querySelector<HTMLButtonElement>('#sidebarToggle');
if (sidebarToggle) {
// restaurer létat depuis localStorage
@@ -66,14 +77,16 @@ document.addEventListener("DOMContentLoaded", async () => {
// conserve une URL relative (path + query)
a.setAttribute("href", url.pathname + "?" + url.searchParams.toString());
});
const openDemoWsButton = document.querySelector<HTMLButtonElement>("#openDemoWsButton");
const openDemoWsButtonSecondary = document.querySelector<HTMLButtonElement>("#openDemoWsButtonSecondary");
const openDemoHttpButton = document.querySelector<HTMLButtonElement>("#openDemoHttpButton");
const openDemoHttpButtonSecondary = document.querySelector<HTMLButtonElement>("#openDemoHttpButtonSecondary");
const openDemoWsManagerButton = document.querySelector<HTMLButtonElement>("#openDemoWsManagerButton");
const openDemoWsManagerButtonSecondary = document.querySelector<HTMLButtonElement>("#openDemoWsManagerButtonSecondary");
const openDemoPipelineButton = document.querySelector<HTMLButtonElement>("#openDemoPipelineButton");
const openDemoPipelineButtonSecondary = document.querySelector<HTMLButtonElement>("#openDemoPipelineButtonSecondary");
if (openDemoWsButton) {
openDemoWsButton.addEventListener("click", () => {
@@ -86,7 +99,7 @@ document.addEventListener("DOMContentLoaded", async () => {
void openDemoWsWindow();
});
}
if (openDemoHttpButton) {
openDemoHttpButton.addEventListener("click", () => {
void openDemoHttpWindow();
@@ -110,7 +123,17 @@ document.addEventListener("DOMContentLoaded", async () => {
void openDemoWsManagerWindow();
});
}
debug("window loaded");
if (openDemoPipelineButton) {
openDemoPipelineButton.addEventListener("click", () => {
void openDemoPipelineWindow();
});
}
if (openDemoPipelineButtonSecondary) {
openDemoPipelineButtonSecondary.addEventListener("click", () => {
void openDemoPipelineWindow();
});
}
});