Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,15 @@ interface CommitRowProps {
wrap?: boolean;
}

export function CommitRow({ commit, isSelected, wrap = false }: CommitRowProps) {
export function CommitRow({
commit,
isSelected,
wrap = false,
}: CommitRowProps) {
return (
<div className="flex min-w-0 flex-1 items-start justify-between gap-2">
<div className="min-w-0 flex-1 overflow-hidden">
<div
className={
wrap
? "text-sm wrap-break-word"
: "truncate text-sm"
}
>
<div className={wrap ? "text-sm wrap-break-word" : "truncate text-sm"}>
{commit.message}
</div>
<div className="truncate text-xs text-muted-foreground">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function DownloadButton({
? "px-2 sm:px-4 py-2 text-sm"
: "px-3 sm:px-6 py-2 sm:py-3 text-sm sm:text-base";

const buttonClasses = `bg-brand/10 text-[#ff8c3a] border border-brand/20 ${sizeClasses} font-normal hover:bg-brand/15 hover:border-brand/35 transition-colors flex items-center gap-2 ${className}`;
const buttonClasses = `bg-foreground text-background ${sizeClasses} font-normal hover:opacity-90 transition-opacity flex items-center gap-2 ${className}`;

const goToInterstitial = () => {
track("download_clicked");
Expand Down
62 changes: 50 additions & 12 deletions apps/marketing/src/app/components/HeroSection/HeroSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,62 @@

import { COMPANY } from "@superset/shared/constants";
import { useScroll } from "framer-motion";
import { useFeatureFlagVariantKey } from "posthog-js/react";
import { useRef, useState } from "react";
import { FaGithub } from "react-icons/fa";
import { isMacPlatform, usePlatform } from "../../hooks/useOS";
import { DownloadButton } from "../DownloadButton";
import { WaitlistModal } from "../WaitlistModal";
import { ProductDemo } from "./components/ProductDemo";
import { TypewriterText } from "./components/TypewriterText";

const HERO_POSITIONING_FLAG = "landing-hero-positioning";

const PIXEL_FONT_STYLE = {
fontFamily: "var(--font-geist-pixel-grid)",
} satisfies React.CSSProperties;

interface HeroCopy {
headline: string;
segments: { text: string; style?: React.CSSProperties }[];
subheadline: string;
}

const TEST_SUBHEADLINE =
"Isolated workspaces for Claude Code, Codex, and any CLI agent — run them side by side and review every change from one dashboard. Free to start.";

const HERO_COPY = {
control: {
headline: "The Code Editor for AI Agents.",
segments: [
{ text: "The Code Editor for " },
{ text: "AI Agents.", style: PIXEL_FONT_STYLE },
],
subheadline:
"Orchestrate 100+ coding agents in parallel. Works for any agents. Built for the AI era.",
},
"capability-mac": {
headline: "Run parallel coding agents on your Mac.",
segments: [
{ text: "Run " },
{ text: "parallel coding agents", style: PIXEL_FONT_STYLE },
{ text: " on your Mac." },
],
subheadline: TEST_SUBHEADLINE,
},
} satisfies Record<string, HeroCopy>;

export function HeroSection() {
const [isWaitlistOpen, setIsWaitlistOpen] = useState(false);
const demoRef = useRef<HTMLDivElement>(null);
const { platform } = usePlatform();
const heroVariant = useFeatureFlagVariantKey(HERO_POSITIONING_FLAG);
const copy =
isMacPlatform(platform) &&
typeof heroVariant === "string" &&
heroVariant in HERO_COPY
? HERO_COPY[heroVariant as keyof typeof HERO_COPY]
: HERO_COPY.control;
Comment on lines +55 to +60

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Prevent prototype pollution by using Object.hasOwn.

Using the in operator checks the entire prototype chain. If PostHog were to return a string like "toString" for the variant key, the check would pass, but HERO_COPY["toString"] would return a function instead of a HeroCopy object, leading to a broken/empty render.

Use Object.hasOwn to safely check for direct properties on the object literal.

🛡️ Proposed fix
 	const copy =
 		isMacPlatform(platform) &&
 		typeof heroVariant === "string" &&
-		heroVariant in HERO_COPY
+		Object.hasOwn(HERO_COPY, heroVariant)
 			? HERO_COPY[heroVariant as keyof typeof HERO_COPY]
 			: HERO_COPY.control;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const copy =
isMacPlatform(platform) &&
typeof heroVariant === "string" &&
heroVariant in HERO_COPY
? HERO_COPY[heroVariant as keyof typeof HERO_COPY]
: HERO_COPY.control;
const copy =
isMacPlatform(platform) &&
typeof heroVariant === "string" &&
Object.hasOwn(HERO_COPY, heroVariant)
? HERO_COPY[heroVariant as keyof typeof HERO_COPY]
: HERO_COPY.control;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/marketing/src/app/components/HeroSection/HeroSection.tsx` around lines
55 - 60, Update the hero copy selection in the copy expression to use
Object.hasOwn(HERO_COPY, heroVariant) instead of the in operator, while
preserving the existing platform, string-type, and control-copy fallback checks.


const { scrollYProgress } = useScroll({
target: demoRef,
Expand All @@ -31,27 +77,19 @@ export function HeroSection() {
}}
>
<span className="invisible" aria-hidden="true">
The Code Editor for AI Agents.
{copy.headline}
</span>
<span className="absolute inset-0">
<TypewriterText
segments={[
{ text: "The Code Editor for " },
{
text: "AI Agents.",
style: {
fontFamily: "var(--font-geist-pixel-grid)",
},
},
]}
key={copy.headline}
segments={copy.segments}
speed={40}
delay={600}
/>
</span>
</h1>
<p className="text-base sm:text-xl font-light text-muted-foreground max-w-4xl mx-auto">
Orchestrate 100+ coding agents in parallel. Works for any
agents. Built for the AI era.
{copy.subheadline}
</p>
</div>

Expand Down
Loading