From 096a6178c26cff66eb90d64b53d8de4087a1f5fa Mon Sep 17 00:00:00 2001 From: jayteemoney Date: Thu, 2 Jul 2026 07:31:48 +0100 Subject: [PATCH 1/2] feat(frontend): animated How-to-StackStream section, pin Turbopack root Rebuild the How-to-StackStream section to match the segment-card pattern: a self-playing sender -> recipient stream visual with a live-ticking sBTC balance, and bullet-point steps that highlight in sync with the animation. Also pin turbopack.root to the frontend dir so Next.js stops inferring the wrong workspace root from the two lockfiles in the monorepo. --- frontend/next.config.ts | 8 +- .../src/components/landing/who-its-for.tsx | 177 ++++++++++++++++-- 2 files changed, 168 insertions(+), 17 deletions(-) diff --git a/frontend/next.config.ts b/frontend/next.config.ts index e9ffa30..a5c09e5 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -1,7 +1,13 @@ import type { NextConfig } from "next"; +import path from "path"; const nextConfig: NextConfig = { - /* config options here */ + // This repo holds two independent npm projects (contract tests at the root, + // this Next.js app in /frontend), so there are two lockfiles. Pin the + // workspace root to this directory so Turbopack stops guessing. + turbopack: { + root: path.resolve(__dirname), + }, }; export default nextConfig; diff --git a/frontend/src/components/landing/who-its-for.tsx b/frontend/src/components/landing/who-its-for.tsx index 407d836..d9b0abb 100644 --- a/frontend/src/components/landing/who-its-for.tsx +++ b/frontend/src/components/landing/who-its-for.tsx @@ -1,7 +1,16 @@ "use client"; +import { useEffect, useState } from "react"; import { motion } from "framer-motion"; -import { Landmark, Store, Laptop, Target } from "lucide-react"; +import { + Landmark, + Store, + Laptop, + Target, + Wallet, + UserRound, + Check, +} from "lucide-react"; const segments = [ { @@ -38,21 +47,125 @@ const steps = [ { number: "1", title: "Create", - description: "Pick a recipient, any token, an amount, and a duration.", + headline: "Set it up once", + points: [ + "Choose who you are paying", + "Pick any token — sBTC, STX, or your own", + "Set the amount and how long it runs", + ], }, { number: "2", title: "Stream", - description: "Funds flow every block on their own, with nothing left to do.", + headline: "It runs itself", + points: [ + "Funds flow every few seconds, on Bitcoin", + "No payroll runs, no reminders to set", + "Pause, top up, or stop anytime", + ], }, { number: "3", title: "Claim", - description: "Recipients pull earnings anytime. Senders reclaim the rest.", + headline: "Cash out anytime", + points: [ + "Watch the balance grow live", + "Pull it to your wallet — all or part", + "Cancel early and the rest comes back", + ], }, ]; +const particles = [0, 1, 2, 3, 4, 5]; + +function StreamVisual({ phase }: { phase: number }) { + const [balance, setBalance] = useState(0); + + useEffect(() => { + let raf = 0; + let start: number | null = null; + const RATE = 0.00042; // sBTC accrued per second, tuned for a calm tick + const loop = (t: number) => { + if (start === null) start = t; + setBalance(((t - start) / 1000) * RATE); + raf = requestAnimationFrame(loop); + }; + raf = requestAnimationFrame(loop); + return () => cancelAnimationFrame(raf); + }, []); + + return ( +
+
+ {/* Sender */} +
+
+ +
+ + Sender + +
+ + {/* Rail with flowing funds */} +
+ {particles.map((p) => ( + + ))} +
+ + {/* Recipient */} +
+
+ +
+ + Recipient + +
+
+ + {/* Live balance landing at the recipient */} +
+
+ +{balance.toFixed(6)}{" "} + sBTC +
+
+ streaming live, settling on Bitcoin every block +
+
+
+ ); +} + export function WhoItsFor() { + const [phase, setPhase] = useState(0); + + useEffect(() => { + const id = setInterval(() => setPhase((p) => (p + 1) % 3), 2400); + return () => clearInterval(id); + }, []); + return (
@@ -95,10 +208,19 @@ export function WhoItsFor() {
{/* How to StackStream */} -
-

- How to StackStream -

+
+
+

+ How to StackStream +

+

+ Three steps, and the first is the only one that takes any effort. + Watch a payment flow from sender to recipient in real time. +

+
+ + +
{steps.map((step, i) => ( -
- {step.number} +
+
+ {step.number} +
+ + {step.title} +
-

- {step.title} +

+ {step.headline}

-

- {step.description} -

+
    + {step.points.map((point) => ( +
  • + + {point} +
  • + ))} +
))}
From 6960ed0458080dab1e11cbc9cfa33b262d284c7f Mon Sep 17 00:00:00 2001 From: jayteemoney Date: Thu, 2 Jul 2026 07:32:00 +0100 Subject: [PATCH 2/2] feat(frontend): surface OpenClaw assistant and add FAQ to landing Add a landing section showcasing the OpenClaw assistant with a faithful preview of the in-app widget, so visitors discover the feature (it previously only appeared inside the app shell). Add a clean accordion FAQ with a feedback/contact strip (feedback form, Telegram, GitHub). Wire both into the landing page between Features and the CTA. --- frontend/src/app/page.tsx | 4 + .../components/landing/assistant-section.tsx | 198 ++++++++++++++++++ frontend/src/components/landing/faq.tsx | 160 ++++++++++++++ 3 files changed, 362 insertions(+) create mode 100644 frontend/src/components/landing/assistant-section.tsx create mode 100644 frontend/src/components/landing/faq.tsx diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 4b483cf..a9c7138 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -3,6 +3,8 @@ import { Hero } from "@/components/landing/hero"; import { WhoItsFor } from "@/components/landing/who-its-for"; import { Features } from "@/components/landing/features"; +import { AssistantSection } from "@/components/landing/assistant-section"; +import { FAQ } from "@/components/landing/faq"; import { CTASection } from "@/components/landing/cta-section"; import { Footer } from "@/components/landing/footer"; import Link from "next/link"; @@ -40,6 +42,8 @@ export default function LandingPage() { + +
diff --git a/frontend/src/components/landing/assistant-section.tsx b/frontend/src/components/landing/assistant-section.tsx new file mode 100644 index 0000000..0d60d9b --- /dev/null +++ b/frontend/src/components/landing/assistant-section.tsx @@ -0,0 +1,198 @@ +"use client"; + +import Link from "next/link"; +import { motion } from "framer-motion"; +import { Button } from "@/components/ui/button"; +import { + Zap, + Hash, + User, + Building2, + Search, + Sparkles, + ArrowRight, +} from "lucide-react"; + +const capabilities = [ + { + icon: Hash, + title: "Look up any stream", + text: "Drop in a stream ID and see status, deposit, and exactly what's claimable right now.", + }, + { + icon: User, + title: "Search by address", + text: "Paste a sender or recipient address to pull every stream flowing in or out of it.", + }, + { + icon: Building2, + title: "Inspect a workspace", + text: "Check a registered DAO or business — streams created, total deposited, active status.", + }, + { + icon: Zap, + title: "Read the chain live", + text: "Ask for the current block height and other on-chain state without leaving the app.", + }, +]; + +// Preview of the OpenClaw panel — mirrors the real in-app widget so visitors +// see exactly what they get once they launch the app. +function AssistantPreview() { + const tabs = [ + { icon: Hash, label: "Stream", active: true }, + { icon: User, label: "Sender", active: false }, + { icon: User, label: "Recipient", active: false }, + { icon: Building2, label: "Workspace", active: false }, + { icon: Zap, label: "Block", active: false }, + ]; + + return ( +
+ {/* Header */} +
+
+ +
+
+

OpenClaw Assistant

+

+ Query streams, workspaces, and blockchain state +

+
+
+ + {/* Tabs */} +
+ {tabs.map((t) => ( + + + {t.label} + + ))} +
+ + {/* Result */} +
+
+
+ + stream + + 7 +
+
+
+ Stream #7 + Active +
+
+
Sender
+
SP2J6ZY...
+
Recipient
+
SP3FBR2...
+
Deposited
+
0.500000 sBTC
+
Claimable
+
0.184210 sBTC
+
Progress
+
36.8%
+
+
+
+
+ + {/* Input */} +
+
+ Stream ID (e.g. 7) +
+
+ +
+
+
+ ); +} + +export function AssistantSection() { + return ( +
+
+
+ {/* Copy */} + +
+ + Built in: OpenClaw Assistant +
+

+ An assistant that reads the chain for you +

+

+ OpenClaw lives inside the app. Ask it about any stream, address, or + workspace and it answers in a click — no block explorer, no raw + contract calls, no guesswork. +

+ +
+ {capabilities.map((cap) => ( +
+
+ +
+

+ {cap.title} +

+

+ {cap.text} +

+
+ ))} +
+ +
+ + + +
+
+ + {/* Preview */} + +
+
+
+ +
+
+ +
+
+
+ ); +} diff --git a/frontend/src/components/landing/faq.tsx b/frontend/src/components/landing/faq.tsx new file mode 100644 index 0000000..003f80b --- /dev/null +++ b/frontend/src/components/landing/faq.tsx @@ -0,0 +1,160 @@ +"use client"; + +import { useState } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { Plus, MessageCircle, Send, Github } from "lucide-react"; +import { FEEDBACK_URL } from "@/lib/constants"; + +const faqs = [ + { + q: "What is StackStream?", + a: "A Bitcoin-native payment streaming protocol. Instead of paying people in one lump sum on a payday, you open a stream and funds flow to them every few seconds. It runs on Stacks and settles on Bitcoin, so payments are final and can't be reversed.", + }, + { + q: "Is it live? Can I use it now?", + a: "Yes. StackStream is live on Stacks mainnet at stackstream.xyz. Connect a Stacks wallet, create a stream, and it starts flowing immediately. You can watch a real balance tick up in the app right now.", + }, + { + q: "Which tokens can I stream?", + a: "sBTC, STX, USDA, ALEX, xBTC, or any SIP-010 token. If it follows the SIP-010 standard on Stacks, you can stream it by the second.", + }, + { + q: "Is my money safe while it's streaming?", + a: "Funds sit in on-chain escrow, not in anyone's pocket. The recipient can only ever claim what they've already earned, and everything not yet earned stays yours. Pause, top up, or cancel at any time and the unearned remainder comes straight back to your wallet.", + }, + { + q: "What is OpenClaw?", + a: "OpenClaw is the assistant built into the app. Ask it about any stream, sender, recipient, or workspace and it pulls the live on-chain answer instantly — no block explorer or raw contract calls needed.", + }, + { + q: "Who is StackStream for?", + a: "DAOs and protocol teams paying contributors, businesses running subscriptions or paying suppliers, freelancers who want to get paid as they work, and grant or bounty programs funding builders steadily instead of in one lump sum.", + }, + { + q: "How is this different from a normal crypto payment?", + a: "A normal payment is one transfer at one moment. A stream is continuous — money moves second by second for as long as you set it to run, and either side can adjust or stop it mid-flow. Think of it as a tap you turn on, not a calendar reminder.", + }, + { + q: "Is the code open and audited?", + a: "The Clarity smart contracts are open source on GitHub and covered by a full test suite that verifies funds are always conserved. You can read every contract and run the tests yourself.", + }, +]; + +function FaqItem({ + faq, + isOpen, + onToggle, +}: { + faq: { q: string; a: string }; + isOpen: boolean; + onToggle: () => void; +}) { + return ( +
+ + + {isOpen && ( + +

+ {faq.a} +

+
+ )} +
+
+ ); +} + +export function FAQ() { + const [openIndex, setOpenIndex] = useState(0); + + return ( +
+
+
+

+ Frequently asked questions +

+

+ Everything you need to know about streaming money on Bitcoin. Still + curious? Reach out below — we answer fast. +

+
+ +
+ {faqs.map((faq, i) => ( + setOpenIndex(openIndex === i ? null : i)} + /> + ))} +
+ + {/* Feedback / contact strip */} +
+
+ +
+

+ Still have a question? +

+

+ Send us feedback or a question directly, or reach the team on + Telegram. Every message gets read. +

+ +
+
+
+ ); +}