mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
216 lines
7.0 KiB
TypeScript
216 lines
7.0 KiB
TypeScript
'use client'
|
|
|
|
import React, { useState } from 'react'
|
|
import { ArrowUpRight, Star, Eye } from 'lucide-react'
|
|
import { Text, XStack, YStack } from '@hanzo/ui'
|
|
import { useI18n } from '@hanzo/i18n/react'
|
|
|
|
export interface RepoCardProps {
|
|
repo: string
|
|
line: string
|
|
language: string
|
|
}
|
|
|
|
export function GithubMark({ size = 14, color = 'currentColor' }: { size?: number; color?: string }) {
|
|
return (
|
|
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22" />
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
export function ForgeMark({ size = 14, color = 'currentColor' }: { size?: number; color?: string }) {
|
|
return (
|
|
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
<circle cx="12" cy="18" r="3" />
|
|
<circle cx="6" cy="6" r="3" />
|
|
<circle cx="18" cy="6" r="3" />
|
|
<path d="M18 9v2a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V9" />
|
|
<path d="M12 12v3" />
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
export default function RepoCard({ repo, line, language }: RepoCardProps) {
|
|
const { t } = useI18n()
|
|
const [starred, setStarred] = useState(false)
|
|
const [watching, setWatching] = useState(false)
|
|
|
|
const [owner, name] = repo.split('/')
|
|
const ghUrl = `https://github.com/${repo}`
|
|
const forgeUrl = `https://forge.hanzo.ai/${repo}`
|
|
|
|
const handleStar = (e: React.MouseEvent) => {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
const next = !starred
|
|
setStarred(next)
|
|
|
|
// Call Forge star API
|
|
try {
|
|
fetch(`https://forge.hanzo.ai/api/v1/user/starred/${owner}/${name}`, {
|
|
method: next ? 'PUT' : 'DELETE',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include',
|
|
}).catch(() => {})
|
|
} catch (_) {}
|
|
|
|
// Open GitHub repo in background tab if starring
|
|
if (next) {
|
|
try {
|
|
const win = window.open(ghUrl, '_blank', 'noopener,noreferrer')
|
|
if (win) win.blur()
|
|
window.focus()
|
|
} catch (_) {}
|
|
}
|
|
}
|
|
|
|
const handleWatch = (e: React.MouseEvent) => {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
setWatching(!watching)
|
|
try {
|
|
window.open(`${ghUrl}/subscription`, '_blank', 'noopener,noreferrer')
|
|
} catch (_) {}
|
|
}
|
|
|
|
return (
|
|
<YStack
|
|
render={<a href={ghUrl} target="_blank" rel="noopener noreferrer" />}
|
|
gap={10}
|
|
p={16}
|
|
rounded={14}
|
|
borderWidth={1}
|
|
borderColor="var(--border)"
|
|
bg="rgba(255, 255, 255, 0.01)"
|
|
hoverStyle={{ borderColor: 'var(--border-strong)', bg: 'rgba(255, 255, 255, 0.025)' }}
|
|
cursor="pointer"
|
|
$platform-web={{
|
|
borderRadius: '14px',
|
|
borderStyle: 'solid',
|
|
textDecoration: 'none',
|
|
transition: 'all 0.16s ease-in-out',
|
|
}}
|
|
>
|
|
{/* Top row: repo name + top-right arrow */}
|
|
<XStack items="center" justify="space-between" gap={8}>
|
|
<Text
|
|
fontFamily="$mono"
|
|
fontSize="$2"
|
|
fontWeight="600"
|
|
color="var(--foreground)"
|
|
numberOfLines={1}
|
|
data-i18n-ltr=""
|
|
>
|
|
{repo}
|
|
</Text>
|
|
<ArrowUpRight size={13} color="var(--muted-foreground)" aria-hidden data-i18n-mirror="" />
|
|
</XStack>
|
|
|
|
{/* Description */}
|
|
<Text
|
|
fontSize="$2"
|
|
lineHeight="$3"
|
|
color="var(--muted-foreground)"
|
|
numberOfLines={2}
|
|
minH={38}
|
|
>
|
|
{t(line)}
|
|
</Text>
|
|
|
|
{/* Bottom Action Row */}
|
|
<XStack items="center" justify="space-between" gap={8} mt="auto" pt={4}>
|
|
<XStack items="center" gap={6}>
|
|
{/* Star pill button */}
|
|
<XStack
|
|
render={<button type="button" onClick={handleStar} title={t('Star on GitHub and Hanzo Forge')} />}
|
|
items="center"
|
|
gap={5}
|
|
px={9}
|
|
py={4}
|
|
rounded={6}
|
|
borderWidth={1}
|
|
borderColor={starred ? '#39bd7d' : 'var(--border)'}
|
|
bg={starred ? 'rgba(57, 189, 125, 0.15)' : 'rgba(255, 255, 255, 0.03)'}
|
|
hoverStyle={{
|
|
borderColor: starred ? '#56e36d' : 'var(--border-strong)',
|
|
bg: starred ? 'rgba(57, 189, 125, 0.22)' : 'rgba(255, 255, 255, 0.08)',
|
|
}}
|
|
cursor="pointer"
|
|
$platform-web={{
|
|
borderRadius: '6px',
|
|
borderStyle: 'solid',
|
|
transition: 'all 0.15s ease',
|
|
}}
|
|
>
|
|
<Star
|
|
size={12}
|
|
color={starred ? '#39bd7d' : 'var(--muted-foreground)'}
|
|
fill={starred ? '#39bd7d' : 'none'}
|
|
/>
|
|
<Text
|
|
fontSize={11}
|
|
fontWeight="600"
|
|
color={starred ? '#39bd7d' : 'var(--foreground)'}
|
|
>
|
|
{starred ? t('Starred') : t('Star')}
|
|
</Text>
|
|
</XStack>
|
|
|
|
{/* Watch icon button */}
|
|
<XStack
|
|
render={<button type="button" onClick={handleWatch} title={t('Subscribe to updates')} />}
|
|
items="center"
|
|
justify="center"
|
|
p={5}
|
|
rounded={6}
|
|
borderWidth={1}
|
|
borderColor={watching ? '#39bd7d' : 'transparent'}
|
|
bg={watching ? 'rgba(57, 189, 125, 0.1)' : 'transparent'}
|
|
hoverStyle={{ bg: 'var(--white-06)' }}
|
|
cursor="pointer"
|
|
$platform-web={{ borderRadius: '6px', borderStyle: 'solid' }}
|
|
>
|
|
<Eye size={13} color={watching ? '#39bd7d' : 'var(--muted-foreground)'} />
|
|
</XStack>
|
|
</XStack>
|
|
|
|
{/* Source links: GH & Forge */}
|
|
<XStack items="center" gap={8}>
|
|
<XStack
|
|
render={<a href={ghUrl} target="_blank" rel="noopener noreferrer" title="View on GitHub" />}
|
|
$platform-web={{ textDecoration: 'none' }}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<Text
|
|
fontFamily="$mono"
|
|
fontSize={11}
|
|
fontWeight="600"
|
|
color="var(--muted-foreground)"
|
|
hoverStyle={{ color: 'var(--foreground)' }}
|
|
>
|
|
GH
|
|
</Text>
|
|
</XStack>
|
|
|
|
<XStack
|
|
render={<a href={forgeUrl} target="_blank" rel="noopener noreferrer" title="View on Hanzo Forge" />}
|
|
$platform-web={{ textDecoration: 'none' }}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<Text
|
|
fontFamily="$mono"
|
|
fontSize={11}
|
|
fontWeight="600"
|
|
color="#39bd7d"
|
|
hoverStyle={{ color: '#56e36d' }}
|
|
>
|
|
Forge
|
|
</Text>
|
|
</XStack>
|
|
</XStack>
|
|
</XStack>
|
|
</YStack>
|
|
)
|
|
}
|