From 83bdca57f468f799fced20b55d98219197fa975c Mon Sep 17 00:00:00 2001
From: Sikkra <159844544+Sikkra@users.noreply.github.com>
Date: Tue, 19 May 2026 11:23:39 -0500
Subject: [PATCH] Add live HF leverage preview
---
frontend/index.html | 5 +++++
frontend/src/main.ts | 20 +++++++++++++++++++
frontend/src/style.css | 45 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 70 insertions(+)
diff --git a/frontend/index.html b/frontend/index.html
index f904f23..4505a37 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -404,6 +404,11 @@
Open Position
Degen
Maxi Degen
+
+ Projected HF
+ —
+ Move leverage to preview risk
+
diff --git a/frontend/src/main.ts b/frontend/src/main.ts
index fcc1ceb..3069fc5 100644
--- a/frontend/src/main.ts
+++ b/frontend/src/main.ts
@@ -1167,6 +1167,25 @@ function switchAdjustSubTab(sub: "leverage" | "add-funds") {
// ── Leverage preview ──────────────────────────────────────────────────────────
+function renderLiveHfPreview(hf: number) {
+ const preview = $("hf-live-preview");
+ const value = $("hf-live-value");
+ const status = $("hf-live-status");
+ const min = minHF();
+ const finite = Number.isFinite(hf);
+ const display = finite ? fmt(hf, expertMode ? 5 : 3) : hf > 0 ? "\u221E" : "--";
+ const state = !finite ? (hf > 0 ? "ok" : "bad") : hf >= 1.1 ? "ok" : hf >= min ? "warn" : "bad";
+
+ value.textContent = display;
+ value.className = `hf-live-value ${state === "ok" ? "hf-ok" : state === "warn" ? "hf-warn" : "hf-bad"}`;
+ status.textContent = state === "ok"
+ ? `Safe above ${fmt(min, expertMode ? 5 : 3)} min`
+ : state === "warn"
+ ? `Near ${fmt(min, expertMode ? 5 : 3)} minimum`
+ : `Below ${fmt(min, expertMode ? 5 : 3)} minimum`;
+ preview.className = `hf-live-preview hf-live-${state}`;
+}
+
function updatePreview() {
const slider = $("leverage-slider") as HTMLInputElement;
const numIn = $("leverage-input") as HTMLInputElement;
@@ -1178,6 +1197,7 @@ function updatePreview() {
const l = rs?.lFactor ?? 1;
const hf = hfForLeverage(lev, c, l);
const pos = positions.byAsset.get(selectedAsset.id);
+ renderLiveHfPreview(hf);
// In adjust mode, use equity as the base; in add-funds mode, use the add-funds input; in open mode, use initial deposit
const equity = (actionMode === "adjust" && pos) ? pos.equity
diff --git a/frontend/src/style.css b/frontend/src/style.css
index 0d4348f..41d6e20 100644
--- a/frontend/src/style.css
+++ b/frontend/src/style.css
@@ -621,6 +621,49 @@ main { flex: 1; max-width: 1200px; width: 100%; margin: 0 auto; padding: 20px 24
.slider-zone.active { opacity: 1; font-weight: 800; }
.slider-zone:not(.active) { opacity: .4; }
+.hf-live-preview {
+ min-height: 38px;
+ display: grid;
+ grid-template-columns: auto auto 1fr;
+ align-items: center;
+ gap: 10px;
+ margin: 0 0 10px;
+ padding: 8px 10px;
+ background: var(--metric-bg);
+ border: 1px solid var(--border);
+ border-radius: var(--r-xs);
+}
+.hf-live-label {
+ color: var(--text-2);
+ font-size: 11px;
+ font-weight: 700;
+ letter-spacing: .4px;
+ text-transform: uppercase;
+}
+.hf-live-value {
+ font-family: var(--mono);
+ font-size: 16px;
+ line-height: 1;
+}
+.hf-live-status {
+ min-width: 0;
+ color: var(--text-3);
+ font-size: 12px;
+ text-align: right;
+}
+.hf-live-preview.hf-live-ok {
+ background: var(--primary-glow);
+ border-color: var(--success-border);
+}
+.hf-live-preview.hf-live-warn {
+ background: var(--warning-bg);
+ border-color: var(--warning-border);
+}
+.hf-live-preview.hf-live-bad {
+ background: var(--danger-bg);
+ border-color: var(--danger-border);
+}
+
/* APY chart */
.apy-chart { margin: 4px 0 10px; overflow: visible; }
.apy-chart svg { width: 100%; height: 120px; display: block; overflow: visible; }
@@ -1192,6 +1235,8 @@ main { flex: 1; max-width: 1200px; width: 100%; margin: 0 auto; padding: 20px 24
.swap-asset-select { width: 100%; }
.slider-row { flex-wrap: wrap; }
.leverage-num-input { width: 100%; margin-top: 4px; }
+ .hf-live-preview { grid-template-columns: 1fr auto; }
+ .hf-live-status { grid-column: 1 / -1; text-align: left; }
#wallet-area { flex-wrap: wrap; gap: 6px; }
.btn-connect { font-size: 12px; padding: 5px 10px; }
.slippage-opt { padding: 2px 6px; font-size: 11px; }