refactor(settings): redesign BotConfigPanel + fix paper mode + clarify all copy

BotConfigPanel (full rewrite, 1113→858 lines):
- Remove 3-tab layout; replace with two expandable cards (Trump Signal /
  Macro Vibes) + collapsed Advanced block — fewer decisions, less noise
- Fix paper mode bug: UserResponse now returns paper_mode; BotConfigPanel
  tracks it in state; missingItems skips HL key requirement for paper users;
  HL key card shows paper status instead of empty input
- Add "Switch to live →" upgrade path — calls /subscribe with paper_mode=false
  so paper users can promote without re-subscribing from scratch
- Add confirmSign before manual override window (consistent with all other
  mutations); rename "Arm/Disarm" → "Override/Cancel override" throughout
- Fix all ambiguous copy: subscribe card, Macro Vibes description (manual
  /adopt flow, not auto-manage), missingItems strings, TP/SL "Not saved yet"
  → "Required — not saved", "Daily spend cap" → "Daily trading cap",
  "Active schedule" → "Trading schedule", "Get started" confirmSign labels

lib/api.ts:
- Add paper_mode: boolean to UserData interface (was missing)

OpenPositions:
- "Load your settings once…" → "Sign in once to view open positions" (removes
  implementation-detail explanation)
- "Today realised" → "Today's realized"; "banked" → "locked in" throughout
- "The bot will appear here" → "When the bot opens a trade, it will show up here"

SystemControl:
- "Net result" → "Current status"

settings/SettingsClient.tsx:
- id="btc-settings" → id="macro-settings" (stale rename)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
k
2026-05-27 23:10:55 +08:00
parent a1e9ec49ef
commit b76de36af0
5 changed files with 530 additions and 656 deletions
+5 -5
View File
@@ -76,16 +76,16 @@ export default function SettingsClient() {
<div className="settings-scope-copy"> <div className="settings-scope-copy">
Position size, Trump leverage, and minimum AI confidence used when a Truth Social post becomes actionable. Position size, Trump leverage, and minimum AI confidence used when a Truth Social post becomes actionable.
</div> </div>
<a href="#trump-settings-panel" className="settings-scope-link">Open panel </a> <a href="#bot-config" className="settings-scope-link">Trump Signal tab </a>
</section> </section>
<section id="btc-settings" className="settings-scope-card btc"> <section id="macro-settings" className="settings-scope-card btc">
<div className="settings-scope-eyebrow">Macro Vibes</div> <div className="settings-scope-eyebrow">Macro Vibes</div>
<div className="settings-scope-title">BTC manage-only settings</div> <div className="settings-scope-title">BTC manage-only settings</div>
<div className="settings-scope-copy"> <div className="settings-scope-copy">
Strategy mode, BTC leverage, and de-risk behavior used by the Macro Vibes sleeve. Strategy mode, BTC leverage, and de-risk behavior used by the Macro Vibes sleeve.
</div> </div>
<a href="#btc-settings" className="settings-scope-link">Open panel </a> <a href="#bot-config" className="settings-scope-link">Macro Vibes tab </a>
</section> </section>
<section id="global-settings" className="settings-scope-card global"> <section id="global-settings" className="settings-scope-card global">
@@ -94,7 +94,7 @@ export default function SettingsClient() {
<div className="settings-scope-copy"> <div className="settings-scope-copy">
Subscription, Hyperliquid API wallet, manual window, schedule, and guardrails that apply across the account. Subscription, Hyperliquid API wallet, manual window, schedule, and guardrails that apply across the account.
</div> </div>
<a href="#global-settings-panel" className="settings-scope-link">Open panel </a> <a href="#bot-config" className="settings-scope-link">Global tab </a>
</section> </section>
</div> </div>
@@ -126,7 +126,7 @@ export default function SettingsClient() {
</div> </div>
</div> </div>
<div className="settings-section-shell"> <div id="bot-config" className="settings-section-shell">
<div className="settings-section-head"> <div className="settings-section-head">
<div> <div>
<div className="settings-section-kicker">Execution setup</div> <div className="settings-section-kicker">Execution setup</div>
+7 -7
View File
@@ -141,7 +141,7 @@ function PositionRow({ p, onClose, onToggleGrow, growBusy, isZh }: {
</div> </div>
{p.realized_usd != null && p.realized_usd !== 0 && ( {p.realized_usd != null && p.realized_usd !== 0 && (
<div style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 1 }}> <div style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 1 }}>
banked {fmtMoney(p.realized_usd, { sign: true })} locked in {fmtMoney(p.realized_usd, { sign: true })}
</div> </div>
)} )}
</div> </div>
@@ -272,10 +272,10 @@ export default function OpenPositions() {
Open positions Open positions
</div> </div>
<div style={{ fontSize: 14, fontWeight: 600, color: 'var(--ink)' }}> <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--ink)' }}>
Load your settings once to unlock private position data. Sign in once to view open positions
</div> </div>
<div style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 4, lineHeight: 1.6 }}> <div style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 4, lineHeight: 1.6 }}>
This panel no longer triggers a wallet signature on its own while you browse. Open the Settings page and load once, then the cached permission will populate positions here. Go to the <strong>Settings page</strong> and click <em>Sign in to view your settings</em>. After signing, your open positions will appear here automatically.
</div> </div>
</div> </div>
) )
@@ -331,7 +331,7 @@ export default function OpenPositions() {
<div> <div>
<div style={{ fontSize: 10, color: 'var(--ink-3)', letterSpacing: '0.06em', <div style={{ fontSize: 10, color: 'var(--ink-3)', letterSpacing: '0.06em',
textTransform: 'uppercase', fontWeight: 600, marginBottom: 2 }}> textTransform: 'uppercase', fontWeight: 600, marginBottom: 2 }}>
Today realised Today&apos;s realized
</div> </div>
<div style={{ fontSize: 18, fontWeight: 700, color: todayColor, <div style={{ fontSize: 18, fontWeight: 700, color: todayColor,
fontVariantNumeric: 'tabular-nums' }}> fontVariantNumeric: 'tabular-nums' }}>
@@ -342,7 +342,7 @@ export default function OpenPositions() {
</div> </div>
{today != null && (today.open_realized_usd ?? 0) !== 0 && ( {today != null && (today.open_realized_usd ?? 0) !== 0 && (
<div style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 1 }}> <div style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 1 }}>
{`incl. ${fmtMoney(today.open_realized_usd, { sign: true })} banked on open`} {`incl. ${fmtMoney(today.open_realized_usd, { sign: true })} locked in on open trades`}
</div> </div>
)} )}
</div> </div>
@@ -387,7 +387,7 @@ export default function OpenPositions() {
padding: '20px 18px', padding: '20px 18px',
fontSize: 13, color: 'var(--ink-3)', textAlign: 'center', fontSize: 13, color: 'var(--ink-3)', textAlign: 'center',
}}> }}>
No open positions. The bot will appear here as soon as a signal triggers a trade. No open positions. When the bot opens a trade, it will show up here.
</div> </div>
)} )}
@@ -432,7 +432,7 @@ export default function OpenPositions() {
</div> </div>
{closing.realized_usd != null && closing.realized_usd !== 0 && ( {closing.realized_usd != null && closing.realized_usd !== 0 && (
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, marginBottom: 6, color: 'var(--ink-3)' }}> <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, marginBottom: 6, color: 'var(--ink-3)' }}>
<span>{isZh ? '已锁定(降风险)' : 'Already banked (de-risk)'}</span> <span>{isZh ? '已锁定(降风险)' : 'Locked in (de-risked)'}</span>
<span className="mono">{fmtMoney(closing.realized_usd, { sign: true })}</span> <span className="mono">{fmtMoney(closing.realized_usd, { sign: true })}</span>
</div> </div>
)} )}
+2 -2
View File
@@ -126,7 +126,7 @@ export default function SystemControl({ system }: { system: 'trump' | 'btc' }) {
// wagmi state that resolves client-side. // wagmi state that resolves client-side.
if (!mounted) return null if (!mounted) return null
const settingsHref = `/${locale}/settings#${system === 'trump' ? 'trump-settings-panel' : 'btc-settings-panel'}` const settingsHref = `/${locale}/settings#bot-config`
const settingsLabel = system === 'trump' ? 'Trump Signal' : 'Macro Vibes' const settingsLabel = system === 'trump' ? 'Trump Signal' : 'Macro Vibes'
// Wallet not connected: a previous version showed a giant full-width // Wallet not connected: a previous version showed a giant full-width
@@ -298,7 +298,7 @@ export default function SystemControl({ system }: { system: 'trump' | 'btc' }) {
}}> }}>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '.06em', <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '.06em',
textTransform: 'uppercase', color: 'var(--ink-3)', marginBottom: 4 }}> textTransform: 'uppercase', color: 'var(--ink-3)', marginBottom: 4 }}>
{isZh ? '⟹ 当前结果' : '⟹ Net result'} {isZh ? '⟹ 当前状态' : '⟹ Current status'}
</div> </div>
<div style={{ fontSize: 14, fontWeight: 700, <div style={{ fontSize: 14, fontWeight: 700,
color: netOk ? 'var(--up)' : 'var(--down)' }}> color: netOk ? 'var(--up)' : 'var(--down)' }}>
File diff suppressed because it is too large Load Diff
+5
View File
@@ -96,6 +96,10 @@ export interface UserSettings {
sys2_mode?: 'standard' | 'aggressive' | null sys2_mode?: 'standard' | 'aggressive' | null
active_from: string | null // ISO UTC active_from: string | null // ISO UTC
active_until: string | null // ISO UTC active_until: string | null // ISO UTC
/** Master on/off switches for each module. false = module is disabled;
* bot ignores all signals for that module even if subscribed. */
trump_enabled?: boolean | null
macro_enabled?: boolean | null
} }
export interface UserData { export interface UserData {
@@ -104,6 +108,7 @@ export interface UserData {
subscribed_at: string | null subscribed_at: string | null
hl_api_key_set: boolean hl_api_key_set: boolean
hl_api_key_masked: string | null hl_api_key_masked: string | null
paper_mode: boolean
trades: BotTrade[] trades: BotTrade[]
settings: UserSettings settings: UserSettings
/** ISO-UTC timestamp until which the bot is manually armed (null = use schedule). */ /** ISO-UTC timestamp until which the bot is manually armed (null = use schedule). */