> ## Documentation Index
> Fetch the complete documentation index at: https://hopsdocs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Hop it out

> Tell us what you want to build. We'll show you how to do it with Hops.

export const HopItOut = () => {
  const useState = React.useState;
  const useMemo = React.useMemo;
  const useRef = React.useRef;
  const HOPS_API_ENDPOINT = typeof window !== "undefined" && window.location.hostname === "localhost" ? "http://localhost:3000/api/hop-proxy" : "https://proxy-gemini-endpoint-production.up.railway.app/api/hop-proxy";
  const HOPS_CLIENT_KEY = "hops-docs-widget-v1";
  const CLIENT_DAILY_LIMIT = 20;
  const QUOTA_KEY = "hopItOut.quota.v1";
  const readQuota = () => {
    if (typeof window === "undefined") return {
      day: 0,
      count: 0
    };
    try {
      const raw = window.localStorage.getItem(QUOTA_KEY);
      const parsed = raw ? JSON.parse(raw) : null;
      const today = Math.floor(Date.now() / 86400000);
      if (!parsed || parsed.day !== today) return {
        day: today,
        count: 0
      };
      return parsed;
    } catch (_) {
      return {
        day: 0,
        count: 0
      };
    }
  };
  const bumpQuota = () => {
    if (typeof window === "undefined") return;
    const q = readQuota();
    const today = Math.floor(Date.now() / 86400000);
    const next = {
      day: today,
      count: q.day === today ? q.count + 1 : 1
    };
    try {
      window.localStorage.setItem(QUOTA_KEY, JSON.stringify(next));
    } catch (_) {}
  };
  const TOOL_OPTIONS = ["Salesforce", "HubSpot", "Pipedrive", "Gmail", "Outlook", "Google Calendar", "Google Drive", "Google Sheets", "Slack", "Microsoft Teams", "Notion", "Jira", "Linear", "Asana", "monday", "ClickUp", "Trello", "Zendesk", "Intercom", "Stripe", "QuickBooks", "Xero", "Airtable", "GitHub", "Shopify", "Zoom", "Calendly", "Dropbox"];
  const EXAMPLES = ["Weekly customer health report from Salesforce + Zendesk", "Draft investor updates from our metrics and Slack", "Triage inbound support tickets and route to the right owner", "Prep me for tomorrow's sales calls with account context"];
  const [step, setStep] = useState(1);
  const [problem, setProblem] = useState("");
  const [selectedTools, setSelectedTools] = useState([]);
  const [otherTools, setOtherTools] = useState("");
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");
  const [plan, setPlan] = useState(null);
  const canContinue = problem.trim().length > 5;
  const canSubmit = selectedTools.length > 0 || otherTools.trim().length > 0;
  const toggleTool = t => {
    setSelectedTools(cur => cur.includes(t) ? cur.filter(x => x !== t) : cur.concat(t));
  };
  const allTools = useMemo(() => {
    const extras = otherTools.split(",").map(s => s.trim()).filter(Boolean);
    return selectedTools.concat(extras);
  }, [selectedTools, otherTools]);
  const reset = () => {
    setStep(1);
    setProblem("");
    setSelectedTools([]);
    setOtherTools("");
    setError("");
    setPlan(null);
  };
  const submit = async () => {
    const q = readQuota();
    if (q.count >= CLIENT_DAILY_LIMIT) {
      setError("You've used your " + CLIENT_DAILY_LIMIT + " plans for today. Try again tomorrow.");
      return;
    }
    setLoading(true);
    setError("");
    setPlan(null);
    try {
      const system = "You are a Hops solutions architect. Hops is an AI workspace with ONE agent — Hops — that is available in every Space. You extend Hops by installing Skills (packaged instructions, tone, and priorities for a specific job). Users create Spaces (project workspaces), pin Space Context, connect apps, install a Skill, and schedule Automations. There are no separate or custom agents — everything runs through the single Hops agent, shaped by the Skill it uses. Given a user's problem and tool stack, produce 2-3 concrete build recommendations that use Hops. Return ONLY valid JSON matching this schema (no prose, no code fences): {\"lead\":\"one-sentence framing\",\"recommendations\":[{\"title\":\"short title\",\"summary\":\"1-2 sentence what it does\",\"why\":\"one sentence why this fits their problem\",\"skill\":\"the ready-to-paste Skill instruction for Hops, 2-5 sentences addressed to the Hops agent\",\"space\":\"how to set up the Space and Space Context in 1-2 sentences\",\"automation\":\"the recurring schedule in 1-2 sentences\",\"have\":[\"tools they already listed that this uses\"],\"need\":[\"additional tools they should connect\"],\"docsUrl\":\"a relevant /docs/... path\"}]}";
      const userMsg = "Problem: " + problem.trim() + "\n\nTools they have: " + (allTools.length ? allTools.join(", ") : "none listed");
      const res = await fetch(HOPS_API_ENDPOINT, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "x-hop-key": HOPS_CLIENT_KEY
        },
        body: JSON.stringify({
          system,
          messages: [{
            role: "user",
            content: userMsg
          }],
          max_tokens: 3072,
          temperature: 0.4
        })
      });
      const data = await res.json();
      if (!res.ok) {
        if (res.status === 429) throw new Error(data && data.error || "Rate limit reached. Try again later.");
        if (res.status === 401 || res.status === 403) throw new Error("Access blocked. Please refresh the page.");
        throw new Error(data && data.error || "Request failed");
      }
      bumpQuota();
      const text = data.content && data.content[0] && data.content[0].text || "";
      const jsonStart = text.indexOf("{");
      const jsonEnd = text.lastIndexOf("}");
      if (jsonStart === -1 || jsonEnd === -1) {
        throw new Error("The model didn't return a valid plan. Try again.");
      }
      const parsed = JSON.parse(text.slice(jsonStart, jsonEnd + 1));
      if (!parsed.recommendations || !Array.isArray(parsed.recommendations)) {
        throw new Error("Unexpected plan format. Try again.");
      }
      setPlan(parsed);
    } catch (e) {
      setError(String(e && e.message || e));
    } finally {
      setLoading(false);
    }
  };
  const CopyBtn = ({text}) => {
    const [copied, setCopied] = useState(false);
    return React.createElement("button", {
      onClick: () => {
        navigator.clipboard.writeText(text);
        setCopied(true);
        setTimeout(() => setCopied(false), 1600);
      },
      style: {
        position: "absolute",
        top: 12,
        right: 12,
        padding: "6px 12px",
        fontSize: 12,
        fontWeight: 600,
        background: copied ? "#059669" : "rgba(255,255,255,0.12)",
        color: "#fff",
        border: "1px solid rgba(255,255,255,0.2)",
        borderRadius: 6,
        cursor: "pointer"
      }
    }, copied ? "Copied \u2713" : "Copy");
  };
  const cardStyle = {
    maxWidth: 1080,
    margin: "24px auto",
    padding: "36px 40px",
    background: "var(--card-bg, #fff)",
    border: "1px solid var(--card-border, #e5e7eb)",
    borderRadius: 14,
    boxShadow: "0 1px 3px rgba(0,0,0,0.05)",
    fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",
    color: "var(--card-fg, #111827)"
  };
  const q = {
    fontSize: 22,
    fontWeight: 700,
    marginBottom: 12,
    color: "var(--card-fg, #111827)"
  };
  const help = {
    fontSize: 14,
    color: "var(--card-muted, #6b7280)",
    marginBottom: 18
  };
  const taStyle = {
    width: "100%",
    minHeight: 120,
    padding: 14,
    fontSize: 16,
    border: "1px solid var(--card-border, #d1d5db)",
    borderRadius: 10,
    resize: "vertical",
    boxSizing: "border-box",
    fontFamily: "inherit",
    background: "var(--input-bg, #fff)",
    color: "var(--card-fg, #111827)"
  };
  const chip = active => ({
    padding: "8px 14px",
    fontSize: 14,
    borderRadius: 999,
    border: "1px solid " + (active ? "#0047AB" : "var(--card-border, #d1d5db)"),
    background: active ? "#0047AB" : "var(--input-bg, #fff)",
    color: active ? "#fff" : "var(--card-fg, #111827)",
    cursor: "pointer",
    fontWeight: 500
  });
  const btnPrimary = disabled => ({
    padding: "12px 22px",
    fontSize: 15,
    fontWeight: 600,
    background: disabled ? "#9ca3af" : "#0047AB",
    color: "#fff",
    border: "none",
    borderRadius: 8,
    cursor: disabled ? "not-allowed" : "pointer"
  });
  const btnGhost = {
    padding: "12px 20px",
    fontSize: 15,
    fontWeight: 600,
    background: "transparent",
    color: "var(--card-fg, #111827)",
    border: "1px solid var(--card-border, #d1d5db)",
    borderRadius: 8,
    cursor: "pointer"
  };
  if (plan) {
    return React.createElement("div", {
      style: cardStyle
    }, React.createElement("div", {
      style: {
        display: "inline-block",
        padding: "4px 12px",
        fontSize: 12,
        fontWeight: 700,
        letterSpacing: 0.5,
        textTransform: "uppercase",
        color: "#0047AB",
        background: "rgba(0,71,171,0.08)",
        borderRadius: 999,
        marginBottom: 14
      }
    }, "Your Hops build plan"), React.createElement("p", {
      style: {
        fontSize: 17,
        lineHeight: 1.55,
        color: "var(--card-muted, #4b5563)",
        marginBottom: 28
      }
    }, plan.lead || "Here's how to build this with Hops."), plan.recommendations.map((r, i) => React.createElement("div", {
      key: i,
      style: {
        border: "1px solid var(--card-border, #e5e7eb)",
        borderRadius: 12,
        padding: 28,
        marginBottom: 22,
        background: "var(--card-bg, #fff)"
      }
    }, React.createElement("div", {
      style: {
        fontSize: 12,
        fontWeight: 600,
        color: "var(--card-muted, #6b7280)",
        marginBottom: 6
      }
    }, "Recommendation " + (i + 1) + " of " + plan.recommendations.length), React.createElement("h2", {
      style: {
        fontSize: 26,
        fontWeight: 700,
        margin: "0 0 10px 0",
        color: "var(--card-fg, #111827)"
      }
    }, r.title), React.createElement("p", {
      style: {
        fontSize: 15,
        lineHeight: 1.6,
        color: "var(--card-muted, #4b5563)",
        marginBottom: 16
      }
    }, r.summary), r.why && React.createElement("div", {
      style: {
        padding: "12px 16px",
        background: "rgba(5,150,105,0.08)",
        borderLeft: "3px solid #059669",
        borderRadius: 6,
        marginBottom: 20,
        fontSize: 14,
        color: "var(--card-fg, #111827)"
      }
    }, React.createElement("strong", null, "Why this fits: "), r.why), r.skill && React.createElement("div", {
      style: {
        marginBottom: 20
      }
    }, React.createElement("h3", {
      style: {
        fontSize: 15,
        fontWeight: 700,
        marginBottom: 8,
        color: "var(--card-fg, #111827)"
      }
    }, "Skill for Hops"), React.createElement("div", {
      style: {
        position: "relative",
        background: "#1f2937",
        color: "#f3f4f6",
        padding: "16px 18px",
        paddingRight: 76,
        borderRadius: 8,
        fontSize: 14,
        lineHeight: 1.55,
        fontFamily: "ui-monospace, Menlo, Monaco, monospace",
        whiteSpace: "pre-wrap",
        wordBreak: "break-word"
      }
    }, r.skill, React.createElement(CopyBtn, {
      text: r.skill
    }))), r.space && React.createElement("div", {
      style: {
        marginBottom: 18
      }
    }, React.createElement("h3", {
      style: {
        fontSize: 15,
        fontWeight: 700,
        marginBottom: 6,
        color: "var(--card-fg, #111827)"
      }
    }, "Space setup"), React.createElement("p", {
      style: {
        fontSize: 14,
        lineHeight: 1.6,
        color: "var(--card-muted, #4b5563)",
        margin: 0
      }
    }, r.space)), r.automation && React.createElement("div", {
      style: {
        marginBottom: 18
      }
    }, React.createElement("h3", {
      style: {
        fontSize: 15,
        fontWeight: 700,
        marginBottom: 6,
        color: "var(--card-fg, #111827)"
      }
    }, "Automation"), React.createElement("p", {
      style: {
        fontSize: 14,
        lineHeight: 1.6,
        color: "var(--card-muted, #4b5563)",
        margin: 0
      }
    }, r.automation)), React.createElement("div", {
      style: {
        marginBottom: 18
      }
    }, React.createElement("h3", {
      style: {
        fontSize: 15,
        fontWeight: 700,
        marginBottom: 10,
        color: "var(--card-fg, #111827)"
      }
    }, "Connections"), React.createElement("div", {
      className: "hop-conn-grid",
      style: {
        display: "grid",
        gridTemplateColumns: "1fr 1fr",
        gap: 16
      }
    }, React.createElement("div", null, React.createElement("div", {
      style: {
        fontSize: 12,
        fontWeight: 600,
        color: "#059669",
        marginBottom: 6,
        textTransform: "uppercase",
        letterSpacing: 0.5
      }
    }, "You already have"), (r.have && r.have.length ? r.have : ["None from your list"]).map((h, k) => React.createElement("span", {
      key: k,
      style: {
        display: "inline-block",
        padding: "4px 10px",
        margin: "0 6px 6px 0",
        fontSize: 13,
        background: r.have && r.have.length ? "rgba(5,150,105,0.1)" : "transparent",
        color: r.have && r.have.length ? "#065f46" : "var(--card-muted, #6b7280)",
        borderRadius: 6,
        fontStyle: r.have && r.have.length ? "normal" : "italic"
      }
    }, h))), React.createElement("div", null, React.createElement("div", {
      style: {
        fontSize: 12,
        fontWeight: 600,
        color: "#d97706",
        marginBottom: 6,
        textTransform: "uppercase",
        letterSpacing: 0.5
      }
    }, "You'd also need"), (r.need && r.need.length ? r.need : ["Nothing extra"]).map((n, k) => React.createElement("span", {
      key: k,
      style: {
        display: "inline-block",
        padding: "4px 10px",
        margin: "0 6px 6px 0",
        fontSize: 13,
        background: r.need && r.need.length ? "rgba(217,119,6,0.1)" : "transparent",
        color: r.need && r.need.length ? "#92400e" : "var(--card-muted, #6b7280)",
        borderRadius: 6,
        fontStyle: r.need && r.need.length ? "normal" : "italic"
      }
    }, n))))), r.docsUrl && React.createElement("a", {
      href: r.docsUrl,
      style: {
        display: "inline-flex",
        alignItems: "center",
        gap: 6,
        padding: "10px 16px",
        fontSize: 14,
        fontWeight: 600,
        color: "#0047AB",
        border: "1px solid #0047AB",
        borderRadius: 8,
        textDecoration: "none"
      }
    }, "Read the docs \u2192"))), React.createElement("div", {
      style: {
        display: "flex",
        gap: 12,
        marginTop: 28,
        flexWrap: "wrap"
      }
    }, React.createElement("button", {
      onClick: reset,
      style: btnPrimary(false)
    }, "Start over"), React.createElement("a", {
      href: "https://hops.ai",
      style: Object.assign({}, btnGhost, {
        textDecoration: "none",
        display: "inline-flex",
        alignItems: "center"
      })
    }, "Try Hops \u2192")), React.createElement("style", null, "@media (max-width: 640px){.hop-conn-grid{grid-template-columns:1fr !important}}"));
  }
  if (step === 1) {
    return React.createElement("div", {
      style: cardStyle
    }, React.createElement("h2", {
      style: q
    }, "What do you want to build?"), React.createElement("p", {
      style: help
    }, "Describe the problem you're solving. Be specific about the outcome you want."), React.createElement("textarea", {
      style: taStyle,
      placeholder: "e.g. Every Monday, send our exec team a summary of last week's customer feedback grouped by theme.",
      value: problem,
      onChange: e => setProblem(e.target.value),
      maxLength: 500
    }), React.createElement("div", {
      style: {
        fontSize: 12,
        color: "var(--card-muted, #9ca3af)",
        marginTop: 6,
        marginBottom: 18,
        textAlign: "right"
      }
    }, problem.length + " / 500"), React.createElement("div", {
      style: {
        marginBottom: 22
      }
    }, React.createElement("div", {
      style: {
        fontSize: 12,
        fontWeight: 600,
        color: "var(--card-muted, #6b7280)",
        textTransform: "uppercase",
        letterSpacing: 0.5,
        marginBottom: 10
      }
    }, "Or start from an example"), React.createElement("div", {
      style: {
        display: "flex",
        flexWrap: "wrap",
        gap: 8
      }
    }, EXAMPLES.map((ex, i) => React.createElement("button", {
      key: i,
      onClick: () => setProblem(ex),
      style: chip(false)
    }, ex)))), React.createElement("div", {
      style: {
        display: "flex",
        justifyContent: "flex-end"
      }
    }, React.createElement("button", {
      onClick: () => setStep(2),
      disabled: !canContinue,
      style: btnPrimary(!canContinue)
    }, "Continue \u2192")));
  }
  return React.createElement("div", {
    style: cardStyle
  }, React.createElement("h2", {
    style: q
  }, "Which tools do you use?"), React.createElement("p", {
    style: help
  }, "Pick the apps you already have. We'll match them to your recommendations and flag what else you'd need."), React.createElement("div", {
    style: {
      display: "flex",
      flexWrap: "wrap",
      gap: 8,
      marginBottom: 22
    }
  }, TOOL_OPTIONS.map(t => React.createElement("button", {
    key: t,
    onClick: () => toggleTool(t),
    style: chip(selectedTools.includes(t))
  }, t))), React.createElement("div", {
    style: {
      marginBottom: 24
    }
  }, React.createElement("label", {
    style: {
      display: "block",
      fontSize: 13,
      fontWeight: 600,
      color: "var(--card-muted, #6b7280)",
      marginBottom: 6
    }
  }, "Other tools (comma separated)"), React.createElement("input", {
    type: "text",
    placeholder: "e.g. Freshdesk, Segment, ClickHouse",
    value: otherTools,
    onChange: e => setOtherTools(e.target.value),
    style: {
      width: "100%",
      padding: 12,
      fontSize: 15,
      border: "1px solid var(--card-border, #d1d5db)",
      borderRadius: 8,
      boxSizing: "border-box",
      background: "var(--input-bg, #fff)",
      color: "var(--card-fg, #111827)"
    }
  })), error && React.createElement("div", {
    style: {
      padding: 12,
      background: "rgba(220,38,38,0.08)",
      color: "#b91c1c",
      borderRadius: 8,
      marginBottom: 16,
      fontSize: 14
    }
  }, error), React.createElement("div", {
    style: {
      display: "flex",
      justifyContent: "space-between",
      alignItems: "center",
      flexWrap: "wrap",
      gap: 12
    }
  }, React.createElement("button", {
    onClick: () => setStep(1),
    style: btnGhost
  }, "\u2190 Back"), React.createElement("button", {
    onClick: submit,
    disabled: !canSubmit || loading,
    style: btnPrimary(!canSubmit || loading)
  }, loading ? "Building your plan..." : "Get my plan \u2192")));
};

<HopItOut />
