420 lines
17 KiB
TypeScript
420 lines
17 KiB
TypeScript
// 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)}`);
|
||
}
|
||
});
|
||
}); |