Files
hanzo.ai/components/referrals/CreatorStudio.tsx

353 lines
11 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import React, { useMemo, useState } from 'react'
import {
Check,
Copy,
Sparkles,
Video,
FileCode,
FlaskConical,
Laugh,
} from 'lucide-react'
import {
Box,
Button,
Text,
View,
XStack,
YStack,
toast,
} from '@hanzo/ui'
import { useI18n } from '@hanzo/i18n/react'
interface CreatorStudioProps {
referralLink?: string
}
type FormatType = 'video' | 'thread' | 'research' | 'meme'
type TopicType = 'agent' | 'benchmark' | 'reasoning' | 'cloud'
type VoiceType = 'technical' | 'relatable' | 'research'
interface TemplateItem {
id: string
title: string
description: string
format: FormatType
topic: TopicType
voice: VoiceType
body: string
}
const TEMPLATES: readonly TemplateItem[] = [
{
id: 'agent_demo',
title: 'Build with an autonomous coding agent',
description: 'A 20-second demo idea for TikTok or YouTube Shorts.',
format: 'video',
topic: 'agent',
voice: 'technical',
body: `POV: You hand your terminal to an autonomous coding agent.
1. "Refactor auth middleware to PKCE with Hanzo ID"
2. The agent traverses the workspace, writes the code, runs the test suite, and fixes broken assertions.
3. Clean git commit ready for PR review.
Testing Hanzo Dev agents with early access tokens: {link}
#coding #ai #softwareengineer #webdev #HanzoAI`,
},
{
id: 'benchmark_eval',
title: 'Benchmark an AI model',
description: 'A technical post with methodology, results, and reproducible evidence.',
format: 'research',
topic: 'benchmark',
voice: 'research',
body: `Running reproducible benchmark tests across frontier reasoning models:
Evaluating multi-turn tool calling and code execution across 120 programming tasks.
• Test harness: SWE-bench verified subset
• Metric: Test pass rate & execution latency
• Reproducible repo & test runner script: https://hanzo.ai/research
Run the benchmarks yourself or claim developer inference credits: {link}
#MachineLearning #AIResearch #SWEbench #LLMs`,
},
{
id: 'dev_meme',
title: 'Developer workflow hot take',
description: 'A relatable observation comparing terminal agents to traditional IDEs.',
format: 'meme',
topic: 'agent',
voice: 'relatable',
body: `2023: Copilot autocompletes 2 lines of CSS.
2024: Prompting chat boxes for snippets, pasting into files, realizing imports are broken.
2026: Autonomous CLI agent checks out branch, fixes failing tests, runs lint, opens PR.
The future of software engineering is autonomous loops, not copy-pasting.
Test it out: {link}`,
},
// Additional templates revealed via "View all templates"
{
id: 'technical_thread_arch',
title: 'Technical deep-dive thread',
description: 'A multi-tweet or LinkedIn architectural breakdown of agentic sandboxes.',
format: 'thread',
topic: 'agent',
voice: 'technical',
body: `How autonomous agentic loops actually execute in secure developer sandboxes:
1/ Autonomous agents can't just emit code; they need execution verification.
2/ Hanzo Dev runs isolated Docker/Firecracker containers with strict network isolation.
3/ Every command execution, diff patch, and test result feeds into the decision loop.
Full technical write-up and architecture documentation: {link}`,
},
{
id: 'cloud_infra_research',
title: 'Spot GPU cluster infrastructure',
description: 'An empirical comparison of distributed spot compute vs hyper-scalers.',
format: 'research',
topic: 'cloud',
voice: 'research',
body: `Distributed compute evaluation:
Comparing inference throughput and per-token pricing across decentralized spot GPU clusters vs traditional cloud instances.
• Sub-millisecond routing across high-bandwidth nodes
• Native OpenAI & Anthropic API compatibility
• Benchmark breakdown and verified run scripts: {link}`,
},
]
export function CreatorStudio({ referralLink = '' }: CreatorStudioProps) {
const { t } = useI18n()
const [selectedFormat, setSelectedFormat] = useState<FormatType>('video')
const [_selectedTopic, setSelectedTopic] = useState<TopicType>('agent')
const [_selectedVoice, setSelectedVoice] = useState<VoiceType>('technical')
const [showAllTemplates, setShowAllTemplates] = useState(false)
const [copied, setCopied] = useState(false)
// Current active template or custom generated text
const [activeContent, setActiveContent] = useState(TEMPLATES[0].body)
const activeLink = referralLink || 'https://hanzo.ai/?ref=yourname'
const formattedContent = useMemo(() => {
return activeContent.replace(/\{link\}/g, activeLink)
}, [activeContent, activeLink])
const handleSelectTemplate = (tpl: TemplateItem) => {
setSelectedFormat(tpl.format)
setSelectedTopic(tpl.topic)
setSelectedVoice(tpl.voice)
setActiveContent(tpl.body)
}
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(formattedContent)
setCopied(true)
toast.success(t('Post copied to clipboard!'))
window.setTimeout(() => setCopied(false), 2400)
} catch {
toast.error(t('Failed to copy'))
}
}
// Filter featured vs all
const visibleTemplates = showAllTemplates ? TEMPLATES : TEMPLATES.slice(0, 3)
return (
<YStack gap={32} width="100%" py={16}>
{/* Section Header */}
<YStack gap={8} items="center" style={{ textAlign: 'center' }}>
<Text
render="h2"
fontWeight="600"
color="var(--foreground)"
style={{ textAlign: 'center', letterSpacing: '-0.02em' }}
$platform-web={{ fontSize: 'clamp(28px, 3.2vw, 42px)' }}
>
{t('Your next post starts here.')}
</Text>
<Text fontSize="$4" color="var(--muted-foreground)" maxW={640} style={{ textAlign: 'center' }}>
{t('Generate a developer-focused post with Hanzo AI. Choose a format, select a topic, and customize your voice.')}
</Text>
</YStack>
{/* Content format tabs */}
<XStack justify="center" flexWrap="wrap" gap={8}>
{[
{ id: 'video', label: t('Short video'), icon: Video },
{ id: 'thread', label: t('Technical thread'), icon: FileCode },
{ id: 'research', label: t('Research'), icon: FlaskConical },
{ id: 'meme', label: t('Meme'), icon: Laugh },
].map((f) => (
<Button
key={f.id}
type="button"
variant={selectedFormat === f.id ? 'default' : 'outline'}
onClick={() => {
setSelectedFormat(f.id as FormatType)
const match = TEMPLATES.find((tpl) => tpl.format === f.id)
if (match) setActiveContent(match.body)
}}
gap={6}
>
<f.icon size={14} aria-hidden />
{f.label}
</Button>
))}
</XStack>
{/* Featured Templates Cards */}
<YStack gap={16}>
<XStack items="center" justify="space-between">
<Text fontFamily="$mono" fontSize="$1" fontWeight="600" color="var(--muted-foreground)">
{t('FEATURED TEMPLATES')}
</Text>
<Button
type="button"
variant="ghost"
size="sm"
onClick={() => setShowAllTemplates(!showAllTemplates)}
>
{showAllTemplates ? t('Show fewer') : t('View all templates →')}
</Button>
</XStack>
<View
display="grid"
gap={16}
gridTemplateColumns="1fr"
$md={{ gridTemplateColumns: 'repeat(3, minmax(0, 1fr))' }}
>
{visibleTemplates.map((tpl) => (
<YStack
key={tpl.id}
p={20}
rounded={14}
borderWidth={1}
borderColor={activeContent === tpl.body ? 'var(--foreground)' : 'var(--border)'}
bg="var(--background)"
gap={12}
justify="space-between"
cursor="pointer"
hoverStyle={{ borderColor: 'var(--foreground)' }}
onClick={() => handleSelectTemplate(tpl)}
$platform-web={{ transition: 'border-color 150ms' }}
>
<YStack gap={6}>
<Text fontSize="$3" fontWeight="600" color="var(--foreground)">
{tpl.title}
</Text>
<Text fontSize="$2" color="var(--muted-foreground)" style={{ lineHeight: '1.45em' }}>
{tpl.description}
</Text>
</YStack>
<XStack items="center" gap={6}>
<Text fontSize="$2" fontWeight="600" color="var(--foreground)">
{t('Generate content →')}
</Text>
</XStack>
</YStack>
))}
</View>
</YStack>
{/* Generator Editor / Preview Box */}
<YStack
p={24}
rounded={16}
borderWidth={1}
borderColor="var(--border)"
bg="var(--background)"
gap={16}
>
<XStack items="center" justify="space-between" flexWrap="wrap" gap={12}>
<XStack items="center" gap={8}>
<Sparkles size={16} color="var(--foreground)" aria-hidden />
<Text fontSize="$2" fontWeight="600" color="var(--foreground)">
{t('Generated Developer Post (Ready to share)')}
</Text>
</XStack>
<XStack items="center" gap={8}>
<Button
type="button"
variant="outline"
size="sm"
onClick={handleCopy}
gap={6}
>
{copied ? <Check size={14} aria-hidden /> : <Copy size={14} aria-hidden />}
{copied ? t('Copied!') : t('Copy Content')}
</Button>
</XStack>
</XStack>
{/* Content Box */}
<Box
p={16}
rounded={12}
bg="var(--secondary)"
borderWidth={1}
borderColor="var(--border)"
>
<Text
render="pre"
fontFamily="$mono"
fontSize="$2"
color="var(--foreground)"
style={{
whiteSpace: 'pre-wrap',
wordBreak: 'break-word',
lineHeight: '1.6em',
margin: 0,
}}
>
{formattedContent}
</Text>
</Box>
{/* Quick Share Links */}
<XStack items="center" justify="space-between" flexWrap="wrap" gap={12} pt={4}>
<Text fontSize="$1" color="var(--muted-foreground)">
{t('Includes your personal referral tracking parameter automatically.')}
</Text>
<XStack items="center" gap={8}>
<a
href={`https://twitter.com/intent/tweet?text=${encodeURIComponent(formattedContent)}`}
target="_blank"
rel="noopener noreferrer"
style={{ textDecoration: 'none' }}
>
<Button type="button" variant="ghost" size="sm">
{t('Share to 𝕏')}
</Button>
</a>
<a
href={`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(activeLink)}`}
target="_blank"
rel="noopener noreferrer"
style={{ textDecoration: 'none' }}
>
<Button type="button" variant="ghost" size="sm">
{t('Share to LinkedIn')}
</Button>
</a>
</XStack>
</XStack>
</YStack>
</YStack>
)
}
export default CreatorStudio