-
-
Notifications
You must be signed in to change notification settings - Fork 6
feat(docs): randomize hero terminal demo across integrations #263
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,15 +3,95 @@ import Link from '@docusaurus/Link'; | |||||||||||||||||||||||||||||||
| import useBaseUrl from '@docusaurus/useBaseUrl'; | ||||||||||||||||||||||||||||||||
| import styles from './styles.module.css'; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| interface DemoScenario { | ||||||||||||||||||||||||||||||||
| id: 'figma' | 'github' | 'linear' | 'notion'; | ||||||||||||||||||||||||||||||||
| tagline: [string, string]; | ||||||||||||||||||||||||||||||||
| subtitle: string; | ||||||||||||||||||||||||||||||||
| command: string; | ||||||||||||||||||||||||||||||||
| outputLines: string[]; | ||||||||||||||||||||||||||||||||
| successLine: string; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const DEMO_SCENARIOS: DemoScenario[] = [ | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| id: 'figma', | ||||||||||||||||||||||||||||||||
| tagline: ['Design in Figma.', 'Ship pixel-perfect code.'], | ||||||||||||||||||||||||||||||||
| subtitle: | ||||||||||||||||||||||||||||||||
| 'Point ralph-starter at a Figma file and get production-ready components with visual validation.', | ||||||||||||||||||||||||||||||||
| command: 'ralph-starter figma', | ||||||||||||||||||||||||||||||||
| outputLines: [ | ||||||||||||||||||||||||||||||||
| ' Figma to Code', | ||||||||||||||||||||||||||||||||
| '? Figma URL: https://figma.com/design/ABC123/Dashboard', | ||||||||||||||||||||||||||||||||
| '? Build: responsive dashboard with sidebar nav', | ||||||||||||||||||||||||||||||||
| '? Stack: Next.js + TypeScript + Tailwind CSS', | ||||||||||||||||||||||||||||||||
| '\u2192 Fetching Figma API... 8 frames, 21 components', | ||||||||||||||||||||||||||||||||
| '\u2192 Visual validation: 98.2% pixel match', | ||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||
| successLine: '\u2713 Done! Cost: $0.94 | 3 commits', | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| id: 'github', | ||||||||||||||||||||||||||||||||
| tagline: ['Specs on GitHub.', 'Code ships itself.'], | ||||||||||||||||||||||||||||||||
| subtitle: | ||||||||||||||||||||||||||||||||
| 'Point ralph-starter at a GitHub issue and get a fully implemented feature with tests and a PR.', | ||||||||||||||||||||||||||||||||
| command: 'ralph-starter run --from github --issue 42', | ||||||||||||||||||||||||||||||||
| outputLines: [ | ||||||||||||||||||||||||||||||||
| '\u2192 Fetching GitHub issue #42...', | ||||||||||||||||||||||||||||||||
| ' Found: "Add user authentication"', | ||||||||||||||||||||||||||||||||
| ' Labels: feature, auth', | ||||||||||||||||||||||||||||||||
| '\u2192 Loop 1/5: Generating auth module...', | ||||||||||||||||||||||||||||||||
| '\u2192 Loop 2/5: Adding tests and validation...', | ||||||||||||||||||||||||||||||||
| '\u2192 Validation passed: 12 tests, lint clean', | ||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||
| successLine: '\u2713 Done! Cost: $0.38 | PR #87 created', | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| id: 'linear', | ||||||||||||||||||||||||||||||||
| tagline: ['Tickets in Linear.', 'Features in production.'], | ||||||||||||||||||||||||||||||||
| subtitle: | ||||||||||||||||||||||||||||||||
| 'Pull Linear tickets and let AI agents implement them end-to-end with automatic commits.', | ||||||||||||||||||||||||||||||||
| command: 'ralph-starter run --from linear --label ready', | ||||||||||||||||||||||||||||||||
| outputLines: [ | ||||||||||||||||||||||||||||||||
| '\u2192 Fetching Linear issues (ready)...', | ||||||||||||||||||||||||||||||||
| ' Found 4 issues: RAL-41, RAL-42, RAL-43, RAL-44', | ||||||||||||||||||||||||||||||||
| '\u2192 Processing RAL-42: "Dark mode toggle"', | ||||||||||||||||||||||||||||||||
| '\u2192 Loop 1/8: Implementing theme provider...', | ||||||||||||||||||||||||||||||||
| '\u2192 Loop 2/8: Adding CSS variables...', | ||||||||||||||||||||||||||||||||
| '\u2192 Validation passed: build clean', | ||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||
| successLine: '\u2713 Done! Cost: $0.52 | 5 commits', | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| id: 'notion', | ||||||||||||||||||||||||||||||||
| tagline: ['Specs in Notion.', 'Code writes itself.'], | ||||||||||||||||||||||||||||||||
| subtitle: | ||||||||||||||||||||||||||||||||
| 'Import requirements from Notion pages and let AI agents turn them into production-ready code.', | ||||||||||||||||||||||||||||||||
| command: 'ralph-starter run --from notion --project "API Spec"', | ||||||||||||||||||||||||||||||||
| outputLines: [ | ||||||||||||||||||||||||||||||||
| '\u2192 Fetching Notion page: "API Spec"...', | ||||||||||||||||||||||||||||||||
| ' Parsed: 3 sections, 12 endpoints', | ||||||||||||||||||||||||||||||||
| '\u2192 Loop 1/6: Scaffolding Express routes...', | ||||||||||||||||||||||||||||||||
| '\u2192 Loop 2/6: Adding middleware & validation...', | ||||||||||||||||||||||||||||||||
| '\u2192 Loop 3/6: Writing integration tests...', | ||||||||||||||||||||||||||||||||
| '\u2192 Validation passed: 18 tests, lint clean', | ||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||
| successLine: '\u2713 Done! Cost: $0.61 | 4 commits', | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| export default function HeroSection(): React.ReactElement { | ||||||||||||||||||||||||||||||||
| const [isVisible, setIsVisible] = useState(false); | ||||||||||||||||||||||||||||||||
| const [copied, setCopied] = useState(false); | ||||||||||||||||||||||||||||||||
| const [scenarioIndex, setScenarioIndex] = useState(0); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||
| setScenarioIndex(Math.floor(Math.random() * DEMO_SCENARIOS.length)); | ||||||||||||||||||||||||||||||||
| const timer = setTimeout(() => setIsVisible(true), 100); | ||||||||||||||||||||||||||||||||
| return () => clearTimeout(timer); | ||||||||||||||||||||||||||||||||
| }, []); | ||||||||||||||||||||||||||||||||
|
Comment on lines
+85
to
91
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SSR hydration mismatch from
Suggested change
Prompt To Fix With AIThis is a comment left during a code review.
Path: docs/src/components/HeroSection/index.tsx
Line: 85-91
Comment:
**SSR hydration mismatch from `Math.random()` in `useEffect`**
`scenarioIndex` is initialised to `0` (Figma) at render time, and then `setScenarioIndex(Math.floor(Math.random() * …))` runs in `useEffect` after hydration. This is intentional for SSR/SEO (Figma is the safe default), but the two `setScenarioIndex` + `setTimeout` calls share one `useEffect` and only the timer is cleaned up. If the component were ever unmounted before the effect runs the `setScenarioIndex` call would still execute, causing a state-update-on-unmounted-component warning. Consider splitting them into two separate effects, or guarding with a ref:
```suggestion
useEffect(() => {
setScenarioIndex(Math.floor(Math.random() * DEMO_SCENARIOS.length));
}, []);
useEffect(() => {
const timer = setTimeout(() => setIsVisible(true), 100);
return () => clearTimeout(timer);
}, []);
```
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const scenario = DEMO_SCENARIOS[scenarioIndex]; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const handleCopy = useCallback(async () => { | ||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||
| await navigator.clipboard.writeText('npx ralph-starter'); | ||||||||||||||||||||||||||||||||
|
|
@@ -50,12 +130,16 @@ export default function HeroSection(): React.ReactElement { | |||||||||||||||||||||||||||||||
| {/* Left: Text content */} | ||||||||||||||||||||||||||||||||
| <div className={styles.heroContent}> | ||||||||||||||||||||||||||||||||
| <h1 className={`${styles.tagline} ${styles.animateIn} ${styles.delay1}`}> | ||||||||||||||||||||||||||||||||
| Get specs from anywhere.<br /> | ||||||||||||||||||||||||||||||||
| Run AI loops<br /> from zero to prod. | ||||||||||||||||||||||||||||||||
| {scenario.tagline[0]}<br /> | ||||||||||||||||||||||||||||||||
| {scenario.tagline[1]} | ||||||||||||||||||||||||||||||||
| </h1> | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| <p className={`${styles.subtitle} ${styles.animateIn} ${styles.delay2}`}> | ||||||||||||||||||||||||||||||||
| Connect your tools, fetch requirements, and let AI agents build production-ready code automatically. | ||||||||||||||||||||||||||||||||
| {scenario.subtitle} Also works with { | ||||||||||||||||||||||||||||||||
| ['Figma', 'GitHub', 'Linear', 'Notion'] | ||||||||||||||||||||||||||||||||
| .filter(name => name.toLowerCase() !== scenario.id) | ||||||||||||||||||||||||||||||||
| .join(', ') | ||||||||||||||||||||||||||||||||
| } specs. | ||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||
|
Comment on lines
+138
to
143
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Subtitle "Also works with" text not updated during SSR On the server, However, the filter comparison uses Prompt To Fix With AIThis is a comment left during a code review.
Path: docs/src/components/HeroSection/index.tsx
Line: 138-143
Comment:
**Subtitle "Also works with" text not updated during SSR**
On the server, `scenarioIndex` is `0` (Figma), so the subtitle correctly says "Also works with GitHub, Linear, Notion specs." After the client-side `useEffect` fires and picks a different scenario, the subtitle switches. This is fine for the crawlable SSR output.
However, the filter comparison uses `name.toLowerCase() !== scenario.id`. The IDs are already lowercase (`'figma'`, `'github'`, etc.) and the display names are title-case (`'Figma'`, `'GitHub'`, etc.). `'GitHub'.toLowerCase()` → `'github'` matches correctly, so the logic is sound. No bug here, just worth a note that this implicit casing convention should be kept consistent if new integrations are added.
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| {/* CTA row: button + install command + integrations */} | ||||||||||||||||||||||||||||||||
|
|
@@ -105,13 +189,13 @@ export default function HeroSection(): React.ReactElement { | |||||||||||||||||||||||||||||||
| <div className={styles.terminalBody}> | ||||||||||||||||||||||||||||||||
| <div className={styles.terminalLine}> | ||||||||||||||||||||||||||||||||
| <span className={styles.terminalPrompt}>$</span> | ||||||||||||||||||||||||||||||||
| <span className={styles.terminalCommand}> ralph-starter run "build a todo app" --commit</span> | ||||||||||||||||||||||||||||||||
| <span className={styles.terminalCommand}> {scenario.command}</span> | ||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||
| <div className={styles.terminalOutput}> | ||||||||||||||||||||||||||||||||
| <div>✓ Loop 1: Analyzing requirements...</div> | ||||||||||||||||||||||||||||||||
| <div>✓ Loop 2: Creating components...</div> | ||||||||||||||||||||||||||||||||
| <div>✓ Loop 3: Adding tests...</div> | ||||||||||||||||||||||||||||||||
| <div className={styles.terminalSuccess}>✓ Done! Cost: $0.42 | 3 commits created</div> | ||||||||||||||||||||||||||||||||
| {scenario.outputLines.map((line, i) => ( | ||||||||||||||||||||||||||||||||
| <div key={i}>{line}</div> | ||||||||||||||||||||||||||||||||
| ))} | ||||||||||||||||||||||||||||||||
| <div className={styles.terminalSuccess}>{scenario.successLine}</div> | ||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||
|
|
@@ -120,34 +204,20 @@ export default function HeroSection(): React.ReactElement { | |||||||||||||||||||||||||||||||
| <div className={styles.integrations}> | ||||||||||||||||||||||||||||||||
| <span className={styles.integrationLabel}>Integrations</span> | ||||||||||||||||||||||||||||||||
| <div className={styles.integrationLogos}> | ||||||||||||||||||||||||||||||||
| <Link to="/docs/sources/github" className={styles.integrationLink}> | ||||||||||||||||||||||||||||||||
| <img | ||||||||||||||||||||||||||||||||
| src={useBaseUrl('/img/github logo.webp')} | ||||||||||||||||||||||||||||||||
| alt="GitHub" | ||||||||||||||||||||||||||||||||
| className={styles.integrationLogo} | ||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||
| </Link> | ||||||||||||||||||||||||||||||||
| <Link to="/docs/sources/figma" className={styles.integrationLink}> | ||||||||||||||||||||||||||||||||
| <img | ||||||||||||||||||||||||||||||||
| src={useBaseUrl('/img/figma-logo.svg')} | ||||||||||||||||||||||||||||||||
| alt="Figma" | ||||||||||||||||||||||||||||||||
| className={styles.integrationLogo} | ||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||
| </Link> | ||||||||||||||||||||||||||||||||
| <Link to="/docs/sources/linear" className={styles.integrationLink}> | ||||||||||||||||||||||||||||||||
| <img | ||||||||||||||||||||||||||||||||
| src={useBaseUrl('/img/linear.jpeg')} | ||||||||||||||||||||||||||||||||
| alt="Linear" | ||||||||||||||||||||||||||||||||
| className={styles.integrationLogo} | ||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||
| </Link> | ||||||||||||||||||||||||||||||||
| <Link to="/docs/sources/notion" className={styles.integrationLink}> | ||||||||||||||||||||||||||||||||
| <img | ||||||||||||||||||||||||||||||||
| src={useBaseUrl('/img/notion logo.png')} | ||||||||||||||||||||||||||||||||
| alt="Notion" | ||||||||||||||||||||||||||||||||
| className={styles.integrationLogo} | ||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||
| </Link> | ||||||||||||||||||||||||||||||||
| {[ | ||||||||||||||||||||||||||||||||
| { id: 'figma' as const, to: '/docs/cli/figma', src: '/img/figma-logo.svg', alt: 'Figma' }, | ||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The Figma logo link was changed to Useful? React with 👍 / 👎. |
||||||||||||||||||||||||||||||||
| { id: 'github' as const, to: '/docs/sources/github', src: '/img/github logo.webp', alt: 'GitHub' }, | ||||||||||||||||||||||||||||||||
| { id: 'linear' as const, to: '/docs/sources/linear', src: '/img/linear.jpeg', alt: 'Linear' }, | ||||||||||||||||||||||||||||||||
| { id: 'notion' as const, to: '/docs/sources/notion', src: '/img/notion logo.png', alt: 'Notion' }, | ||||||||||||||||||||||||||||||||
| ].map(({ id, to, src, alt }) => ( | ||||||||||||||||||||||||||||||||
| <Link key={id} to={to} className={styles.integrationLink}> | ||||||||||||||||||||||||||||||||
| <img | ||||||||||||||||||||||||||||||||
| src={useBaseUrl(src)} | ||||||||||||||||||||||||||||||||
| alt={alt} | ||||||||||||||||||||||||||||||||
| className={`${styles.integrationLogo} ${scenario.id === id ? styles.integrationLogoActive : ''}`} | ||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||
| </Link> | ||||||||||||||||||||||||||||||||
| ))} | ||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,15 +3,16 @@ import { useEffect } from 'react'; | |
| import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; | ||
| import Layout from '@theme/Layout'; | ||
| import HeroSection from '@site/src/components/HeroSection'; | ||
| import FigmaShowcase from '@site/src/components/FigmaShowcase'; | ||
|
||
| import VisualValidation from '@site/src/components/VisualValidation'; | ||
|
||
| import FeatureSections from '@site/src/components/FeatureSections'; | ||
| import QuickStart from '@site/src/components/QuickStart'; | ||
| import UseCases from '@site/src/components/UseCases'; | ||
| import ClientShowcase from '@site/src/components/ClientShowcase'; | ||
| import LLMProviders from '@site/src/components/LLMProviders'; | ||
| import IntegrationShowcase from '@site/src/components/IntegrationShowcase'; | ||
| import AutoMode from '@site/src/components/AutoMode'; | ||
| import QuickStart from '@site/src/components/QuickStart'; | ||
| import PresetsShowcase from '@site/src/components/PresetsShowcase'; | ||
| import SkillsShowcase from '@site/src/components/SkillsShowcase'; | ||
| import UseCases from '@site/src/components/UseCases'; | ||
| import AgentEcosystem from '@site/src/components/AgentEcosystem'; | ||
| import IntegrationShowcase from '@site/src/components/IntegrationShowcase'; | ||
|
|
||
| export default function Home(): ReactNode { | ||
| useDocusaurusContext(); | ||
|
|
@@ -27,17 +28,18 @@ export default function Home(): ReactNode { | |
| return ( | ||
| <Layout | ||
| title="Home" | ||
| description="Connect your tools like Figma, GitHub, Linear, and Notion. Fetch specs from anywhere and let AI coding agents build production-ready code automatically with autonomous loops."> | ||
| description="AI-powered autonomous coding from specs to production. Connect Figma, GitHub, Linear, and Notion to run AI coding loops with visual validation."> | ||
| <HeroSection /> | ||
| <main> | ||
| <FigmaShowcase /> | ||
| <VisualValidation /> | ||
| <FeatureSections /> | ||
| <AutoMode /> | ||
| <QuickStart /> | ||
| <PresetsShowcase /> | ||
| <SkillsShowcase /> | ||
| <QuickStart /> | ||
| <UseCases /> | ||
| <ClientShowcase /> | ||
| <LLMProviders /> | ||
| <AgentEcosystem /> | ||
| <IntegrationShowcase /> | ||
| </main> | ||
| </Layout> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interfaceinstead oftypeforDemoScenarioDemoScenariois a plain data structure with no inheritance or extension — the project's conventions prefertypeoverinterfacefor such shapes.Context Used: Rule from
dashboard- Usetypeby default in TypeScript unless you specifically needinterfacefeatures like extension... (source)Prompt To Fix With AI
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!