diff --git a/apps/landing-page/components/footer.jsx b/apps/landing-page/components/footer.jsx index 46ca3c0..a43cc46 100644 --- a/apps/landing-page/components/footer.jsx +++ b/apps/landing-page/components/footer.jsx @@ -6,7 +6,13 @@ function Footer() {
PoliHack v19 · BMW · AppDev with AI · "Applications that encourage AI adoption"
-
© 2026 LearnLoop
+
+ GitHub + · + MIT + · + © 2026 LearnLoop +
); diff --git a/apps/landing-page/components/hero.jsx b/apps/landing-page/components/hero.jsx index 2e533d1..650453f 100644 --- a/apps/landing-page/components/hero.jsx +++ b/apps/landing-page/components/hero.jsx @@ -17,7 +17,7 @@ function Hero() { - Closed beta · 2026 + Open source · MIT · self-hosted @@ -34,8 +34,8 @@ function Hero() {

{/* CTA */} -
- +
+
{/* credential cards */} @@ -61,139 +61,33 @@ function Hero() { ); } -function WaitlistButton() { - const [open, setOpen] = useState(false); +// There is no hosted product or waitlist: LearnLoop is open source and +// self-hosted. The CTAs point at the code, the self-hosting guide and the +// walkthrough video. + +function CtaLinks() { return ( <> - - {open && setOpen(false)} />} - - ); -} - -function WaitlistModal({ onClose }) { - const [email, setEmail] = useState(''); - const [state, setState] = useState('idle'); // idle | submitting | done | error - const [error, setError] = useState(''); - const inputRef = useRef(null); - - const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim()); - - useEffect(() => { - const onKey = (e) => { if (e.key === 'Escape') onClose(); }; - window.addEventListener('keydown', onKey); - document.body.style.overflow = 'hidden'; - setTimeout(() => inputRef.current?.focus(), 50); - return () => { - window.removeEventListener('keydown', onKey); - document.body.style.overflow = ''; - }; - }, [onClose]); - - function onSubmit(e) { - e.preventDefault(); - if (!valid || state === 'submitting') return; - setState('submitting'); - setError(''); - try { - const list = JSON.parse(localStorage.getItem('learnloop_waitlist') || '[]'); - if (!list.includes(email.trim())) list.push(email.trim()); - localStorage.setItem('learnloop_waitlist', JSON.stringify(list)); - setTimeout(() => setState('done'), 600); - } catch (err) { - setState('error'); - setError('Something went wrong. Try again.'); - } - } - - return ( -
-
e.stopPropagation()} + View on GitHub → + + - - - {state === 'done' ? ( -
-
- - - -
-
You're on the list.
-

- We'll email {email.trim()} when closed beta opens up a spot. -

- -
- ) : ( -
-
- - Closed beta -
-
- Join the waitlist -
-

- We'll email you when your team's invite is ready. -

- - setEmail(e.target.value)} - placeholder="you@team.com" - className="mt-5 w-full rounded-md border hairline-strong bg-paper-50 px-3.5 py-3 text-ink-900 placeholder:text-ink-400 font-mono text-[13px] focus:outline-none focus:ring-2 focus:ring-[#1FA29A]/30 focus:border-[#1FA29A]" - /> - - {error &&
{error}
} -
- No spam. One email when your invite is ready. -
-
- )} -
-
+ Self-host it + + + Watch the 3-min demo + + ); } diff --git a/apps/landing-page/components/nav.jsx b/apps/landing-page/components/nav.jsx index 0dc67c7..33127b4 100644 --- a/apps/landing-page/components/nav.jsx +++ b/apps/landing-page/components/nav.jsx @@ -10,6 +10,7 @@ function Nav() { ['Problem', '#problem'], ['Solution', '#features'], ['Demo', '#demo'], + ['GitHub', REPO_URL], ]; return ( diff --git a/apps/landing-page/components/primitives.jsx b/apps/landing-page/components/primitives.jsx index 0782c44..f770fa4 100644 --- a/apps/landing-page/components/primitives.jsx +++ b/apps/landing-page/components/primitives.jsx @@ -1,6 +1,8 @@ /* Shared primitives */ const { useState, useEffect, useRef, useMemo, useCallback } = React; +const REPO_URL = 'https://github.com/Bogzx/LearnLoop'; + function Logomark({ size = 32, className = '' }) { const w = size * 1.6; return ( @@ -96,4 +98,4 @@ function Container({ children, className = '' }) { return
{children}
; } -Object.assign(window, { Logomark, Wordmark, SectionKicker, SectionTitle, Reveal, LiveDot, Orbs, Container }); +Object.assign(window, { REPO_URL, Logomark, Wordmark, SectionKicker, SectionTitle, Reveal, LiveDot, Orbs, Container }); diff --git a/apps/landing-page/components/sections.jsx b/apps/landing-page/components/sections.jsx index ac378c4..1afb578 100644 --- a/apps/landing-page/components/sections.jsx +++ b/apps/landing-page/components/sections.jsx @@ -1,4 +1,4 @@ -// Problem · Solution · Features · Demo +// Problem · Features · Demo function Problem() { return ( @@ -11,8 +11,8 @@ function Problem() { It's that teams don't know how to use it together.

- Today, almost every developer uses AI individually — but fewer than 25% know how to integrate - it into how their team actually works. The result: inconsistent prompts, lost context, + Most developers use AI on their own. What one person learns about prompting the + team's codebase rarely reaches anyone else. The result: inconsistent prompts, lost context, and slow onboarding.

@@ -21,92 +21,34 @@ function Problem() { ); } -function Solution() { - const pillars = [ - { - n:'01', - title:'Standardizes every prompt to your team', - body:"A 5-dimension scorecard runs live under every textarea — goal, specificity, context, constraints, output. Below 7/10? It rewrites the prompt in your team's voice, with your team's conventions, before you hit send.", - tone:'#2BC68A', - }, - { - n:'02', - title:"Loads the knowledge base into the LLM's context", - body:"Your repo's wiki — patterns, conventions, past decisions — is injected into the model's context automatically, scoped to the file you're working on. No more pasting links or re-explaining the codebase.", - tone:'#3D8BFF', - }, - { - n:'03', - title:"Auto-updates with every member's contribution", - body:"Every prompt that scores 9+/10 is auto-deduped into the team wiki, then becomes context for the next person who asks something similar. The team's tribal knowledge compounds — without anyone writing docs.", - tone:'#9C7AD9', - }, - { - n:'04', - title:'Functional wherever you use AI', - body:'Same coach, same context, same wiki — across Browser (Claude.ai · ChatGPT · Gemini), VS Code, and CLI / MCP (Claude Code · Copilot). The connective tissue across the surfaces your team already uses.', - tone:'#E5A24F', - }, - ]; - - return ( -
- - -
- The solution - - LearnLoop — your team's prompts, standardized and shared. - -
- -
- {pillars.map(p => ( -
-
-
{p.n}
- -
-

- {p.title} -

-

{p.body}

-
- ))} -
-
-
- ); -} - function Features() { const features = [ { tag: 'capability 01', title: "Standardizes every prompt to your team's voice", - body: <>A live 5-dimension scorecard runs under every textarea — goal · specificity · context · constraints · output. Below 7/10, LearnLoop rewrites the prompt using your team's conventions, the file you're in, and patterns from past wins. The vague "fix the retry" becomes a precise spec the model can actually answer., + body: <>Every prompt is scored when you send it, on goal · specificity · context · constraints · output. Below 7/10 the send is held and you choose: Improve (a short Q&A that rewrites the prompt with your team's conventions), send as-is, or edit. Nothing is sent without you. The vague "fix the retry" becomes a spec the model can actually answer., backed: 'apps/browser-ext + apps/vscode-ext · POST /score', mock: , }, { tag: 'capability 02', title: "Loads your knowledge base into the LLM's context", - body: <>Your repo's wiki — patterns, conventions, past decisions, hard-won bug fixes — is injected into the model's context automatically, scoped to the file you're editing. Stop pasting links into chat. Stop re-explaining your codebase. The model already knows., + body: <>Your repo's wiki — patterns, conventions, past decisions, hard-won bug fixes — goes into the model's context for the part of the codebase you're working in: the MCP server looks it up per file, and the browser extension prepends it for the wiki node you pick. Stop pasting links into chat and re-explaining your codebase., backed: 'GET /examples · GET /wiki · apps/api', mock: , }, { tag: 'capability 03', title: "Auto-updates with every member's contribution", - body: <>Every prompt that scores 9+/10 is auto-deduped into the team wiki, then becomes context for the next person who asks something similar. Team tribal knowledge compounds — without anyone writing docs, holding meetings, or onboarding the new hire by hand., + body: <>Strong prompts join the team's prompt library (mean score ≥ 7, no dimension below 5, confirmed by a second independent score), and conventions people state are deduplicated into the wiki, becoming durable after three mentions. Both become context for the next person who works in the same place., backed: 'wiki_save · POST /wiki/propose', mock: , }, { tag: 'everywhere', title: 'Functional wherever you use AI', - body: <>Same coach. Same context. Same wiki. Whether the dev is in Claude.ai brainstorming, VS Code shipping, or Claude Code / Copilot CLI automating — LearnLoop is the connective tissue across the surfaces your team already uses., - backed: 'browser-ext · vscode-ext · mcp-server (4 tools)', + body: <>Same coach. Same context. Same wiki. Whether the dev is in Claude.ai brainstorming, VS Code shipping, or Claude Code / Copilot Chat automating — LearnLoop is the connective tissue across the surfaces your team already uses., + backed: 'browser-ext · vscode-ext · mcp-server (5 tools)', mock: , }, ]; @@ -174,9 +116,9 @@ function FeatureCard({ f, wide, stacked }) { function SurfacesMock() { const surfaces = [ - { name:'Browser', sub:'Claude.ai · ChatGPT · Gemini', tone:'#2BC68A' }, - { name:'VS Code', sub:'extension · ⌘⇧K scaffold', tone:'#3D8BFF' }, - { name:'CLI / MCP', sub:'Claude Code · Copilot', tone:'#9C7AD9' }, + { name:'Browser', sub:'Claude.ai (Chrome)', tone:'#2BC68A' }, + { name:'VS Code', sub:'sidebar score card', tone:'#3D8BFF' }, + { name:'CLI / MCP', sub:'Claude Code · Copilot Chat', tone:'#9C7AD9' }, ]; return (
@@ -209,22 +151,6 @@ function ScorecardMock() { ); } -function ScaffoldMock() { - return ( -
-
⌘⇧K
-
- {[['goal','reduce p99 latency'],['ctx','src/api/handlers/'],['out','only modified function']].map(([l,v]) => ( -
-
{l}
-
{v}
-
- ))} -
-
- ); -} - function ExamplesMock() { return (
@@ -268,19 +194,6 @@ function WikiMock() { ); } -function MCPMock() { - return ( -
- {['wiki_search','wiki_save','prompt_score','team_examples'].map(t => ( -
- - {t} -
- ))} -
- ); -} - function Demo() { return (
@@ -313,72 +226,6 @@ function Demo() { ); } -function SkillArcMini() { - const lines = [ - { c:'#2BC68A', d:'M10 90 L 60 78 L 110 60 L 160 42 L 220 28' }, - { c:'#3D8BFF', d:'M10 96 L 60 88 L 110 76 L 160 60 L 220 42' }, - { c:'#9C7AD9', d:'M10 100 L 60 92 L 110 80 L 160 68 L 220 50' }, - { c:'#E5A24F', d:'M10 104 L 60 100 L 110 92 L 160 80 L 220 64' }, - { c:'#E26F95', d:'M10 108 L 60 104 L 110 96 L 160 86 L 220 72' }, - ]; - return ( - - {[30,60,90,120].map(y => )} - {lines.map((l,i) => )} - {lines.map((l,i) => { - const x = 220, y = parseFloat(l.d.split('L').pop().trim().split(' ')[1]); - return ; - })} - - ); -} - -function TeamMetricsMini() { - return ( -
- {[ - ['Avg score','6.8','+0.7'], - ['Reuse','71%','+24%'], - ['Durable','23','+9'], - ['Active','12/14','+3'], - ].map(([l,v,d]) => ( -
-
{l}
-
{v}
-
{d}
-
- ))} -
- ); -} - -function WikiTreeMini() { - const nodes = [ - {x:30,y:65,label:'.prompts'}, - {x:120,y:30,label:'patterns'}, - {x:120,y:65,label:'webhook'}, - {x:120,y:100,label:'api'}, - {x:200,y:30,label:'retry'}, - {x:200,y:65,label:'idemp'}, - {x:200,y:100,label:'errors'}, - ]; - return ( - - - - - - {nodes.map((n,i) => ( - - - {n.label} - - ))} - - ); -} - window.Problem = Problem; -window.Solution = Solution; window.Features = Features; window.Demo = Demo;