Per vedere e provare il bot senza Unraid: F5 in VS Code, scripts/run-dev e scripts/run-docker usano tutti deploy/local come /config e /data, con la stessa disposizione del container, così chiavi e configurazione valgono in ogni modo. ENCELADO_DATA_DIR separa i dati anche fuori dal container. scripts/screenshots rigenera docs/img dal server campione; /api/health in --sample non finge più un motore acceso. docker stop ora termina in un secondo con «SIGTERM: arresto» (PosixSignalRegistration), provato sull'immagine 5.0.0 costruita dalla catena. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
8.5 KiB
Linee guida dell'interfaccia web
Aggiornato: 2026-09-23 (5.0, ADR-0007). L'interfaccia è servita dal bot stesso (src/Encelado.Server/Web/wwwroot/: index.html, login.html, app.css, app.js, icon.svg, manifest.webmanifest, incorporati nell'assembly). Nessuna libreria, nessun font remoto, nessun <script src=http…>: un test lo verifica (EmbeddedUiTests).
Principi
- La UI non decide niente. Legge lo snapshot (
/api/snapshot,/api/stream) e manda comandi (POST /api/commands/<nome>). Ogni regola di rischio sta nel motore. - Le stesse informazioni della dashboard di sempre: equity, P&L di oggi, P&L aperto (conto e basket), drawdown, basket aperti con in attesa / orfane / esterne, margine; la tabella dei basket; prossimo evento, collegamento eToro, Telegram; l'attività. I dettagli stanno nei tooltip (
title) e nel log, non in più riquadri. - Ogni numero ha un tooltip con la formula o la fonte; ogni importo convertito porta nel tooltip il valore in USD e il tasso usato. Il ledger resta in USD.
- Gli orari a schermo sono nel fuso scelto (
ui.timeZone,TZ); la barra in alto mostra sempre anche l'UTC, che è l'ora del ledger. - Le azioni irreversibili chiedono conferma in un dialogo: chiusura di un basket, kill-switch (con la spunta «chiudi anche le esterne», deselezionata), reset (motivazione ≥ 10 caratteri), rimozione delle chiavi, ripristino della configurazione, avvio Live (frase
CONFERMO LIVEscritta per intero).
Material 3 senza libreria
Colori (token CSS in :root)
| Ruolo | Scuro (default) | Chiaro | Uso |
|---|---|---|---|
--primary / --on-primary |
#adc6ff / #002e69 |
#005ac1 / #ffffff |
pulsante AVVIA, tab attiva, focus, curva dell'equity |
--primary-container / --on-primary-container |
#1f4e9c / #d8e2ff |
#d8e2ff / #001a41 |
voce attiva del rail |
--surface, --surface-container-low/-/high/highest |
#101418, #181c20, #1c2024, #262a2f, #31353a |
#f8f9ff, #f2f3f9, #eceef4, #e6e8ee, #e0e2e8 |
sfondo, rail, card, KPI, intestazioni delle tabelle |
--on-surface / --on-surface-variant |
#e0e2e8 / #c3c6d0 |
#191c20 / #43474e |
testo, testo secondario |
--outline / --outline-variant |
#8d9099 / #43474e |
#74777f / #c3c6d0 |
bordi di input e tabelle |
--error / --error-container |
#ffb4ab / #93000a |
#ba1a1a / #ffdad6 |
kill-switch, banner rosso, stato Halted |
--up / --down / --warn |
#7fd39a / #ff8a80 / #f5b74f |
#1b7f3b / #c62828 / #9a6400 |
solo P&L, pip, stati (verde/rosso), avvisi (giallo) |
Una sola tinta d'accento; contrasto ≥ 4,5:1 per il testo. Il tema si sceglie in Impostazioni ▸ Interfaccia (ui.theme: dark | light) e viene ricordato nel browser (localStorage, solo comodità per chi guarda); prefers-color-scheme è rispettato quando il file dice dark ma il sistema è chiaro solo per color-scheme.
Tipografia
Roboto, "Segoe UI", system-ui, sans-serif; monospazio Cascadia Mono, JetBrains Mono, Consolas per log, id e attività. Scala: titolo di pagina 20 px (title-large), titoli delle card 16 px (title-medium), KPI 24 px (headline-small), corpo 14 px, etichette 12 px. Cifre tabulari (font-variant-numeric: tabular-nums) su ogni numero.
Forma, elevazione, stati
Angoli 12 px (card, banner), 16 px (dialoghi grandi 28 px come da M3), pulsanti a pillola (999 px). Elevazione tonale (superfici a livelli), niente ombre salvo dialoghi e snackbar. State layer su hover (8 %) e pressed (12 %) tramite ::after. :focus-visible con anello di 2 px in --primary.
Componenti
| Componente | Dove | Note |
|---|---|---|
| Navigation rail | sinistra, 80 px chiuso / 256 px aperto | quattro voci: Dashboard, Storico ordini, Log, Impostazioni; stato ricordato (ui.navExpanded e localStorage); sotto 600 px diventa un drawer con scrim; in fondo i chip ambiente e stato |
| Top app bar | sopra la pagina | titolo della pagina, ora UTC e ora nel fuso, selettore della valuta, AVVIA/FERMA, KILL-SWITCH (attivo solo a motore acceso); linear progress durante un comando |
| Cards | dashboard, contesto, impostazioni | KPI con etichetta, valore, riga secondaria |
| Data table compatta | basket, storico, log | intestazione fissa, righe da 32 px, allineamento a destra dei numeri; sotto 600 px la tabella dei basket diventa cards |
| Chips | ambiente (PAPER blu, DEMO giallo, LIVE rosso), stato del motore, stato del basket, origine della posizione |
testo in minuscolo per gli stati |
| Buttons | filled (AVVIA, Salva, conferme), tonal (Applica, Avvia il ripristino), outlined danger (KILL-SWITCH), text (Chiudi, Esporta) | mai due filled affiancati |
| Select, input, switch | filtri, impostazioni, Segui nel log | validazione lato server con messaggio accanto al campo |
| Dialog | conferma, prompt (motivazione, CONFERMO LIVE), ripristino in cinque passi |
<dialog> nativo, showModal, Esc chiude |
| Snackbar | esito dei comandi | 4 s, 8 s se errore |
| Banner | dashboard | rosso per errore, kill-switch con residuo, equity stop, sospensione; giallo per entrate bloccate, non riconciliate, orfane |
| Tooltip | title su ogni numero e riga |
la formula, la fonte, il valore in USD |
Classi di finestra
| Classe | Larghezza | Layout |
|---|---|---|
| compact | < 600 px | rail nascosto (menu ☰), KPI su due colonne, basket a cards, orologi nascosti |
| medium | 600-839 px | rail 80 px, KPI su due colonne, contesto su una colonna, impostazioni su una colonna |
| expanded | ≥ 840 px | rail 80/256 px, KPI su tre colonne (due righe), contesto su tre, impostazioni su due |
Accessibilità
Tastiera completa (Tab su rail, pulsanti, tabelle, dialoghi; Esc chiude drawer e dialoghi), aria-label su icone e selettori, aria-current="page" sulla voce attiva, role="tablist"/"tab"/"tabpanel" nello storico, role="status" su banner e snackbar, link «Vai al contenuto», prefers-reduced-motion (nessuna transizione), prefers-color-scheme.
Pagine
- Dashboard: banner, sei KPI, tabella dei basket (nome e cross, stato, z, ρ, HL, pip, TP, P&L, costo, prossimo evento, Chiudi), preset, tre card di contesto, attività (ultime righe di log).
- Storico ordini: tre tab. Ordini (da
orders.jsonl: unità richieste ed eseguite, prezzi, slippage, stato, esito, id), Posizioni (storico eToro classificatobasket/orfana-bot/esterna/movimento di cassa, con P&L lordo, fee, netto, pip, durata, motivo), Profitti per periodo (oggi, ieri, 7 e 30 giorni, mese corrente e precedente, anno, tutto, intervallo personalizzato; curva dell'equity realizzata in SVG). Filtri e Esporta CSV (;, colonnamotivazione). - Log: livello, ricerca, «Segui», 2000 righe per pagina lette a incrementi (
/api/log?after=). - Impostazioni: configurazione a gruppi (eToro, esecuzione, notifiche, interfaccia/valuta, log) con validazione; chiavi eToro (verifica e salvataggio cifrato); ripristino in cinque passi e ripristino dei valori predefiniti; Ricerca (stato del modello in ombra, bandit, volatilità, criterio di riattivazione, «Esegui ciclo di apprendimento»); Diagnostica (strategia e run, percorsi, .NET, sistema, uptime, quote API, fuso, bonifica); Informazioni (nome, autore, versione, data di build, commit, licenza, documentazione).
- Login: solo con
ENCELADO_WEB_TOKEN; cookie HttpOnly per 30 giorni.
Flusso dei dati
/api/stream (SSE, event: snapshot, uno al secondo) → apply(snapshot) → render(); se il browser non ha EventSource, polling di /api/snapshot ogni 2 s; ?once=1 scatta una sola lettura (per gli screenshot). I comandi rispondono {ok, message, …} e finiscono nella snackbar; l'esito vero arriva con lo snapshot successivo. Il log legge /api/log ogni 1,5 s solo mentre la pagina Log è aperta.
Screenshot
docs/img/ (dashboard, storico, log, impostazioni, mobile), presi dal server in modalità campione con scripts\screenshots.ps1 (o sh scripts/screenshots.sh): avvia il server con --sample su una porta libera, apre ogni pagina con Edge headless (?once=1, 1440×900 e 420×860) e lo ferma. A mano, una pagina:
dotnet run --project src/Encelado.Server -- --sample --port 8085
& "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" --headless=new --disable-gpu --hide-scrollbars --window-size=1440,900 --virtual-time-budget=5000 --user-data-dir=$env:TEMP\edge-shot --screenshot=docs\img\dashboard.png "http://127.0.0.1:8085/?once=1#/dashboard"
Prima di dichiarare finita una modifica: EmbeddedUiTests e WebHostTests verdi, screenshot rifatti se cambia una pagina, revisione visiva con l'utente.