mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
353 lines
11 KiB
TypeScript
353 lines
11 KiB
TypeScript
'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
|