- Add Node.js MCP server (stdio + HTTP/SSE) with generate_qr and generate_pix_qr tools - Add landing pages PT/EN at /mcp and /mcp/en with hreflang SEO - Fix OAuth returnUrl via RedirectUri query param (state was always null in callback) - Fix API key requests bypassing web credit check (use rate limiter instead) - Add /api/mcp nginx route + Docker Swarm service for n8n cloud integration - Auto-create API key on first OAuth login with TempData display - Add UseDefaultFiles() for /mcp → /mcp/index.html serving - Fix Serilog console log level in Development (was Error, now Info for app logs) - Add /api/v1/QRManager/me endpoint for API key validation - Update CI/CD to build and deploy qrrapido-mcp image alongside .NET app Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
739 lines
44 KiB
HTML
739 lines
44 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>QR Rápido MCP — QR code generator for AI agents and automations</title>
|
|
<meta name="description" content="Generate QR codes via Claude MCP, REST API, n8n, and Make.com. Inline base64 response — no file hosting needed. Built for AI agents, workflows, and automations.">
|
|
<meta name="keywords" content="QR code API, MCP, Claude Desktop, n8n, automation, AI agents, Make.com, REST API">
|
|
<meta property="og:title" content="QR Rápido MCP — QR code generator for AI agents">
|
|
<meta property="og:description" content="The native QR code generator for AI agents and automations. MCP, REST API, n8n, Make.com.">
|
|
<meta property="og:url" content="https://qrrapido.site/mcp/en">
|
|
<meta name="robots" content="index, follow">
|
|
<link rel="canonical" href="https://qrrapido.site/mcp/en">
|
|
<link rel="alternate" hreflang="pt-BR" href="https://qrrapido.site/mcp">
|
|
<link rel="alternate" hreflang="en" href="https://qrrapido.site/mcp/en">
|
|
<link rel="alternate" hreflang="x-default" href="https://qrrapido.site/mcp">
|
|
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=Figtree:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
|
|
<style>
|
|
:root {
|
|
--bg-base: #06060e;
|
|
--bg-surface: #0d0d1a;
|
|
--bg-elevated: #13131f;
|
|
--bg-card: #0f0f1c;
|
|
--border: rgba(99, 102, 241, 0.15);
|
|
--border-bright: rgba(99, 102, 241, 0.35);
|
|
--indigo: #6366f1;
|
|
--violet: #8b5cf6;
|
|
--emerald: #10b981;
|
|
--cyan: #06b6d4;
|
|
--text-primary: #eeeef5;
|
|
--text-secondary: #9090b0;
|
|
--text-muted: #55556a;
|
|
--text-code: #e0e0ff;
|
|
--font-display: 'Syne', sans-serif;
|
|
--font-body: 'Figtree', sans-serif;
|
|
--font-mono: 'JetBrains Mono', monospace;
|
|
--radius-sm: 6px;
|
|
--radius-md: 12px;
|
|
--radius-lg: 20px;
|
|
--transition: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
html { scroll-behavior: smooth; }
|
|
body {
|
|
background: var(--bg-base);
|
|
color: var(--text-primary);
|
|
font-family: var(--font-body);
|
|
font-size: 16px;
|
|
line-height: 1.6;
|
|
overflow-x: hidden;
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
body::before {
|
|
content: '';
|
|
position: fixed;
|
|
inset: 0;
|
|
background-image: radial-gradient(circle, rgba(99,102,241,0.11) 1px, transparent 1px);
|
|
background-size: 28px 28px;
|
|
pointer-events: none;
|
|
z-index: 0;
|
|
}
|
|
body::after {
|
|
content: '';
|
|
position: fixed;
|
|
width: 700px; height: 700px;
|
|
border-radius: 50%;
|
|
background: radial-gradient(circle, rgba(99,102,241,0.07) 0%, transparent 70%);
|
|
top: -250px; right: -150px;
|
|
pointer-events: none;
|
|
z-index: 0;
|
|
}
|
|
section, nav, footer, .integrations { position: relative; z-index: 1; }
|
|
.container { max-width: 1100px; margin: 0 auto; padding: 0 24px; }
|
|
|
|
/* NAV */
|
|
nav {
|
|
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
|
padding: 14px 24px;
|
|
background: rgba(6, 6, 14, 0.88);
|
|
backdrop-filter: blur(20px);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.nav-inner { max-width: 1100px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; }
|
|
.nav-logo { display: flex; align-items: center; gap: 10px; text-decoration: none; font-family: var(--font-display); font-weight: 700; font-size: 17px; color: var(--text-primary); }
|
|
.nav-logo-icon { width: 30px; height: 30px; background: linear-gradient(135deg, var(--indigo), var(--violet)); border-radius: 7px; display: flex; align-items: center; justify-content: center; font-size: 15px; }
|
|
.nav-badge { font-size: 10px; font-family: var(--font-mono); background: rgba(99,102,241,0.15); border: 1px solid rgba(99,102,241,0.3); color: var(--indigo); padding: 2px 8px; border-radius: 4px; letter-spacing: 0.05em; }
|
|
.nav-links { display: flex; align-items: center; gap: 4px; }
|
|
.nav-link { font-size: 14px; color: var(--text-secondary); text-decoration: none; padding: 7px 13px; border-radius: var(--radius-sm); transition: var(--transition); }
|
|
.nav-link:hover { color: var(--text-primary); background: rgba(255,255,255,0.05); }
|
|
.lang-switch { font-size: 12px; font-family: var(--font-mono); color: var(--text-muted); text-decoration: none; padding: 5px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm); transition: var(--transition); }
|
|
.lang-switch:hover { color: var(--indigo); border-color: var(--border-bright); }
|
|
.btn { display: inline-flex; align-items: center; gap: 8px; padding: 9px 18px; border-radius: var(--radius-sm); font-family: var(--font-body); font-size: 14px; font-weight: 600; text-decoration: none; transition: var(--transition); cursor: pointer; border: none; }
|
|
.btn-primary { background: linear-gradient(135deg, var(--indigo), var(--violet)); color: white; box-shadow: 0 0 20px rgba(99,102,241,0.3); }
|
|
.btn-primary:hover { transform: translateY(-1px); box-shadow: 0 0 32px rgba(99,102,241,0.5); }
|
|
.btn-ghost { background: transparent; color: var(--text-secondary); border: 1px solid var(--border); }
|
|
.btn-ghost:hover { color: var(--text-primary); border-color: var(--border-bright); background: rgba(99,102,241,0.05); }
|
|
.btn-lg { padding: 14px 28px; font-size: 15px; border-radius: var(--radius-md); }
|
|
|
|
/* HERO */
|
|
.hero { padding: 140px 24px 80px; min-height: 100vh; display: flex; align-items: center; }
|
|
.hero-inner { max-width: 1100px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr; gap: 60px; align-items: center; }
|
|
.hero-tag { display: inline-flex; align-items: center; gap: 8px; font-family: var(--font-mono); font-size: 12px; color: var(--indigo); background: rgba(99,102,241,0.1); border: 1px solid rgba(99,102,241,0.25); padding: 6px 14px; border-radius: 100px; margin-bottom: 24px; letter-spacing: 0.05em; opacity: 0; animation: fadeUp 0.6s 0.1s forwards; }
|
|
.hero-tag-dot { width: 6px; height: 6px; background: var(--emerald); border-radius: 50%; animation: pulse 2s infinite; }
|
|
.hero-title { font-family: var(--font-display); font-size: clamp(36px, 4.5vw, 54px); font-weight: 800; line-height: 1.1; letter-spacing: -0.03em; margin-bottom: 20px; opacity: 0; animation: fadeUp 0.6s 0.2s forwards; }
|
|
.hero-title .accent { background: linear-gradient(135deg, var(--indigo) 0%, var(--violet) 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
|
|
.hero-subtitle { font-size: 17px; color: var(--text-secondary); line-height: 1.65; margin-bottom: 36px; font-weight: 300; opacity: 0; animation: fadeUp 0.6s 0.3s forwards; }
|
|
.hero-actions { display: flex; gap: 12px; flex-wrap: wrap; opacity: 0; animation: fadeUp 0.6s 0.4s forwards; }
|
|
.hero-stats { display: flex; gap: 32px; margin-top: 40px; padding-top: 32px; border-top: 1px solid var(--border); opacity: 0; animation: fadeUp 0.6s 0.5s forwards; }
|
|
.stat-value { font-family: var(--font-display); font-size: 22px; font-weight: 700; }
|
|
.stat-label { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
|
|
|
|
/* TERMINAL */
|
|
.terminal { background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; box-shadow: 0 40px 80px rgba(0,0,0,0.6), inset 0 0 0 1px rgba(99,102,241,0.08); opacity: 0; animation: fadeIn 0.8s 0.5s forwards; }
|
|
.terminal-header { display: flex; align-items: center; gap: 8px; padding: 13px 16px; background: rgba(0,0,0,0.35); border-bottom: 1px solid var(--border); }
|
|
.dot { width: 10px; height: 10px; border-radius: 50%; }
|
|
.dot-r { background: #ff5f57; } .dot-y { background: #febc2e; } .dot-g { background: #28c840; }
|
|
.terminal-title { font-family: var(--font-mono); font-size: 12px; color: var(--text-muted); margin-left: 8px; }
|
|
.terminal-body { padding: 20px; font-family: var(--font-mono); font-size: 13px; line-height: 1.9; min-height: 340px; }
|
|
.t-prompt { color: var(--indigo); } .t-tool { color: var(--violet); } .t-key { color: var(--cyan); }
|
|
.t-string { color: #a5f3fc; } .t-success { color: var(--emerald); } .t-value { color: #fde68a; }
|
|
.t-dim { color: var(--text-muted); } .t-comment { color: var(--text-muted); font-style: italic; }
|
|
.terminal-line { display: block; opacity: 0; transition: opacity 0.25s ease; }
|
|
.cursor { display: inline-block; width: 8px; height: 14px; background: var(--indigo); vertical-align: middle; animation: blink 1s infinite; margin-left: 2px; }
|
|
|
|
/* INTEGRATIONS */
|
|
.integrations { padding: 28px 24px; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); background: rgba(13,13,26,0.7); }
|
|
.integrations-inner { max-width: 1100px; margin: 0 auto; display: flex; align-items: center; gap: 28px; flex-wrap: wrap; }
|
|
.integrations-label { font-size: 11px; font-family: var(--font-mono); color: var(--text-muted); letter-spacing: 0.12em; white-space: nowrap; }
|
|
.integrations-list { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
|
.integration-badge { display: flex; align-items: center; gap: 7px; padding: 7px 14px; background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-sm); font-size: 13px; font-weight: 500; color: var(--text-secondary); transition: var(--transition); }
|
|
.integration-badge:hover { border-color: var(--border-bright); color: var(--text-primary); background: rgba(99,102,241,0.06); }
|
|
|
|
/* SECTIONS */
|
|
.section { padding: 100px 24px; }
|
|
.section-label { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.15em; color: var(--indigo); margin-bottom: 14px; text-transform: uppercase; }
|
|
.section-title { font-family: var(--font-display); font-size: clamp(28px, 3.5vw, 42px); font-weight: 700; line-height: 1.15; letter-spacing: -0.02em; margin-bottom: 14px; }
|
|
.section-sub { font-size: 17px; color: var(--text-secondary); max-width: 520px; font-weight: 300; margin-bottom: 56px; line-height: 1.6; }
|
|
|
|
/* HOW IT WORKS */
|
|
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; }
|
|
.step { position: relative; }
|
|
.step-number { font-family: var(--font-display); font-size: 60px; font-weight: 800; color: rgba(99,102,241,0.07); line-height: 1; margin-bottom: -12px; user-select: none; }
|
|
.step-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 28px; transition: var(--transition); }
|
|
.step-card:hover { border-color: var(--border-bright); transform: translateY(-4px); box-shadow: 0 20px 40px rgba(0,0,0,0.4); }
|
|
.step-icon { width: 44px; height: 44px; background: linear-gradient(135deg, rgba(99,102,241,0.18), rgba(139,92,246,0.18)); border: 1px solid rgba(99,102,241,0.28); border-radius: var(--radius-sm); display: flex; align-items: center; justify-content: center; font-size: 20px; margin-bottom: 16px; }
|
|
.step-title { font-family: var(--font-display); font-size: 18px; font-weight: 700; margin-bottom: 8px; }
|
|
.step-desc { font-size: 14px; color: var(--text-secondary); line-height: 1.6; }
|
|
.step-arrow { position: absolute; top: 72px; right: -18px; color: var(--text-muted); font-size: 22px; z-index: 2; }
|
|
|
|
/* CODE */
|
|
.code-showcase { background: rgba(13,13,26,0.5); }
|
|
.tabs { display: flex; gap: 4px; margin-bottom: 20px; background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 4px; width: fit-content; }
|
|
.tab-btn { font-family: var(--font-mono); font-size: 12px; padding: 8px 16px; border-radius: var(--radius-sm); border: none; cursor: pointer; background: transparent; color: var(--text-muted); transition: var(--transition); }
|
|
.tab-btn.active { background: linear-gradient(135deg, var(--indigo), var(--violet)); color: white; }
|
|
.tab-btn:hover:not(.active) { color: var(--text-secondary); background: rgba(255,255,255,0.05); }
|
|
.tab-content { display: none; } .tab-content.active { display: block; }
|
|
.code-window { background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; box-shadow: 0 20px 60px rgba(0,0,0,0.5); }
|
|
.code-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; background: rgba(0,0,0,0.3); border-bottom: 1px solid var(--border); }
|
|
.code-header-left { display: flex; align-items: center; gap: 8px; }
|
|
.code-dots { display: flex; gap: 6px; }
|
|
.code-filename { font-family: var(--font-mono); font-size: 12px; color: var(--text-muted); margin-left: 8px; }
|
|
.copy-btn { font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); background: transparent; border: 1px solid var(--border); padding: 4px 10px; border-radius: 4px; cursor: pointer; transition: var(--transition); }
|
|
.copy-btn:hover { color: var(--indigo); border-color: rgba(99,102,241,0.4); }
|
|
pre { padding: 24px; overflow-x: auto; font-family: var(--font-mono); font-size: 13px; line-height: 1.85; color: var(--text-code); }
|
|
.hl-key { color: var(--cyan); } .hl-string { color: #a5f3fc; } .hl-number { color: #fde68a; }
|
|
.hl-bool { color: #f9a8d4; } .hl-comment { color: var(--text-muted); } .hl-url { color: var(--emerald); }
|
|
.n8n-mockup { padding: 24px; display: flex; flex-direction: column; gap: 14px; }
|
|
.n8n-node { background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.07); border-radius: var(--radius-md); padding: 16px; }
|
|
.n8n-node-header { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
|
|
.n8n-node-icon { width: 30px; height: 30px; border-radius: var(--radius-sm); display: flex; align-items: center; justify-content: center; font-size: 15px; }
|
|
.n8n-node-title { font-family: var(--font-display); font-size: 14px; font-weight: 600; }
|
|
.n8n-field { display: grid; grid-template-columns: 130px 1fr; gap: 8px; align-items: center; margin-bottom: 8px; }
|
|
.n8n-field-label { font-size: 12px; color: var(--text-muted); }
|
|
.n8n-field-value { font-family: var(--font-mono); font-size: 12px; background: rgba(0,0,0,0.3); border: 1px solid var(--border); border-radius: 4px; padding: 4px 8px; color: #a5f3fc; }
|
|
|
|
/* QR TYPES */
|
|
.qr-types-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
|
|
.qr-type-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 24px 18px; text-align: center; transition: var(--transition); }
|
|
.qr-type-card:hover { border-color: var(--border-bright); background: var(--bg-elevated); transform: translateY(-2px); }
|
|
.qr-type-icon { font-size: 28px; margin-bottom: 12px; display: block; }
|
|
.qr-type-name { font-family: var(--font-display); font-size: 15px; font-weight: 700; margin-bottom: 6px; }
|
|
.qr-type-badge { font-family: var(--font-mono); font-size: 11px; color: var(--indigo); background: rgba(99,102,241,0.1); border: 1px solid rgba(99,102,241,0.2); padding: 2px 8px; border-radius: 4px; margin-bottom: 8px; display: inline-block; }
|
|
.qr-type-desc { font-size: 12px; color: var(--text-muted); }
|
|
|
|
/* PRICING */
|
|
.pricing-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; }
|
|
.pricing-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 28px 22px; position: relative; transition: var(--transition); }
|
|
.pricing-card:hover { border-color: var(--border-bright); transform: translateY(-4px); box-shadow: 0 20px 40px rgba(0,0,0,0.4); }
|
|
.pricing-card.featured { border-color: rgba(99,102,241,0.4); background: linear-gradient(135deg, rgba(99,102,241,0.08), rgba(139,92,246,0.04)); box-shadow: 0 0 0 1px rgba(99,102,241,0.1), 0 20px 40px rgba(99,102,241,0.08); }
|
|
.pricing-featured-badge { position: absolute; top: -12px; left: 50%; transform: translateX(-50%); background: linear-gradient(135deg, var(--indigo), var(--violet)); color: white; font-size: 11px; font-weight: 600; padding: 4px 14px; border-radius: 100px; white-space: nowrap; }
|
|
.pricing-tier { font-family: var(--font-mono); font-size: 11px; color: var(--indigo); letter-spacing: 0.1em; text-transform: uppercase; margin-bottom: 10px; }
|
|
.pricing-price { font-family: var(--font-display); font-size: 34px; font-weight: 800; margin-bottom: 4px; line-height: 1; }
|
|
.pricing-price .currency { font-size: 17px; font-weight: 400; color: var(--text-secondary); vertical-align: super; }
|
|
.pricing-price .period { font-size: 13px; font-weight: 400; color: var(--text-muted); }
|
|
.pricing-desc { font-size: 13px; color: var(--text-muted); margin-bottom: 20px; margin-top: 6px; }
|
|
.pricing-divider { border: none; border-top: 1px solid var(--border); margin: 18px 0; }
|
|
.pricing-features { list-style: none; display: flex; flex-direction: column; gap: 9px; margin-bottom: 22px; }
|
|
.pricing-feature { display: flex; align-items: flex-start; gap: 9px; font-size: 13px; color: var(--text-secondary); }
|
|
.pricing-feature::before { content: '✓'; color: var(--emerald); font-size: 12px; flex-shrink: 0; margin-top: 2px; }
|
|
.pricing-cta { width: 100%; text-align: center; padding: 10px; border-radius: var(--radius-sm); font-family: var(--font-body); font-size: 13px; font-weight: 600; cursor: pointer; text-decoration: none; display: block; transition: var(--transition); }
|
|
.pricing-cta-ghost { background: transparent; border: 1px solid var(--border); color: var(--text-secondary); }
|
|
.pricing-cta-ghost:hover { border-color: var(--border-bright); color: var(--text-primary); }
|
|
.pricing-cta-primary { background: linear-gradient(135deg, var(--indigo), var(--violet)); border: none; color: white; }
|
|
.pricing-cta-primary:hover { box-shadow: 0 0 20px rgba(99,102,241,0.4); transform: translateY(-1px); }
|
|
|
|
/* RESPONSE */
|
|
.response-section { background: rgba(13,13,26,0.5); }
|
|
.response-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; align-items: start; }
|
|
.response-features { display: flex; flex-direction: column; gap: 22px; }
|
|
.response-feature { display: flex; gap: 16px; }
|
|
.response-feature-icon { width: 40px; height: 40px; background: rgba(99,102,241,0.1); border: 1px solid rgba(99,102,241,0.2); border-radius: var(--radius-sm); display: flex; align-items: center; justify-content: center; font-size: 18px; flex-shrink: 0; }
|
|
.response-feature-title { font-family: var(--font-display); font-size: 15px; font-weight: 600; margin-bottom: 4px; }
|
|
.response-feature-desc { font-size: 13px; color: var(--text-secondary); line-height: 1.55; }
|
|
|
|
/* FINAL CTA */
|
|
.final-cta { padding: 120px 24px; text-align: center; position: relative; overflow: hidden; }
|
|
.final-cta::before { content: ''; position: absolute; width: 700px; height: 700px; border-radius: 50%; background: radial-gradient(circle, rgba(99,102,241,0.13) 0%, transparent 70%); left: 50%; top: 50%; transform: translate(-50%, -50%); pointer-events: none; }
|
|
.final-cta-title { font-family: var(--font-display); font-size: clamp(32px, 5vw, 52px); font-weight: 800; letter-spacing: -0.03em; margin-bottom: 16px; }
|
|
.final-cta-sub { font-size: 18px; color: var(--text-secondary); margin-bottom: 40px; font-weight: 300; }
|
|
|
|
/* FOOTER */
|
|
footer { padding: 28px 24px; border-top: 1px solid var(--border); }
|
|
.footer-inner { max-width: 1100px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 16px; }
|
|
.footer-links { display: flex; gap: 20px; }
|
|
.footer-link { font-size: 13px; color: var(--text-muted); text-decoration: none; transition: var(--transition); }
|
|
.footer-link:hover { color: var(--text-secondary); }
|
|
.footer-copy { font-size: 13px; color: var(--text-muted); }
|
|
|
|
/* ANIMATIONS */
|
|
@keyframes fadeUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
|
|
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
|
|
@keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.5; transform: scale(0.8); } }
|
|
.reveal { opacity: 0; transform: translateY(22px); transition: opacity 0.65s ease, transform 0.65s ease; }
|
|
.reveal.visible { opacity: 1; transform: translateY(0); }
|
|
|
|
/* RESPONSIVE */
|
|
@media (max-width: 900px) {
|
|
.hero-inner { grid-template-columns: 1fr; }
|
|
.terminal { display: none; }
|
|
.steps { grid-template-columns: 1fr; }
|
|
.step-arrow { display: none; }
|
|
.qr-types-grid { grid-template-columns: repeat(2, 1fr); }
|
|
.pricing-grid { grid-template-columns: repeat(2, 1fr); }
|
|
.response-grid { grid-template-columns: 1fr; }
|
|
}
|
|
@media (max-width: 600px) {
|
|
.nav-links { display: none; }
|
|
.qr-types-grid { grid-template-columns: repeat(2, 1fr); }
|
|
.pricing-grid { grid-template-columns: 1fr; }
|
|
.hero-stats { flex-wrap: wrap; gap: 20px; }
|
|
.tabs { width: 100%; }
|
|
.tab-btn { flex: 1; text-align: center; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- NAV -->
|
|
<nav>
|
|
<div class="nav-inner">
|
|
<a href="https://qrrapido.site" class="nav-logo">
|
|
<div class="nav-logo-icon">▦</div>
|
|
QR Rápido
|
|
<span class="nav-badge">MCP</span>
|
|
</a>
|
|
<div class="nav-links">
|
|
<a href="#how-it-works" class="nav-link">How it works</a>
|
|
<a href="#qr-types" class="nav-link">QR Types</a>
|
|
<a href="#pricing" class="nav-link">Pricing</a>
|
|
<a href="https://qrrapido.site/api/docs" class="nav-link">API Docs</a>
|
|
<a href="/mcp" class="lang-switch">🇧🇷 PT</a>
|
|
<a href="/Account/Login?returnUrl=/Developer?new=1" class="btn btn-primary">Start free →</a>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- HERO -->
|
|
<section class="hero">
|
|
<div class="hero-inner">
|
|
<div class="hero-content">
|
|
<div class="hero-tag">
|
|
<div class="hero-tag-dot"></div>
|
|
MCP · REST API · n8n · Make.com
|
|
</div>
|
|
<h1 class="hero-title">
|
|
QR codes for<br>
|
|
<span class="accent">AI agents</span><br>
|
|
and automations
|
|
</h1>
|
|
<p class="hero-subtitle">
|
|
Generate QR codes via MCP, REST API, or any HTTP client.
|
|
<strong style="color: var(--text-primary); font-weight: 500;">Inline base64 response</strong> —
|
|
no upload, no file hosting, ready to use instantly.
|
|
</p>
|
|
<div class="hero-actions">
|
|
<a href="/Account/Login?returnUrl=/Developer?new=1" class="btn btn-primary btn-lg">Start for free →</a>
|
|
<a href="https://qrrapido.site/api/docs" class="btn btn-ghost btn-lg">View API docs</a>
|
|
</div>
|
|
<div class="hero-stats">
|
|
<div class="stat">
|
|
<div class="stat-value"><0.4s</div>
|
|
<div class="stat-label">avg generation</div>
|
|
</div>
|
|
<div class="stat">
|
|
<div class="stat-value">8 types</div>
|
|
<div class="stat-label">of QR code</div>
|
|
</div>
|
|
<div class="stat">
|
|
<div class="stat-value">base64</div>
|
|
<div class="stat-label">inline, no hosting</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="terminal" id="hero-terminal">
|
|
<div class="terminal-header">
|
|
<div class="dot dot-r"></div><div class="dot dot-y"></div><div class="dot dot-g"></div>
|
|
<span class="terminal-title">claude — MCP Tool Call</span>
|
|
</div>
|
|
<div class="terminal-body" id="terminal-body"></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- INTEGRATIONS -->
|
|
<div class="integrations">
|
|
<div class="integrations-inner">
|
|
<span class="integrations-label">WORKS WITH</span>
|
|
<div class="integrations-list">
|
|
<div class="integration-badge"><span>🤖</span> Claude Desktop</div>
|
|
<div class="integration-badge"><span>🔄</span> n8n</div>
|
|
<div class="integration-badge"><span>⚙️</span> Make.com</div>
|
|
<div class="integration-badge"><span>⚡</span> Zapier</div>
|
|
<div class="integration-badge"><span>🔌</span> REST API</div>
|
|
<div class="integration-badge"><span>📡</span> Any HTTP client</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- HOW IT WORKS -->
|
|
<section class="section" id="how-it-works">
|
|
<div class="container">
|
|
<div class="reveal">
|
|
<div class="section-label">// how it works</div>
|
|
<h2 class="section-title">Three steps to start<br>generating</h2>
|
|
<p class="section-sub">From zero to QR codes in your agent in under 2 minutes. No credit card.</p>
|
|
</div>
|
|
<div class="steps reveal">
|
|
<div class="step">
|
|
<div class="step-number">01</div>
|
|
<div class="step-card">
|
|
<div class="step-icon">👤</div>
|
|
<div class="step-title">Create your account</div>
|
|
<p class="step-desc">Free login with Google or Microsoft. Free plan with 500 req/month included. No credit card required.</p>
|
|
</div>
|
|
<div class="step-arrow">→</div>
|
|
</div>
|
|
<div class="step">
|
|
<div class="step-number">02</div>
|
|
<div class="step-card">
|
|
<div class="step-icon">🔑</div>
|
|
<div class="step-title">Copy your API key</div>
|
|
<p class="step-desc">Your key is auto-generated on first login. Displayed prominently — copy with one click, snippet ready.</p>
|
|
</div>
|
|
<div class="step-arrow">→</div>
|
|
</div>
|
|
<div class="step">
|
|
<div class="step-number">03</div>
|
|
<div class="step-card">
|
|
<div class="step-icon">⚡</div>
|
|
<div class="step-title">Connect to your agent</div>
|
|
<p class="step-desc">Paste into Claude Desktop config, n8n HTTP node, or any client. Done — your agent generates QR codes.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- CODE SHOWCASE -->
|
|
<section class="section code-showcase">
|
|
<div class="container">
|
|
<div class="reveal">
|
|
<div class="section-label">// integration</div>
|
|
<h2 class="section-title">Connect however<br>you prefer</h2>
|
|
<p class="section-sub">Native MCP, REST API, or HTTP Request — the same API serves all clients.</p>
|
|
</div>
|
|
<div class="reveal">
|
|
<div class="tabs">
|
|
<button class="tab-btn active" onclick="switchTab(event,'mcp')">Claude MCP</button>
|
|
<button class="tab-btn" onclick="switchTab(event,'rest')">REST API</button>
|
|
<button class="tab-btn" onclick="switchTab(event,'n8n')">n8n</button>
|
|
</div>
|
|
|
|
<div class="tab-content active" id="tab-mcp">
|
|
<div class="code-window">
|
|
<div class="code-header">
|
|
<div class="code-header-left">
|
|
<div class="code-dots"><div class="dot dot-r"></div><div class="dot dot-y"></div><div class="dot dot-g"></div></div>
|
|
<span class="code-filename">claude_desktop_config.json</span>
|
|
</div>
|
|
<button class="copy-btn" onclick="copyCode('mcp-code', this)">copy</button>
|
|
</div>
|
|
<pre id="mcp-code"><span class="hl-comment">// ~/.config/claude/claude_desktop_config.json</span>
|
|
{
|
|
<span class="hl-key">"mcpServers"</span>: {
|
|
<span class="hl-key">"qrrapido"</span>: {
|
|
<span class="hl-key">"command"</span>: <span class="hl-string">"qrrapido-mcp"</span>,
|
|
<span class="hl-key">"env"</span>: {
|
|
<span class="hl-key">"QR_API_KEY"</span>: <span class="hl-string">"qr_your_key_here"</span>
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
<span class="hl-comment">// Claude Desktop now has access to tools:</span>
|
|
<span class="hl-comment">// → generate_qr(type, content, format, color, size)</span>
|
|
<span class="hl-comment">// → list_qr_history()</span>
|
|
<span class="hl-comment">// → get_qr_analytics(trackingId)</span></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tab-content" id="tab-rest">
|
|
<div class="code-window">
|
|
<div class="code-header">
|
|
<div class="code-header-left">
|
|
<div class="code-dots"><div class="dot dot-r"></div><div class="dot dot-y"></div><div class="dot dot-g"></div></div>
|
|
<span class="code-filename">terminal</span>
|
|
</div>
|
|
<button class="copy-btn" onclick="copyCode('rest-code', this)">copy</button>
|
|
</div>
|
|
<pre id="rest-code"><span class="hl-comment"># Generate a QR code via REST API</span>
|
|
curl -X POST <span class="hl-url">https://qrrapido.site/api/v1/QRManager/generate</span> \
|
|
-H <span class="hl-string">"X-API-Key: qr_your_key_here"</span> \
|
|
-H <span class="hl-string">"Content-Type: application/json"</span> \
|
|
-d '{
|
|
<span class="hl-key">"type"</span>: <span class="hl-string">"url"</span>,
|
|
<span class="hl-key">"content"</span>: <span class="hl-string">"https://my-store.com/product/123"</span>,
|
|
<span class="hl-key">"outputFormat"</span>: <span class="hl-string">"png"</span>,
|
|
<span class="hl-key">"size"</span>: <span class="hl-number">400</span>
|
|
}'
|
|
|
|
<span class="hl-comment"># Response: JSON with inline qrCodeBase64</span>
|
|
<span class="hl-comment"># No upload. No CDN. Ready to use.</span></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tab-content" id="tab-n8n">
|
|
<div class="code-window">
|
|
<div class="code-header">
|
|
<div class="code-header-left">
|
|
<div class="code-dots"><div class="dot dot-r"></div><div class="dot dot-y"></div><div class="dot dot-g"></div></div>
|
|
<span class="code-filename">n8n — HTTP Request Node</span>
|
|
</div>
|
|
</div>
|
|
<div class="n8n-mockup">
|
|
<div class="n8n-node">
|
|
<div class="n8n-node-header">
|
|
<div class="n8n-node-icon" style="background:rgba(255,102,0,0.15);border:1px solid rgba(255,102,0,0.3);">🔄</div>
|
|
<div class="n8n-node-title">HTTP Request</div>
|
|
</div>
|
|
<div class="n8n-field"><span class="n8n-field-label">Method</span><span class="n8n-field-value">POST</span></div>
|
|
<div class="n8n-field"><span class="n8n-field-label">URL</span><span class="n8n-field-value" style="color:#4ade80;font-size:11px;">https://qrrapido.site/api/v1/QRManager/generate</span></div>
|
|
<div class="n8n-field"><span class="n8n-field-label">Auth Type</span><span class="n8n-field-value">Header Auth</span></div>
|
|
<div class="n8n-field"><span class="n8n-field-label">Header Name</span><span class="n8n-field-value">X-API-Key</span></div>
|
|
<div class="n8n-field"><span class="n8n-field-label">Header Value</span><span class="n8n-field-value">qr_your_key_here</span></div>
|
|
<div class="n8n-field"><span class="n8n-field-label">Body (JSON)</span><span class="n8n-field-value">{"type":"url","content":"{{ $json.url }}"}</span></div>
|
|
</div>
|
|
<div class="n8n-node" style="background:rgba(16,185,129,0.05);border-color:rgba(16,185,129,0.15);">
|
|
<div class="n8n-node-header">
|
|
<div class="n8n-node-icon" style="background:rgba(16,185,129,0.15);border:1px solid rgba(16,185,129,0.3);">✓</div>
|
|
<div class="n8n-node-title" style="color:#10b981;">Output available</div>
|
|
</div>
|
|
<p style="font-size:12px;color:var(--text-muted);line-height:1.6;">
|
|
Use <code style="color:#a5f3fc;font-family:var(--font-mono);">{{ $json.qrCodeBase64 }}</code> in downstream nodes
|
|
to send via email, save to file, or return via webhook — no external URL needed.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- QR TYPES -->
|
|
<section class="section" id="qr-types">
|
|
<div class="container">
|
|
<div class="reveal">
|
|
<div class="section-label">// supported types</div>
|
|
<h2 class="section-title">8 QR code types,<br>one single endpoint</h2>
|
|
<p class="section-sub">Pass <code style="font-family:var(--font-mono);color:var(--indigo);font-size:15px;">"type"</code> in the payload and QR Rápido handles the correct format.</p>
|
|
</div>
|
|
<div class="qr-types-grid reveal">
|
|
<div class="qr-type-card"><span class="qr-type-icon">🔗</span><div class="qr-type-name">URL</div><div class="qr-type-badge">"url"</div><p class="qr-type-desc">Links, products, landing pages</p></div>
|
|
<div class="qr-type-card"><span class="qr-type-icon">💸</span><div class="qr-type-name">PIX</div><div class="qr-type-badge">"pix"</div><p class="qr-type-desc">Brazilian instant payments</p></div>
|
|
<div class="qr-type-card"><span class="qr-type-icon">📶</span><div class="qr-type-name">Wi-Fi</div><div class="qr-type-badge">"wifi"</div><p class="qr-type-desc">Wireless network credentials</p></div>
|
|
<div class="qr-type-card"><span class="qr-type-icon">👤</span><div class="qr-type-name">vCard</div><div class="qr-type-badge">"vcard"</div><p class="qr-type-desc">Digital business cards</p></div>
|
|
<div class="qr-type-card"><span class="qr-type-icon">💬</span><div class="qr-type-name">WhatsApp</div><div class="qr-type-badge">"whatsapp"</div><p class="qr-type-desc">Pre-filled messages</p></div>
|
|
<div class="qr-type-card"><span class="qr-type-icon">📧</span><div class="qr-type-name">Email</div><div class="qr-type-badge">"email"</div><p class="qr-type-desc">Direct email composition</p></div>
|
|
<div class="qr-type-card"><span class="qr-type-icon">📱</span><div class="qr-type-name">SMS</div><div class="qr-type-badge">"sms"</div><p class="qr-type-desc">Pre-defined SMS text</p></div>
|
|
<div class="qr-type-card"><span class="qr-type-icon">📝</span><div class="qr-type-name">Text</div><div class="qr-type-badge">"texto"</div><p class="qr-type-desc">Any free-form text content</p></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- RESPONSE FORMAT -->
|
|
<section class="section response-section">
|
|
<div class="container">
|
|
<div class="response-grid">
|
|
<div>
|
|
<div class="reveal">
|
|
<div class="section-label">// api response</div>
|
|
<h2 class="section-title">Inline base64.<br>No hosting.</h2>
|
|
<p class="section-sub">QR code returned directly in the JSON. Your agent receives it, uses it, moves on — no external URL, no upload.</p>
|
|
</div>
|
|
<div class="response-features reveal">
|
|
<div class="response-feature">
|
|
<div class="response-feature-icon">📦</div>
|
|
<div>
|
|
<div class="response-feature-title">Image in JSON</div>
|
|
<p class="response-feature-desc">Base64 embedded in the response. Agents use it directly — no dependency on external URLs or CDN.</p>
|
|
</div>
|
|
</div>
|
|
<div class="response-feature">
|
|
<div class="response-feature-icon">⚡</div>
|
|
<div>
|
|
<div class="response-feature-title">Automatic cache</div>
|
|
<p class="response-feature-desc">Identical QRs return from cache in milliseconds. <code style="font-family:var(--font-mono);color:var(--indigo);font-size:12px;">fromCache: true</code> signals a hit.</p>
|
|
</div>
|
|
</div>
|
|
<div class="response-feature">
|
|
<div class="response-feature-icon">🎯</div>
|
|
<div>
|
|
<div class="response-feature-title">3 output formats</div>
|
|
<p class="response-feature-desc">PNG, WebP (~40% smaller), or vector SVG. Choose per request via <code style="font-family:var(--font-mono);color:var(--indigo);font-size:12px;">outputFormat</code>.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="reveal">
|
|
<div class="code-window">
|
|
<div class="code-header">
|
|
<div class="code-header-left">
|
|
<div class="code-dots"><div class="dot dot-r"></div><div class="dot dot-y"></div><div class="dot dot-g"></div></div>
|
|
<span class="code-filename">response.json</span>
|
|
</div>
|
|
<button class="copy-btn" onclick="copyCode('response-code', this)">copy</button>
|
|
</div>
|
|
<pre id="response-code">{
|
|
<span class="hl-key">"success"</span>: <span class="hl-bool">true</span>,
|
|
<span class="hl-key">"qrCodeBase64"</span>: <span class="hl-string">"iVBORw0KGgoAAAANSUhEUgAA..."</span>,
|
|
<span class="hl-key">"qrId"</span>: <span class="hl-string">"6842a1f3b8c94d2e..."</span>,
|
|
<span class="hl-key">"format"</span>: <span class="hl-string">"png"</span>,
|
|
<span class="hl-key">"mimeType"</span>: <span class="hl-string">"image/png"</span>,
|
|
<span class="hl-key">"generationTimeMs"</span>: <span class="hl-number">312</span>,
|
|
<span class="hl-key">"fromCache"</span>: <span class="hl-bool">false</span>,
|
|
<span class="hl-key">"remainingCredits"</span>: <span class="hl-number">487</span>,
|
|
<span class="hl-key">"message"</span>: <span class="hl-string">"QR generated successfully"</span>
|
|
}</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- PRICING -->
|
|
<section class="section" id="pricing">
|
|
<div class="container">
|
|
<div class="reveal" style="text-align:center;margin-bottom:60px;">
|
|
<div class="section-label">// plans</div>
|
|
<h2 class="section-title">Start free.<br>Scale when you need.</h2>
|
|
<p class="section-sub" style="margin:0 auto;">No credit card to start. Upgrade anytime.</p>
|
|
</div>
|
|
<div class="pricing-grid reveal">
|
|
<div class="pricing-card">
|
|
<div class="pricing-tier">FREE</div>
|
|
<div class="pricing-price">Free</div>
|
|
<p class="pricing-desc">For testing and small projects</p>
|
|
<hr class="pricing-divider">
|
|
<ul class="pricing-features">
|
|
<li class="pricing-feature">500 req/month</li>
|
|
<li class="pricing-feature">10 req/minute</li>
|
|
<li class="pricing-feature">All 8 QR types</li>
|
|
<li class="pricing-feature">PNG, WebP, SVG</li>
|
|
<li class="pricing-feature">Automatic cache</li>
|
|
</ul>
|
|
<a href="/Account/Login?returnUrl=/Developer?new=1" class="pricing-cta pricing-cta-ghost">Start free</a>
|
|
</div>
|
|
<div class="pricing-card">
|
|
<div class="pricing-tier">STARTER</div>
|
|
<div class="pricing-price"><span class="currency">R$</span>39<span class="period">/mo</span></div>
|
|
<p class="pricing-desc">For automations and workflows</p>
|
|
<hr class="pricing-divider">
|
|
<ul class="pricing-features">
|
|
<li class="pricing-feature">10,000 req/month</li>
|
|
<li class="pricing-feature">50 req/minute</li>
|
|
<li class="pricing-feature">All 8 QR types</li>
|
|
<li class="pricing-feature">PNG, WebP, SVG</li>
|
|
<li class="pricing-feature">Email support</li>
|
|
</ul>
|
|
<a href="https://qrrapido.site/Developer/Pricing" class="pricing-cta pricing-cta-ghost">Get Starter</a>
|
|
</div>
|
|
<div class="pricing-card featured">
|
|
<div class="pricing-featured-badge">Most popular</div>
|
|
<div class="pricing-tier">PRO</div>
|
|
<div class="pricing-price"><span class="currency">R$</span>119<span class="period">/mo</span></div>
|
|
<p class="pricing-desc">For agents and production products</p>
|
|
<hr class="pricing-divider">
|
|
<ul class="pricing-features">
|
|
<li class="pricing-feature">100,000 req/month</li>
|
|
<li class="pricing-feature">200 req/minute</li>
|
|
<li class="pricing-feature">Dynamic QR + tracking</li>
|
|
<li class="pricing-feature">Scan analytics</li>
|
|
<li class="pricing-feature">Priority support</li>
|
|
</ul>
|
|
<a href="https://qrrapido.site/Developer/Pricing" class="pricing-cta pricing-cta-primary">Get Pro</a>
|
|
</div>
|
|
<div class="pricing-card">
|
|
<div class="pricing-tier">BUSINESS</div>
|
|
<div class="pricing-price"><span class="currency">R$</span>349<span class="period">/mo</span></div>
|
|
<p class="pricing-desc">For high volume and teams</p>
|
|
<hr class="pricing-divider">
|
|
<ul class="pricing-features">
|
|
<li class="pricing-feature">500,000 req/month</li>
|
|
<li class="pricing-feature">500 req/minute</li>
|
|
<li class="pricing-feature">Dynamic QR + tracking</li>
|
|
<li class="pricing-feature">5 simultaneous API keys</li>
|
|
<li class="pricing-feature">SLA + dedicated support</li>
|
|
</ul>
|
|
<a href="https://qrrapido.site/Developer/Pricing" class="pricing-cta pricing-cta-ghost">Get Business</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- FINAL CTA -->
|
|
<section class="final-cta">
|
|
<div class="container">
|
|
<div class="reveal">
|
|
<h2 class="final-cta-title">
|
|
Up and running in<br>
|
|
<span style="background:linear-gradient(135deg,var(--indigo),var(--violet));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;">2 minutes</span>
|
|
</h2>
|
|
<p class="final-cta-sub">Free login. Key auto-generated. Snippet ready to copy.</p>
|
|
<a href="/Account/Login?returnUrl=/Developer?new=1" class="btn btn-primary btn-lg">Create free account →</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- FOOTER -->
|
|
<footer>
|
|
<div class="footer-inner">
|
|
<a href="https://qrrapido.site" class="nav-logo" style="font-size:15px;">
|
|
<div class="nav-logo-icon" style="width:26px;height:26px;font-size:13px;">▦</div>
|
|
QR Rápido
|
|
</a>
|
|
<div class="footer-links">
|
|
<a href="https://qrrapido.site/api/docs" class="footer-link">API Docs</a>
|
|
<a href="https://qrrapido.site/Developer/Pricing" class="footer-link">Pricing</a>
|
|
<a href="https://qrrapido.site/pt-BR/Home/TermosDeUso" class="footer-link">Terms</a>
|
|
<a href="https://qrrapido.site/pt-BR/Home/PoliticaDePrivacidade" class="footer-link">Privacy</a>
|
|
<a href="/mcp" class="footer-link">🇧🇷 Versão PT</a>
|
|
</div>
|
|
<p class="footer-copy">© 2025 QR Rápido · Made in Brazil 🇧🇷</p>
|
|
</div>
|
|
</footer>
|
|
|
|
<script>
|
|
const lines = [
|
|
{ html: '<span class="t-prompt">→</span> Tool: <span class="t-tool">generate_qr</span>', delay: 400 },
|
|
{ html: '', delay: 600 },
|
|
{ html: ' <span class="t-key">type</span>: <span class="t-string">"url"</span>', delay: 800 },
|
|
{ html: ' <span class="t-key">content</span>: <span class="t-string">"https://my-store.com"</span>', delay: 1050 },
|
|
{ html: ' <span class="t-key">format</span>: <span class="t-string">"png"</span>', delay: 1300 },
|
|
{ html: ' <span class="t-key">size</span>: <span class="t-value">400</span>', delay: 1550 },
|
|
{ html: '', delay: 1750 },
|
|
{ html: '<span class="t-dim">⠸ Generating QR code...</span>', delay: 1950 },
|
|
{ html: '', delay: 2800 },
|
|
{ html: '<span class="t-success">✓ Done in 312ms</span>', delay: 3000 },
|
|
{ html: '', delay: 3200 },
|
|
{ html: ' <span class="t-key">qrCodeBase64</span>: <span class="t-string">"iVBORw0KGgo..."</span>', delay: 3400 },
|
|
{ html: ' <span class="t-key">fromCache</span>: <span class="t-value">false</span>', delay: 3600 },
|
|
{ html: ' <span class="t-key">generationTimeMs</span>: <span class="t-value">312</span>', delay: 3800 },
|
|
{ html: ' <span class="t-key">remainingCredits</span>: <span class="t-value">487</span>', delay: 4000 },
|
|
{ html: '', delay: 4200 },
|
|
{ html: '<span class="t-comment">// inline base64 — no hosting, no upload</span>', delay: 4400 },
|
|
];
|
|
|
|
function animateTerminal() {
|
|
const body = document.getElementById('terminal-body');
|
|
if (!body) return;
|
|
lines.forEach((line, i) => {
|
|
setTimeout(() => {
|
|
const el = document.createElement('span');
|
|
el.className = 'terminal-line';
|
|
el.innerHTML = line.html || ' ';
|
|
body.appendChild(el);
|
|
requestAnimationFrame(() => { el.style.opacity = '1'; });
|
|
if (i === lines.length - 1) {
|
|
setTimeout(() => {
|
|
const cursor = document.createElement('span');
|
|
cursor.className = 'cursor';
|
|
body.appendChild(cursor);
|
|
}, 300);
|
|
}
|
|
}, line.delay);
|
|
});
|
|
}
|
|
|
|
function switchTab(e, tab) {
|
|
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
|
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
|
|
e.target.classList.add('active');
|
|
document.getElementById('tab-' + tab).classList.add('active');
|
|
}
|
|
|
|
function copyCode(id, btn) {
|
|
const text = document.getElementById(id).innerText;
|
|
navigator.clipboard.writeText(text).then(() => {
|
|
btn.textContent = 'copied!';
|
|
btn.style.color = 'var(--emerald)';
|
|
setTimeout(() => { btn.textContent = 'copy'; btn.style.color = ''; }, 2000);
|
|
});
|
|
}
|
|
|
|
const observer = new IntersectionObserver(entries => {
|
|
entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('visible'); });
|
|
}, { threshold: 0.08 });
|
|
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
|
|
|
|
window.addEventListener('load', animateTerminal);
|
|
</script>
|
|
</body>
|
|
</html>
|