// dash-tabs.jsx — all dashboard tab screens (each fully functional) const { useState: useT, useEffect: useTE, useRef: useTR } = React; /* ---------- small building blocks ---------- */ function Card({ children, strong, style, className = "", ...rest }) { return
{children}
; } function StatTile({ label, value, icon, accent }) { return (
{label}
{value}
); } function Badge({ running, idleText = "Idle", liveText = "Live" }) { return running ? {liveText} : {idleText}; } function FieldLabel({ children }) { return {children}; } // Bare +/- glyphs for nudging a slider value by exactly 1. Styled INLINE on purpose: dashboard.php // links /ui/styles.css without cache-busting while the JS bundle is content-hashed, so a class-based // style can arrive after its markup and render as an unstyled browser button. Inline styles ship // with the bundle. Kept deliberately plain - no box, border or fill - because these rows are flat. const stepBtn = (disabled) => ({ width: 20, height: 20, padding: 0, margin: 0, lineHeight: 1, border: "none", background: "none", outline: "none", fontFamily: "var(--font)", fontSize: 16, fontWeight: 500, color: "var(--ink-3)", opacity: disabled ? 0.3 : 1, cursor: disabled ? "default" : "pointer", display: "inline-flex", alignItems: "center", justifyContent: "center", transition: "color .15s ease, opacity .15s ease", WebkitAppearance: "none", appearance: "none", }); function SliderRow({ label, value, min, max, step = 1, onChange, fmt = v => v.toLocaleString() }) { // The slider jumps in `step` increments (10 / 50 / 100 ...), which is quick but coarse. These // nudge by exactly 1 so an exact figure is reachable, clamped to the slider's own bounds. // type="button" is required - one of these rows sits inside a native
// (the auto-start slot in dash-wired.jsx), where a default-type button submits the form. const lo = Number(min), hi = Number(max), cur = Number(value) || 0; const atMin = !isNaN(lo) && cur <= lo; const atMax = !isNaN(hi) && cur >= hi; // Click-to-type. `draft` holds the raw digits while editing; the formatted string is only for // display, so entering edit mode swaps "1,250" for "1250" and back again on commit. const [editing, setEditing] = useT(false); const [draft, setDraft] = useT(""); const inputRef = useTR(null); const clamp = (n) => { if (!isNaN(lo)) n = Math.max(lo, n); if (!isNaN(hi)) n = Math.min(hi, n); return n; }; const beginEdit = () => { setDraft(String(cur)); setEditing(true); }; const commit = () => { const parsed = parseInt(String(draft).replace(/[^0-9-]/g, ""), 10); setEditing(false); if (isNaN(parsed)) return; // junk or empty -> keep the current value const next = clamp(parsed); if (next !== cur && onChange) onChange(next); }; const onKey = (e) => { if (e.key === "Enter") { // Never let this bubble to a surrounding - it would submit and reload the page. e.preventDefault(); e.stopPropagation(); commit(); } else if (e.key === "Escape") { e.preventDefault(); setEditing(false); } }; const nudge = (delta) => { let next = cur + delta; if (!isNaN(lo)) next = Math.max(lo, next); if (!isNaN(hi)) next = Math.min(hi, next); if (next !== cur && onChange) onChange(next); }; const hover = (on, disabled) => (e) => { if (disabled) return; e.currentTarget.style.color = on ? "var(--ink)" : "var(--ink-3)"; }; return (
{label} {editing ? ( setDraft(e.target.value)} onKeyDown={onKey} onBlur={commit} onFocus={e => e.target.select()} aria-label={"Type a value for " + label} style={{ width: Math.max(2, String(draft).length + 1) + "ch", background: "none", border: "none", outline: "none", padding: 0, margin: 0, textAlign: "right", fontFamily: "var(--font)", fontSize: "inherit", fontWeight: 640, color: "var(--accent)", fontVariantNumeric: "tabular-nums", }} /> ) : ( {fmt(value)} )}
onChange(+e.target.value)} style={{ width: "100%" }} />
); } const grid = (cols, gap = 14) => ({ display: "grid", gridTemplateColumns: `repeat(${cols},1fr)`, gap }); /* reusable min/max delay control (clamped so max >= min) used across every bot */ function DelayFields({ min, max, setMin, setMax, lo = 1, hi = 60, step = 1, unit = "s", label = "RESPONSE DELAY", loMin, hiMin, loMax, hiMax }) { // MIN and MAX may take asymmetric bounds (matches the original dashboard — e.g. the viewbot // MIN delay capped at 450 while MAX capped at 900). They fall back to the shared lo/hi. const lMin = loMin != null ? loMin : lo, hMin = hiMin != null ? hiMin : hi; const lMax = loMax != null ? loMax : lo, hMax = hiMax != null ? hiMax : hi; const onMin = (v) => { setMin(v); if (v > max) setMax(v); }; const onMax = (v) => { setMax(v); if (v < min) setMin(v); }; return (
{label}
v + unit} /> v + unit} />
); } function ToggleRow_unused() { return null; } /* ================= AUTO-START SLOTS (paid) ================= */ function AutoStartSlots() { const [enabled, setEnabled] = useT(true); const limit = 5; // Pro plan const [slots, setSlots] = useT([ { id: "s1", channel: "kick.com/yourchannel", viewers: 1500, armed: true }, { id: "s2", channel: "kick.com/secondary", viewers: 800, armed: true }, { id: "s3", channel: "kick.com/altstream", viewers: 400, armed: false }, ]); const add = () => { if (slots.length >= limit) return; setSlots(s => [...s, { id: Math.random().toString(36).slice(2), channel: "kick.com/", viewers: 500, armed: false }]); }; const update = (id, patch) => setSlots(s => s.map(x => x.id === id ? { ...x, ...patch } : x)); const remove = (id) => setSlots(s => s.filter(x => x.id !== id)); const armedCount = slots.filter(s => s.armed).length; return ( {enabled ? `${armedCount} armed` : "Paused"} } />

The moment KVB detects your channel goes live, every armed slot launches its viewers automatically — no manual start. Included on paid Viewbot plans.

{slots.map((s, i) => (
update(s.id, { channel: v })} />
VIEWERS {s.viewers.toLocaleString()}
update(s.id, { viewers: +e.target.value })} style={{ width: "100%" }} />
))}
{slots.length} / {limit} slots used · Pro plan
); } /* ================= OVERVIEW ================= */ const ACTIVITY = [ { icon: "zap", t: "Auto-start armed for kick.com/yourchannel", time: "2m" }, { icon: "signal", t: "Viewer session hit a 1,312 peak", time: "14m" }, { icon: "poll", t: "Poll \u201cWho takes the 1v1?\u201d launched", time: "31m" }, { icon: "users", t: "+318 followers gained today", time: "1h" }, { icon: "spark", t: "AI Chatbot replied to 420 messages", time: "2h" }, { icon: "scissors", t: "Clip boost finished \u2014 +2,500 views", time: "3h" }, ]; const SERVICES = [ { name: "Viewer Bot", icon: "signal", val: "1,240 live", on: true, go: "Viewer Bot" }, { name: "Ai Chatbot", icon: "spark", val: "180 / min", on: true, go: "Ai Chatbot" }, { name: "Follow Bot", icon: "users", val: "+42 / min", on: true, go: "Follow Bot" }, { name: "Poll Bot", icon: "poll", val: "Voting", on: true, go: "Poll Bot" }, { name: "Clip Viewbot", icon: "scissors", val: "Idle", on: false, go: "Clip Viewbot" }, ]; function OverviewScreen({ go }) { const [live, setLive] = useT(1240); return (
{/* chart + active services */}
Viewer bot · active
{live.toLocaleString()} concurrent · kick.com/yourchannel
{[["Session peak", "1,312"], ["Delivery time", "28s"], ["Avg. watch", "2h 14m"]].map(([l, v], i) => (
{v}
{l}
))}
4 running} />
{SERVICES.map(s => (
go(s.go)} className="tile" style={{ display: "flex", alignItems: "center", gap: 12, padding: "11px 12px", borderRadius: 12, cursor: "pointer", border: "1px solid var(--glass-border-soft)", background: "color-mix(in oklch, white 3%, transparent)" }}>
{s.name}
{s.val}
{s.on ? On : Off}
))}
{/* recent activity + quick launch */}
Last 3 hours} />
{ACTIVITY.map((a, i) => (
{a.t} {a.time}
))}
{[["Viewer Bot", "signal"], ["Chat Bot", "chat"], ["Poll Bot", "poll"], ["Clip Viewbot", "scissors"]].map(([t, ic]) => (
go(t)} className="tile" style={{ padding: 14, borderRadius: 12, cursor: "pointer", border: "1px solid var(--glass-border-soft)", background: "color-mix(in oklch, white 3%, transparent)" }}>
{t}
))}
Pro plan credits 1,420 / 1,800
); } /* ================= VIEWER BOT ================= */ const C_CONNECTING = "oklch(0.80 0.135 215)"; const C_ERRORS = "oklch(0.83 0.155 82)"; const C_DEAD = "oklch(0.66 0.20 22)"; const CONN_LEGEND = [ ["connecting", "Connecting", C_CONNECTING], ["connected", "Connected", "var(--accent)"], ["errors", "Errors", C_ERRORS], ["dead", "Dead", C_DEAD], ]; function ConnStatTile({ label, value, dot, accent }) { return (
{label}
{value}
); } function ViewerBotScreen() { const [running, setRunning] = useT(true); const [url, setUrl] = useT("kick.com/yourchannel"); const [target, setTarget] = useT(1240); const [minDelay, setMinDelay] = useT(5); const [maxDelay, setMaxDelay] = useT(30); const [dynamic, setDynamic] = useT(true); const [stats, setStats] = useT({ connected: 1040, connecting: 150, errors: 52, dead: 90 }); const z = running ? stats : { connected: 0, connecting: 0, errors: 0, dead: 0 }; return (
STREAM URL
{[["Session peak", "1,312"], ["Delivery time", "28s"], ["Avg. watch", "2h 14m"]].map(([l, v], i) => (
{v}
{l}
))}
CONNECTIONS — LIVE
{z.connected.toLocaleString()}
{CONN_LEGEND.map(([k, lbl, col]) => (
{lbl} {z[k].toLocaleString()}
))}
{[ ["Fill rate", running ? Math.round(z.connected / target * 100) + "%" : "0%"], ["Success rate", running ? Math.round(z.connected / Math.max(1, z.connected + z.errors + z.dead) * 100) + "%" : "—"], ["Mode", dynamic ? "Dynamic" : "Steady"], ].map(([l, v], i) => (
{v}
{l}
))}
); } /* ================= CHAT BOT ================= */ function ChatBotScreen() { const [running, setRunning] = useT(true); const [channel, setChannel] = useT("kick.com/yourchannel"); const [rate, setRate] = useT(180); const [tone, setTone] = useT("Hype"); const [emotes, setEmotes] = useT(true); const [minDelay, setMinDelay] = useT(2); const [maxDelay, setMaxDelay] = useT(12); return (
CHANNEL
PERSONALITY
Use emotes & reactions
LIVE CHAT FEED
); } /* ================= FOLLOW BOT ================= */ function FollowBotScreen() { const [running, setRunning] = useT(true); const [channel, setChannel] = useT("kick.com/yourchannel"); const [rate, setRate] = useT(42); const [drip, setDrip] = useT(true); const [minDelay, setMinDelay] = useT(3); const [maxDelay, setMaxDelay] = useT(20); const [total, setTotal] = useT(12840); return (
CHANNEL
Natural drip pacing

Spreads follows organically so growth never looks artificial.

FOLLOWERS — CUMULATIVE
{total.toLocaleString()}
{["18m ago", "12m", "6m", "now"].map(t => {t})}
); } /* ================= AI CHATBOT ================= */ const AI_QA = [ { q: "yo is this guy actually cracked?", a: "He's been grinding ranked all week — the aim's no accident 👀 stick around for the clutch." }, { q: "what sens does he use", a: "Running 800 DPI · 0.45 in-game. Full settings are pinned in the panel below!" }, { q: "how long is stream today", a: "We're live for ~4 hours today — big tournament warmups, then customs at 9pm 🔥" }, { q: "first time here, what do we play", a: "Welcome in! Mostly FPS ranked + viewer games on weekends. Glad you made it 🙌" }, { q: "drop a follow?", a: "Appreciate the love! Hit follow so you don't miss the next insane clip 💚" }, ]; function AiChatbotScreen() { const [running, setRunning] = useT(true); const [channel, setChannel] = useT("kick.com/yourchannel"); const [creativity, setCreativity] = useT(70); const [model, setModel] = useT("Balanced"); const [mentions, setMentions] = useT(true); const [minDelay, setMinDelay] = useT(1); const [maxDelay, setMaxDelay] = useT(6); const feedRef = useTR(null); const [items, setItems] = useT(() => AI_QA.slice(0, 2).map((x, i) => ({ ...x, id: i }))); const idc = useTR(100); useTE(() => { if (!running) return; const id = setInterval(() => { const qa = AI_QA[(Math.random() * AI_QA.length) | 0]; setItems(s => [...s.slice(-5), { ...qa, id: idc.current++ }]); }, 2800); return () => clearInterval(id); }, [running]); useTE(() => { const el = feedRef.current; if (el) el.scrollTop = el.scrollHeight; }, [items]); return (
CHANNEL
MODEL
v + "%"} />
Reply to @mentions
AI CONVERSATION
{items.map(it => (
viewer {it.q}
KVB AI {it.a}
))}
); } /* ================= POLL BOT ================= */ function PollVizBars({ running, options }) { const [vals, setVals] = useT(() => options.map(o => o.base)); useTE(() => { if (!running) return; const id = setInterval(() => { setVals(vs => vs.map((v, i) => Math.max(3, v + (options[i].lean + (Math.random() - 0.5) * 6)))); }, 650); return () => clearInterval(id); }, [running]); const sum = vals.reduce((a, b) => a + b, 0) || 1; const lead = vals.indexOf(Math.max(...vals)); return (
{options.map((o, i) => { const pct = Math.round(vals[i] / sum * 100); const on = i === lead; return (
{o.label} {pct}%
); })}
); } function PollbotScreen() { const [running, setRunning] = useT(true); const [channel, setChannel] = useT("kick.com/yourchannel"); const [question, setQuestion] = useT("Who takes the 1v1?"); const [rate, setRate] = useT(120); const [lean, setLean] = useT("Option A"); const [minDelay, setMinDelay] = useT(1); const [maxDelay, setMaxDelay] = useT(8); const options = [ { label: "Streamer wins", base: 58, lean: lean === "Option A" ? 2.4 : 0.4 }, { label: "Chat's champion", base: 30, lean: lean === "Option B" ? 2.4 : 0.4 }, { label: "It's a draw", base: 12, lean: 0.2 }, ]; return (
CHANNEL
QUESTION
BOOST OPTION
LIVE POLL
{question}
{running ? "Votes streaming in from distributed accounts" : "Poll paused"}
); } /* ================= CLIP VIEWBOT ================= */ const CLIP_TITLES = ["Insane 1v5 clutch", "He did NOT just hit that", "Chat went crazy 😳", "Fastest ace this season", "Reverse sweep highlight"]; function ClipViewbotScreen() { const [running, setRunning] = useT(true); const [clipUrl, setClipUrl] = useT("kick.com/yourchannel?clip=8f3a2"); const [target, setTarget] = useT(2500); const [drip, setDrip] = useT(true); const [minDelay, setMinDelay] = useT(2); const [maxDelay, setMaxDelay] = useT(15); const [views, setViews] = useT(0); const clipsRef = useTR(null); if (!clipsRef.current) clipsRef.current = CLIP_TITLES.slice(0, 4).map((t, i) => ({ id: i, t, v: 1200 + i * 640, done: i === 3 })); const [clips, setClips] = useT(clipsRef.current); useTE(() => { if (!running) return; const id = setInterval(() => { setClips(cs => cs.map(c => c.done ? c : { ...c, v: c.v + ((Math.random() * 40) | 0) })); }, 500); return () => clearInterval(id); }, [running]); return (
CLIP URL
Natural drip pacing

Spreads views gradually so clips climb the algorithm naturally.

VIEWS DELIVERED / MIN
{running ? views.toLocaleString() : "0"}
{clips.map(c => (
{c.t} {c.v.toLocaleString()} {c.done ? Done : Live}
))}
); } /* ================= bottom panels (fill out each bot page) ================= */ function ToggleRow({ label, sub, on, onChange, last }) { return (
{label}
{sub &&
{sub}
}
); } function Chip({ children }) { return {children} ; } function ChatExtras() { const [packs, setPacks] = useT({ hype: true, emotes: true, questions: true, raid: false, compliments: true }); const tog = k => setPacks(p => ({ ...p, [k]: !p[k] })); const triggers = [["!discord", "Join the fam → discord.gg/kvb 💚"], ["!sens", "800 DPI · 0.45 in-game"], ["raid", "Welcome raiders! Make yourselves at home 🔥"], ["!socials", "All links pinned below the stream!"]]; return ( 4 active} />
ACTIVE PACKS
tog("hype")} /> tog("emotes")} /> tog("questions")} /> tog("raid")} /> tog("compliments")} last />
AUTO-REPLY TRIGGERS
{triggers.map(([k, v]) => (
{k} {v}
))}
); } function AiExtras() { const [beh, setBeh] = useT({ safe: true, mod: true, qonly: false, ctx: true }); const tog = k => setBeh(b => ({ ...b, [k]: !b[k] })); const facts = ["Streams FPS ranked + viewer games on weekends", "Sensitivity: 800 DPI · 0.45 in-game", "Live Mon–Fri from 6pm ET", "Community: discord.gg/kvb"]; return ( Smart model} />
KNOWLEDGE BASE
{facts.map((f, i) => (
{f}
))}
{}} placeholder="Add a fact or link the AI should know…" mono={false} />
BEHAVIOR
tog("safe")} /> tog("mod")} /> tog("qonly")} /> tog("ctx")} last />
BANNED WORDS
{["scam", "free vbucks", "dm me", "giveaway"].map(w => {w})}
); } function PollExtras() { const templates = ["Who takes the 1v1?", "What game next?", "Rate that play /10", "Should I run it back?", "Pick my loadout"]; const history = [["Best moment today?", "4,182", "Clip #3", "12m"], ["Face cam on?", "2,933", "Yes", "48m"], ["Next ranked or chill?", "5,401", "Ranked", "2h"]]; return (
SAVED TEMPLATES
{templates.map(t => (
{t}
))}
RECENT POLLS
{history.map((r, i) => ( ))}
QuestionVotesWinnerWhen
{r[0]}{r[1]}{r[2]}{r[3]}
); } function FollowExtras() { const [days, setDays] = useT({ M: true, T: true, W: true, Th: true, F: true, Sa: false, Su: false }); const milestones = [["10,000 followers", 1, "Reached"], ["20,000 followers", 12840 / 20000, "12,840 / 20,000"], ["50,000 followers", 12840 / 50000, "12,840 / 50,000"]]; return (
WEEKLY SCHEDULE
{Object.keys(days).map(d => ( ))}

Drips ≈ 300 follows/day on active days, paced naturally across each session.

MILESTONES
{milestones.map(([label, frac, note]) => (
{label} = 1 ? "var(--accent)" : "var(--ink-3)" }}>{note}
))}
); } function ClipExtras() { const queue = [["Insane 1v5 clutch", "2,500", "2,500", "done"], ["He did NOT just hit that", "5,000", "3,180", "live"], ["Chat went crazy 😳", "2,000", "1,440", "live"], ["Fastest ace this season", "8,000", "0", "queued"], ["Reverse sweep highlight", "3,000", "0", "queued"]]; return (
Clip queue
{queue.map((r, i) => ( ))}
ClipTargetViews deliveredStatus
{r[0]} {r[1]} {r[2]} {r[3] === "done" ? Done : r[3] === "live" ? Boosting : Queued}
); } /* ================= BILLING ================= */ const CURRENT_PLAN = { name: "Standard Plan", product: "views" }; function UpgradePlanCard({ plan, isCurrent }) { const [h, setH] = useT(false); return (
setH(true)} onMouseLeave={() => setH(false)} style={{ padding: 18, paddingTop: 24, borderRadius: "var(--radius)", display: "flex", flexDirection: "column", position: "relative", borderColor: isCurrent ? "color-mix(in oklch, var(--accent) 60%, transparent)" : plan.highlight ? "color-mix(in oklch, var(--accent) 45%, transparent)" : (h ? "var(--glass-hi)" : "var(--glass-border)"), transform: h ? "translateY(-4px)" : "none", transition: "transform .3s, border-color .3s", boxShadow: plan.highlight ? "var(--shadow-2), 0 0 44px -20px var(--accent-glow)" : undefined, }}> {plan.highlight && !isCurrent && (
Most popular
)} {isCurrent && (
Your plan
)}
{plan.name}
{plan.price} / {plan.per}
{plan.feats.map(f => (
{f}
))}
); } function UpgradeSection() { const [product, setProduct] = useT("views"); const plans = product === "views" ? (window.KICK_VIEWS || []) : (window.KICK_AIO || []); return (
Upgrade your plan

Choose the plan that fits your stream

Same pricing as our site · cancel anytime · upgrade or downgrade whenever you want.

{[["Kick Views", "views"], ["All-in-One", "aio"]].map(([l, v]) => ( ))}
{plans.map(p => ( ))}

We accept Stripe & Crypto. Holiday discounts drop in our support chat.

); } const CREDIT_TYPES = [ { key: "views", label: "Live Views", sub: "Concurrent stream viewers", cost: 0.10, step: 100, icon: "signal" }, { key: "followers", label: "Followers", sub: "Channel followers", cost: 0.015, step: 100, icon: "users" }, { key: "chatters", label: "Chatters", sub: "Chat bot messages", cost: 0.10, step: 50, icon: "chat" }, { key: "ai", label: "AI Chatbot", sub: "AI-generated replies", cost: 0.005, step: 500, icon: "spark" }, { key: "polls", label: "Polls", sub: "Poll bot votes", cost: 0.10, step: 10, icon: "poll" }, { key: "clips", label: "Clip Views", sub: "Clip view boosts", cost: 0.030, step: 100, icon: "scissors" }, { key: "slots", label: "Auto-Start Slot", sub: "Extra automation slot", cost: 10.00, step: 1, icon: "zap" }, ]; const QUICK_ADD = [ { label: "Starter pack", qty: { views: 500, ai: 2000, followers: 200 } }, { label: "Growth pack", qty: { views: 1500, chatters: 500, clips: 500, ai: 5000 } }, { label: "Pro pack", qty: { views: 5000, followers: 1000, clips: 1000, polls: 200, ai: 10000 } }, ]; function CreditStepper({ value, step, onChange }) { const btn = { width: 30, height: 30, borderRadius: 8, flexShrink: 0, display: "grid", placeItems: "center", background: "color-mix(in oklch, white 6%, transparent)", border: "1px solid var(--glass-border)", color: "var(--ink)", cursor: "pointer", fontSize: 16, fontWeight: 600, lineHeight: 0, }; return (
onChange(Math.max(0, parseInt(e.target.value.replace(/\D/g, "")) || 0))} style={{ width: 72, textAlign: "center", background: "color-mix(in oklch, black 26%, transparent)", border: "1px solid var(--glass-border)", borderRadius: 9, color: "var(--ink)", fontSize: 14, fontWeight: 600, padding: "7px 4px", fontFamily: "var(--font)", fontVariantNumeric: "tabular-nums" }} />
); } function PurchaseCredits() { const [qty, setQty] = useT(() => Object.fromEntries(CREDIT_TYPES.map(c => [c.key, 0]))); const set = (key, v) => setQty(q => ({ ...q, [key]: v })); const lineTotal = (c) => qty[c.key] * c.cost; const total = CREDIT_TYPES.reduce((s, c) => s + lineTotal(c), 0); const items = CREDIT_TYPES.filter(c => qty[c.key] > 0).length; const applyPack = (pack) => setQty(q => { const next = Object.fromEntries(CREDIT_TYPES.map(c => [c.key, 0])); return { ...next, ...pack }; }); const fmt = (n) => "$" + n.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }); return (
Top up

Purchase credits

Pay only for what you need — à la carte, no subscription. Credits never expire.

{QUICK_ADD.map(p => ( ))}
{CREDIT_TYPES.map(c => { const active = qty[c.key] > 0; return (
{c.label}
{c.sub}
${c.cost.toFixed(3)}
per {c.key === "slots" ? "slot" : "credit"}
set(c.key, v)} /> {fmt(lineTotal(c))}
); })}
Total{items ? ` · ${items} ${items === 1 ? "item" : "items"}` : ""}
0 ? "var(--accent)" : "var(--ink-3)", fontVariantNumeric: "tabular-nums" }}>{fmt(total)}
{total > 0 && }
); } function BillingScreen() { const pays = ["Stripe", "Square", "Zelle", "Cash App", "Venmo", "Crypto"]; const invoices = [["Jun 1, 2026", "Standard — Monthly", "$50.00", "Paid"], ["May 1, 2026", "Standard — Monthly", "$50.00", "Paid"], ["Apr 1, 2026", "Standard — Monthly", "$50.00", "Paid"], ["Mar 1, 2026", "Starter — Monthly", "$10.00", "Paid"]]; return (
Active} />
$50/ month · renews Jul 1
{[["AI Chatbot", 1980, 3750], ["Followers", 318, 500], ["Clip Views", 640, 1000], ["Auto-Start Slots", 2, 5]].map(([l, a, b]) => (
{l}{a.toLocaleString()} / {b.toLocaleString()}
))}
{pays.map(p => {p})}
Visa ···· 4242
Expires 09 / 28
Default

Holiday discounts drop in our Discord around Christmas & Halloween.

Invoices
{invoices.map((iv, i) => ( ))}
DatePlanAmountStatus
{iv[0]}{iv[1]}{iv[2]} {iv[3]}
); } /* ================= SETTINGS ================= */ function SettingsScreen() { const [name, setName] = useT("yourname"); const [email, setEmail] = useT("you@email.com"); const [twofa, setTwofa] = useT(true); const [notif, setNotif] = useT({ updates: true, security: true, marketing: false }); return (
USERNAME
EMAIL

Enabled
{[["updates", "Product updates"], ["security", "Security alerts"], ["marketing", "Promotions & discounts"]].map(([k, l], i) => ( {i > 0 &&
} setNotif(n => ({ ...n, [k]: v }))} />
))}
); } function Row({ label, sub, children }) { return (
{label}
{sub &&
{sub}
}
{children}
); } const SCREENS = { "Overview": OverviewScreen, "Viewer Bot": ViewerBotScreen, "Chat Bot": ChatBotScreen, "Ai Chatbot": AiChatbotScreen, "Poll Bot": PollbotScreen, "Follow Bot": FollowBotScreen, "Clip Viewbot": ClipViewbotScreen, "Billing": BillingScreen, "Settings": SettingsScreen, }; Object.assign(window, { SCREENS });