'use client' import React, { useMemo, useState } from 'react' import { cn } from '../../utils/cn' import { EyeIcon } from '../icons' import { Copy01Icon } from '../icons-v2-generated/documents' import { CheckIcon } from '../icons-v2-generated/signs-and-symbols' import { ExternalLink } from 'lucide-react' import { OpenFrameLogo } from '../..' import { useCopyToClipboard } from '../../hooks/use-copy-to-clipboard' import { useIsTruncated } from '../../hooks/ui/use-is-truncated' import { FloatingTooltip } from './floating-tooltip' import { TruncateText } from './truncate-text' export type ServiceCardRowAction = { copy?: boolean open?: boolean reveal?: boolean } export type ServiceCardRow = { label?: string value: string href?: string copyValue?: string isSecret?: boolean monospace?: boolean actions?: ServiceCardRowAction } export type ServiceCardTag = { label: string } export interface ServiceCardProps { title: string subtitle?: string icon?: React.ReactNode tag?: ServiceCardTag rows: ServiceCardRow[] className?: string } function MaskedValue({ value, isRevealed }: { value: string; isRevealed: boolean }) { if (isRevealed) return {value} return {'•'.repeat(Math.min(value.length, 12))} } export function ServiceCard({ title, subtitle, icon, tag, rows, className }: ServiceCardProps) { const resolvedIcon = icon ?? ( ) return (
{/* Header */}
{resolvedIcon}
{title} {subtitle && ( {subtitle} )}
{tag && (
{tag.label}
)}
{/* Rows */}
{rows.map((row, idx) => ( ))}
) } function ServiceCardRowItem({ row }: { row: ServiceCardRow }) { const [revealed, setRevealed] = useState(false) const { copy, copied } = useCopyToClipboard() const { ref: valueRef, isTruncated: valueTruncated } = useIsTruncated(row.value) const actions = useMemo(() => ({ copy: true, open: !!row.href, reveal: !!row.isSecret, ...row.actions }), [row]) const displayValue = row.isSecret ? : {row.value} const handleCopy = () => copy(row.copyValue ?? row.value) const openInNewTab = () => { if (!row.href) return window.open(row.href, '_blank', 'noopener,noreferrer') } return (
{row.label && (
{row.label}
)}
{/* No tooltip while a secret is masked — the old native `title` leaked the raw value on hover. */}
{displayValue}
{actions.reveal && ( )} {actions.copy && ( )} {actions.open && row.href && ( )}
) }