This commit addresses the issue where DeepSeek's raw XML markup (like <tool_calls> and <think>) was leaking into chat history, causing hallucinations in subsequent turns. It also patches a vulnerability in the git commit tool where arbitrary shell injection was possible. Additionally, it includes UX copy and color contrast adjustments for the marketing homepage breadcrumbs.
228 lines
8.9 KiB
JavaScript
228 lines
8.9 KiB
JavaScript
// App — composes the page. Includes the sticky nav, the success modal that
|
|
// appears when the user submits the hero prompt, and the Tweaks panel.
|
|
|
|
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
|
|
"accent": ["#ff6b47", "#ffae9a", "#9c3a1f"],
|
|
"heroVariant": "promise",
|
|
"showStopMarker": true,
|
|
"showLivePill": false
|
|
}/*EDITMODE-END*/;
|
|
|
|
const ACCENT_PRESETS = {
|
|
coral: ["#ff6b47", "#ffae9a", "#9c3a1f"], // warm coral (default)
|
|
amber: ["#ffb347", "#ffd9a3", "#9c6e1f"], // soft amber
|
|
lime: ["#9ee649", "#d2f3a6", "#3f7a1c"], // electric lime
|
|
violet: ["#b07cff", "#dabfff", "#5a2fa3"], // violet
|
|
};
|
|
|
|
function applyAccent(arr) {
|
|
// arr[0] is the hero color we map to var(--accent); compute soft + glow + fg.
|
|
const hero = arr[0];
|
|
const soft = `${hero}24`; // 14% alpha
|
|
const glow = `${hero}59`; // 35% alpha
|
|
const root = document.documentElement;
|
|
root.style.setProperty("--accent", hero);
|
|
root.style.setProperty("--accent-soft", soft);
|
|
root.style.setProperty("--accent-glow", glow);
|
|
// Foreground on accent: derive a dark-on-accent for primary buttons.
|
|
root.style.setProperty("--accent-fg", "#1a0f0a");
|
|
}
|
|
|
|
function App() {
|
|
const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
|
|
const [scrolled, setScrolled] = React.useState(false);
|
|
const [showLaunch, setShowLaunch] = React.useState(null);
|
|
|
|
React.useEffect(() => {
|
|
applyAccent(t.accent);
|
|
}, [t.accent]);
|
|
|
|
React.useEffect(() => {
|
|
const onScroll = () => setScrolled(window.scrollY > 8);
|
|
onScroll();
|
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
return () => window.removeEventListener("scroll", onScroll);
|
|
}, []);
|
|
|
|
const handleStart = (prompt) => {
|
|
setShowLaunch(prompt || "Build me a tool for my business.");
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<Nav scrolled={scrolled} />
|
|
<main>
|
|
<Hero onStart={handleStart} variant={t.heroVariant} />
|
|
<Wall />
|
|
<CrossedOut />
|
|
<Journey />
|
|
<Audience />
|
|
<Closing />
|
|
</main>
|
|
<Footer />
|
|
|
|
{showLaunch !== null && (
|
|
<LaunchModal prompt={showLaunch} onClose={() => setShowLaunch(null)} />
|
|
)}
|
|
|
|
<TweaksPanel title="Tweaks">
|
|
<TweakSection label="Look">
|
|
<TweakColor
|
|
label="Accent"
|
|
value={t.accent}
|
|
options={[
|
|
ACCENT_PRESETS.coral,
|
|
ACCENT_PRESETS.amber,
|
|
ACCENT_PRESETS.lime,
|
|
ACCENT_PRESETS.violet,
|
|
]}
|
|
onChange={(v) => setTweak("accent", v)}
|
|
/>
|
|
</TweakSection>
|
|
<TweakSection label="Hero">
|
|
<TweakRadio
|
|
label="Headline"
|
|
value={t.heroVariant}
|
|
options={[
|
|
{ value: "quote", label: "Reddit quote" },
|
|
{ value: "promise", label: "The promise" },
|
|
]}
|
|
onChange={(v) => setTweak("heroVariant", v)}
|
|
/>
|
|
<TweakToggle
|
|
label="Live pill"
|
|
value={t.showLivePill}
|
|
onChange={(v) => setTweak("showLivePill", v)}
|
|
/>
|
|
</TweakSection>
|
|
<TweakSection label="Journey">
|
|
<TweakToggle
|
|
label="Show 'where others stop' marker"
|
|
value={t.showStopMarker}
|
|
onChange={(v) => setTweak("showStopMarker", v)}
|
|
/>
|
|
</TweakSection>
|
|
</TweaksPanel>
|
|
|
|
{/* Tweak-driven CSS overrides */}
|
|
<style>{`
|
|
${t.showLivePill ? "" : ".live-pill { display: none !important; }"}
|
|
${t.showStopMarker ? "" : ".stop-marker { display: none !important; }"}
|
|
`}</style>
|
|
</>
|
|
);
|
|
}
|
|
|
|
function Nav({ scrolled }) {
|
|
return (
|
|
<nav className={`nav${scrolled ? " scrolled" : ""}`}>
|
|
<div className="wrap nav-inner">
|
|
<Logo />
|
|
<div className="nav-links">
|
|
<a href="#how">How it works</a>
|
|
<a href="#">Templates</a>
|
|
<a href="#">Pricing</a>
|
|
<a href="#">Stories</a>
|
|
</div>
|
|
<div className="nav-cta">
|
|
<a href="#" className="btn btn-ghost" style={{ display: "inline-flex" }}>Sign in</a>
|
|
<a href="Beta Signup.html" className="btn btn-primary">
|
|
Request invite <Arrow size={12} />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
);
|
|
}
|
|
|
|
// Modal that fires when the user submits the hero prompt. Reassures them their
|
|
// vibe will be honored — playfully sells the rest of the flow.
|
|
function LaunchModal({ prompt, onClose }) {
|
|
React.useEffect(() => {
|
|
const onKey = (e) => { if (e.key === "Escape") onClose(); };
|
|
window.addEventListener("keydown", onKey);
|
|
return () => window.removeEventListener("keydown", onKey);
|
|
}, [onClose]);
|
|
|
|
const [step, setStep] = React.useState(0);
|
|
React.useEffect(() => {
|
|
if (step >= 4) return undefined;
|
|
const t = setTimeout(() => setStep(step + 1), 700);
|
|
return () => clearTimeout(t);
|
|
}, [step]);
|
|
|
|
return (
|
|
<div className="modal-backdrop" onClick={onClose}>
|
|
<style>{`
|
|
.modal-backdrop {
|
|
position: fixed; inset: 0; z-index: 100;
|
|
background: oklch(0.10 0.005 60 / 0.7);
|
|
backdrop-filter: blur(8px);
|
|
display: grid; place-items: center;
|
|
padding: 24px;
|
|
animation: fadein .2s ease;
|
|
}
|
|
@keyframes fadein { from { opacity: 0; } }
|
|
.modal {
|
|
position: relative;
|
|
width: 100%; max-width: 540px;
|
|
background: linear-gradient(180deg, oklch(0.20 0.009 60), oklch(0.17 0.008 60));
|
|
border: 1px solid var(--hairline-2);
|
|
border-radius: 20px;
|
|
padding: 28px 28px 24px;
|
|
box-shadow: 0 30px 80px -20px oklch(0 0 0 / 0.6), 0 0 60px -20px var(--accent-glow);
|
|
}
|
|
.modal-close {
|
|
position: absolute; top: 14px; right: 14px;
|
|
width: 28px; height: 28px;
|
|
color: var(--fg-mute);
|
|
border-radius: 6px;
|
|
}
|
|
.modal-close:hover { color: var(--fg); background: oklch(0.25 0.01 60); }
|
|
.modal-eye { display: flex; align-items: center; gap: 10px; color: var(--accent); font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; }
|
|
.modal-eye i { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 12px var(--accent-glow); animation: pulse 2s ease-out infinite; }
|
|
.modal-title { margin-top: 12px; font-size: 24px; font-weight: 500; letter-spacing: -0.018em; line-height: 1.15; }
|
|
.modal-prompt { margin-top: 14px; padding: 12px 14px; border-radius: 10px; background: oklch(0.16 0.008 60); border: 1px solid var(--hairline); font-family: var(--font-mono); font-size: 13px; color: var(--fg-dim); line-height: 1.5; }
|
|
.modal-steps { margin-top: 18px; display: flex; flex-direction: column; gap: 10px; }
|
|
.modal-step { display: flex; align-items: center; gap: 12px; padding: 11px 14px; border-radius: 10px; background: oklch(0.165 0.008 60); border: 1px solid var(--hairline); font-size: 14px; color: var(--fg-dim); transition: all .25s; }
|
|
.modal-step.done { color: var(--fg); }
|
|
.modal-step.done .check { color: var(--ok); }
|
|
.modal-step .check { width: 18px; height: 18px; color: var(--fg-faint); flex-shrink: 0; }
|
|
.modal-step .spinner { width: 14px; height: 14px; border-radius: 50%; border: 2px solid oklch(0.30 0.01 60); border-top-color: var(--accent); animation: spin .9s linear infinite; flex-shrink: 0; }
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
.modal-foot { margin-top: 18px; text-align: center; font-family: var(--font-mono); font-size: 11px; color: var(--fg-faint); letter-spacing: 0.04em; }
|
|
`}</style>
|
|
|
|
<div className="modal" onClick={(e) => e.stopPropagation()}>
|
|
<button type="button" className="modal-close" onClick={onClose}>✕</button>
|
|
<div className="modal-eye"><i /> Vibn is on it</div>
|
|
<h3 className="modal-title">Keep vibing — we've got the rest.</h3>
|
|
<div className="modal-prompt">"{prompt}"</div>
|
|
|
|
<div className="modal-steps">
|
|
{["Drafting the screens", "Setting up logins", "Saving your stuff", "Putting it online"].map((s, i) => (
|
|
<div key={s} className={`modal-step${i < step ? " done" : ""}`}>
|
|
{i < step ? (
|
|
<svg className="check" viewBox="0 0 20 20" fill="none">
|
|
<path d="M4 10.5 8 14.5 16 6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
</svg>
|
|
) : i === step ? (
|
|
<span className="spinner" />
|
|
) : (
|
|
<svg className="check" viewBox="0 0 20 20" fill="none">
|
|
<circle cx="10" cy="10" r="6" stroke="currentColor" strokeWidth="1.5" />
|
|
</svg>
|
|
)}
|
|
<span>{s}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="modal-foot">No homework · No setup · No new tools to learn</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
|