This commit is contained in:
2026-05-11 11:02:47 +02:00
parent d66afede28
commit 7f130dba6b
49 changed files with 10301 additions and 8481 deletions

View File

@@ -29,11 +29,11 @@
<div class="accordion" id="demoPipeline2LeftAccordion">
<div class="accordion-item border-0 shadow-sm mb-3">
<h1 class="accordion-header" id="demoPipeline2CatalogHeading">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#demoPipeline2CatalogCollapse" aria-expanded="true" aria-controls="demoPipeline2CatalogCollapse">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#demoPipeline2CatalogCollapse" aria-expanded="false" aria-controls="demoPipeline2CatalogCollapse">
Catalogue local
</button>
</h1>
<div id="demoPipeline2CatalogCollapse" class="accordion-collapse collapse show" aria-labelledby="demoPipeline2CatalogHeading" data-bs-parent="#demoPipeline2LeftAccordion">
<div id="demoPipeline2CatalogCollapse" class="accordion-collapse collapse" aria-labelledby="demoPipeline2CatalogHeading" data-bs-parent="#demoPipeline2LeftAccordion">
<div class="accordion-body">
<div class="d-flex gap-2 mb-3">
<button id="demoPipeline2RefreshCatalogButton" type="button" class="btn btn-primary">
@@ -175,6 +175,32 @@
</div>
</div>
<div class="accordion-item border-0 shadow-sm mb-3">
<h2 class="accordion-header" id="demoPipeline2ProtocolCandidatesHeading">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#demoPipeline2ProtocolCandidatesCollapse" aria-expanded="false" aria-controls="demoPipeline2ProtocolCandidatesCollapse">
Protocol candidates
</button>
</h2>
<div id="demoPipeline2ProtocolCandidatesCollapse" class="accordion-collapse collapse" aria-labelledby="demoPipeline2ProtocolCandidatesHeading" data-bs-parent="#demoPipeline2LeftAccordion">
<div class="accordion-body">
<p class="small text-body-secondary mb-3">
Résume les programmes candidats par priorité : transaction_count, occurrence_count, dernier slot et dernière signature.
</p>
<div class="mb-3">
<label for="demoPipeline2ProtocolCandidateLimitInput" class="form-label">Summary limit</label>
<input id="demoPipeline2ProtocolCandidateLimitInput" type="number" min="1" step="1" class="form-control" value="50" />
</div>
<div class="d-flex gap-2">
<button id="demoPipeline2RefreshProtocolCandidatesButton" type="button" class="btn btn-outline-warning">
Refresh protocol candidates
</button>
</div>
</div>
</div>
</div>
<div class="accordion-item border-0 shadow-sm">
<h2 class="accordion-header" id="demoPipeline2CandlesControlHeading">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#demoPipeline2CandlesControlCollapse" aria-expanded="false" aria-controls="demoPipeline2CandlesControlCollapse">
@@ -265,13 +291,26 @@
</div>
</div>
<div class="accordion-item border-0 shadow-sm mb-3">
<h2 class="accordion-header" id="demoPipeline2ProtocolCandidateSummaryHeading">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#demoPipeline2ProtocolCandidateSummaryCollapse" aria-expanded="false" aria-controls="demoPipeline2ProtocolCandidateSummaryCollapse">
Protocol candidate summaries
</button>
</h2>
<div id="demoPipeline2ProtocolCandidateSummaryCollapse" class="accordion-collapse collapse" aria-labelledby="demoPipeline2ProtocolCandidateSummaryHeading" data-bs-parent="#demoPipeline2ContentAccordion">
<div class="accordion-body">
<textarea id="demoPipeline2ProtocolCandidateSummariesTextarea" class="form-control font-monospace" rows="16" readonly spellcheck="false"></textarea>
</div>
</div>
</div>
<div class="accordion-item border-0 shadow-sm mb-3">
<h2 class="accordion-header" id="demoPipeline2ChartHeading">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#demoPipeline2ChartCollapse" aria-expanded="true" aria-controls="demoPipeline2ChartCollapse">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#demoPipeline2ChartCollapse" aria-expanded="false" aria-controls="demoPipeline2ChartCollapse">
Candles / OHLCV
</button>
</h2>
<div id="demoPipeline2ChartCollapse" class="accordion-collapse collapse show" aria-labelledby="demoPipeline2ChartHeading" data-bs-parent="#demoPipeline2ContentAccordion">
<div id="demoPipeline2ChartCollapse" class="accordion-collapse collapse" aria-labelledby="demoPipeline2ChartHeading" data-bs-parent="#demoPipeline2ContentAccordion">
<div class="accordion-body">
<div class="d-flex flex-wrap justify-content-between align-items-center gap-3 mb-3">
<div id="demoPipeline2ChartMeta" class="small text-body-secondary">

View File

@@ -0,0 +1,10 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Response payload for protocol candidate summary diagnostics.
*/
export type DemoPipeline2ProtocolCandidateSummaryPayload = {
/**
* Pretty JSON summary rows.
*/
summariesJson: string, };

View File

@@ -0,0 +1,10 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Request payload for protocol candidate summary diagnostics.
*/
export type DemoPipeline2ProtocolCandidateSummaryRequest = {
/**
* Maximum number of summary rows to return.
*/
limit: number, };

View File

@@ -15,6 +15,8 @@ import type { DemoPipeline2PairCandlesRequest } from "./bindings/DemoPipeline2Pa
import type { DemoPipeline2PairCandlesPayload } from "./bindings/DemoPipeline2PairCandlesPayload.ts";
import type { DemoPipeline2LocalDiagnosticsPayload } from "./bindings/DemoPipeline2LocalDiagnosticsPayload.ts";
import type { DemoPipeline2LocalValidationPayload } from "./bindings/DemoPipeline2LocalValidationPayload.ts";
import { DemoPipeline2ProtocolCandidateSummaryRequest } from './bindings/DemoPipeline2ProtocolCandidateSummaryRequest.ts';
import { DemoPipeline2ProtocolCandidateSummaryPayload } from './bindings/DemoPipeline2ProtocolCandidateSummaryPayload.ts';
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
@@ -354,6 +356,9 @@ document.addEventListener("DOMContentLoaded", async () => {
const diagnoseLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2DiagnoseLocalPipelineButton");
const validateLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2ValidateLocalPipelineButton");
const protocolCandidateLimitInput = document.querySelector<HTMLInputElement>("#demoPipeline2ProtocolCandidateLimitInput");
const refreshProtocolCandidatesButton = document.querySelector<HTMLButtonElement>("#demoPipeline2RefreshProtocolCandidatesButton");
const pairSelect = document.querySelector<HTMLSelectElement>("#demoPipeline2PairSelect");
const timeframeSelect = document.querySelector<HTMLSelectElement>("#demoPipeline2TimeframeSelect");
const customTimeframeInput = document.querySelector<HTMLInputElement>("#demoPipeline2CustomTimeframeInput");
@@ -366,6 +371,8 @@ document.addEventListener("DOMContentLoaded", async () => {
const localDiagnosticsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipeline2LocalDiagnosticsTextarea");
const localValidationTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipeline2LocalValidationTextarea");
const protocolCandidateSummariesTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipeline2ProtocolCandidateSummariesTextarea");
const clearLogButton = document.querySelector<HTMLButtonElement>("#demoPipeline2ClearLogButton");
const logTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipeline2LogTextarea");
@@ -388,6 +395,8 @@ document.addEventListener("DOMContentLoaded", async () => {
!replayLocalPipelineButton ||
!diagnoseLocalPipelineButton ||
!validateLocalPipelineButton ||
!protocolCandidateLimitInput ||
!refreshProtocolCandidatesButton ||
!pairSelect ||
!timeframeSelect ||
!customTimeframeInput ||
@@ -396,6 +405,7 @@ document.addEventListener("DOMContentLoaded", async () => {
!backfillSummaryTextarea ||
!localDiagnosticsTextarea ||
!localValidationTextarea ||
!protocolCandidateSummariesTextarea ||
!chartElement ||
!chartMeta ||
!clearLogButton ||
@@ -644,6 +654,45 @@ document.addEventListener("DOMContentLoaded", async () => {
}
});
refreshProtocolCandidatesButton.addEventListener("click", async () => {
const limit = readPositiveIntegerInput(
protocolCandidateLimitInput,
logTextarea,
"protocolCandidateSummaryLimit",
);
if (limit === undefined) {
return;
}
appendLogLine(
logTextarea,
`[ui] loading protocol candidate summaries with limit '${limit.toString()}'`,
);
const request: DemoPipeline2ProtocolCandidateSummaryRequest = {
limit,
};
try {
const payload = await invoke<DemoPipeline2ProtocolCandidateSummaryPayload>(
"demo_pipeline2_get_protocol_candidate_summaries",
{ request },
);
protocolCandidateSummariesTextarea.value = payload.summariesJson;
appendLogLine(
logTextarea,
"[ui] protocol candidate summaries loaded",
);
} catch (error) {
appendLogLine(
logTextarea,
`[ui] protocol candidate summary error: ${String(error)}`,
);
}
});
loadCandlesButton.addEventListener("click", async () => {
const pairIdText = pairSelect.value.trim();
if (pairIdText === "") {

View File

@@ -1,7 +1,7 @@
{
"name": "kb-demo-app",
"private": true,
"version": "0.7.27",
"version": "0.7.28",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -10,6 +10,34 @@
use tauri::Manager;
use ts_rs::TS;
/// Request payload for protocol candidate summary diagnostics.
#[derive(Clone, Debug, serde::Deserialize, ts_rs::TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2ProtocolCandidateSummaryRequest.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2ProtocolCandidateSummaryRequest {
/// Maximum number of summary rows to return.
pub limit: u32,
}
/// Response payload for protocol candidate summary diagnostics.
#[derive(Clone, Debug, serde::Serialize, ts_rs::TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2ProtocolCandidateSummaryPayload.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2ProtocolCandidateSummaryPayload {
/// Pretty JSON summary rows.
pub summaries_json: std::string::String,
}
/// Local diagnostics payload returned to the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
@@ -690,6 +718,42 @@ pub(crate) struct DemoPipeline2PairCandlesPayload {
pub candles_json: std::string::String,
}
/// Lists protocol candidate summaries ordered by investigation priority.
#[tauri::command]
pub(crate) async fn demo_pipeline2_get_protocol_candidate_summaries(
state: tauri::State<'_, crate::AppState>,
request: DemoPipeline2ProtocolCandidateSummaryRequest,
) -> Result<DemoPipeline2ProtocolCandidateSummaryPayload, std::string::String> {
if request.limit == 0 {
return Err("protocol candidate summary limit must be > 0".to_string());
}
let summaries_result = kb_lib::query_protocol_candidate_summaries_list_by_priority(
state.database.as_ref(),
request.limit,
)
.await;
let summaries = match summaries_result {
Ok(summaries) => summaries,
Err(error) => {
return Err(format!(
"cannot list protocol candidate summaries with limit '{}': {}",
request.limit, error
));
},
};
let summaries_json_result = serde_json::to_string_pretty(&summaries);
let summaries_json = match summaries_json_result {
Ok(summaries_json) => summaries_json,
Err(error) => {
return Err(format!(
"cannot serialize protocol candidate summaries: {}",
error
));
},
};
return Ok(DemoPipeline2ProtocolCandidateSummaryPayload { summaries_json });
}
/// Runs local pipeline diagnostics from persisted data only.
#[tauri::command]
pub(crate) async fn demo_pipeline2_diagnose_local_pipeline(

View File

@@ -153,6 +153,7 @@ pub async fn run() -> Result<(), kb_lib::Error> {
crate::demo_pipeline2::demo_pipeline2_replay_local_pipeline,
crate::demo_pipeline2::demo_pipeline2_diagnose_local_pipeline,
crate::demo_pipeline2::demo_pipeline2_validate_local_pipeline,
crate::demo_pipeline2::demo_pipeline2_get_protocol_candidate_summaries,
]);
tauri_builder = tauri_builder.plugin(tracing_builder.build::<tauri::Wry>());
tauri_builder = tauri_builder.setup(|app| {

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "kb-demo-app",
"version": "0.7.27",
"version": "0.7.28",
"identifier": "com.sasedev.kb-demo-app",
"build": {
"beforeDevCommand": "npm run dev",