Skip to content
Open
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
111 changes: 111 additions & 0 deletions website/src/theme/CodeBlock/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import React, { useState, useCallback, useRef } from 'react';
import OriginalCodeBlock from '@theme-original/CodeBlock';
import { Check, Copy } from 'lucide-react';
import { cn } from '@site/src/lib/utils';

// Map common language identifiers to display labels
const LANG_LABELS: Record<string, string> = {
solidity: 'Solidity',
sol: 'Solidity',
javascript: 'JavaScript',
js: 'JavaScript',
typescript: 'TypeScript',
ts: 'TypeScript',
python: 'Python',
py: 'Python',
bash: 'Bash',
sh: 'Shell',
shell: 'Shell',
yaml: 'YAML',
yml: 'YAML',
json: 'JSON',
toml: 'TOML',
go: 'Go',
rust: 'Rust',
sql: 'SQL',
dockerfile: 'Dockerfile',
html: 'HTML',
css: 'CSS',
jsx: 'JSX',
tsx: 'TSX',
xml: 'XML',
graphql: 'GraphQL',
};

type CodeBlockProps = React.ComponentProps<typeof OriginalCodeBlock>;

export default function CodeBlockWrapper(props: CodeBlockProps) {
const [copied, setCopied] = useState(false);
const [toastVisible, setToastVisible] = useState(false);
const toastTimer = useRef<ReturnType<typeof setTimeout>>();

// Resolve language label from className prop (e.g. "language-solidity")
const langClass = (props.className || '') as string;
const langKey = langClass.replace(/^language-/, '').toLowerCase();
const langLabel = LANG_LABELS[langKey] || (langKey || '');

const handleCopy = useCallback(async () => {
const code = typeof props.children === 'string' ? props.children : '';
if (!code) return;
try {
await navigator.clipboard.writeText(code);
setCopied(true);

// Show toast
setToastVisible(true);
if (toastTimer.current) clearTimeout(toastTimer.current);
toastTimer.current = setTimeout(() => {
setToastVisible(false);
setCopied(false);
}, 2000);
} catch {
// Ignore — clipboard may not be available in all contexts
}
}, [props.children]);

return (
<div className="group relative my-6 overflow-hidden rounded-lg border border-border/70 bg-background shadow-[0_0_0_1px_rgba(0,0,0,0.06)] dark:border-white/[0.07] dark:shadow-[0_0_0_1px_rgba(255,255,255,0.06)]">
{/* Language label — top-left */}
{langLabel && (
<div className="absolute left-3 top-3 z-10 flex items-center gap-1.5 rounded-md border border-border/50 bg-background/90 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-widest text-muted-foreground backdrop-blur-sm dark:border-white/[0.08]">
{langLabel}
</div>
)}

<OriginalCodeBlock {...props} />

{/* Copy button — top-right, appears on group hover */}
<button
type="button"
id={`code-copy-${langKey}`}
onClick={handleCopy}
className={cn(
'absolute right-3 top-3 z-10 flex h-8 w-8 items-center justify-center rounded-md border border-border/60 bg-background/95 text-muted-foreground opacity-0 shadow-sm backdrop-blur-sm transition-all duration-150',
'hover:border-primary/40 hover:text-primary focus:opacity-100 group-hover:opacity-100',
copied && 'border-emerald-500/50 text-emerald-500 hover:border-emerald-500/50 hover:text-emerald-500',
'dark:border-white/[0.1] dark:bg-black/60'
)}
aria-label={copied ? 'Copied!' : 'Copy code to clipboard'}
title={copied ? 'Copied!' : 'Copy'}
>
{copied ? (
<Check className="h-3.5 w-3.5" />
) : (
<Copy className="h-3.5 w-3.5" />
)}
</button>

{/* Inline toast */}
{toastVisible && (
<div
role="status"
aria-live="polite"
className="absolute right-3 top-14 z-20 flex items-center gap-1.5 rounded-md border border-emerald-500/30 bg-background/95 px-2.5 py-1.5 text-xs font-medium text-emerald-500 shadow-md backdrop-blur-sm animate-in slide-in-from-top-1 duration-200 dark:bg-black/80"
>
<Check className="h-3 w-3" />
Copied!
</div>
)}
</div>
);
}
117 changes: 117 additions & 0 deletions website/src/theme/Footer/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import React from 'react';
import { useThemeConfig } from '@docusaurus/theme-common';
import Link from '@docusaurus/Link';
import useBaseUrl from '@docusaurus/useBaseUrl';
import { Github, Twitter, MessageCircle, Send, Heart } from 'lucide-react';
import styles from './styles.module.css';

interface FooterLinkItem {
label: string;
to?: string;
href?: string;
}

interface FooterLinkColumn {
title: string;
items: FooterLinkItem[];
}

const socialLinks = [
{ icon: Github, href: 'https://github.com/XinFinOrg', label: 'GitHub' },
{ icon: Twitter, href: 'https://twitter.com/XinFin_Official', label: 'Twitter / X' },
{ icon: MessageCircle, href: 'https://discord.gg/xdc', label: 'Discord' },
{ icon: Send, href: 'https://t.me/xinfintalk', label: 'Telegram' },
];

export default function Footer() {
const { footer } = useThemeConfig();
const footerLinks = (footer?.links as FooterLinkColumn[]) ?? [];
const logoSrc = useBaseUrl('img/logo-dark.svg');
const logoSrcDark = useBaseUrl('img/logo.svg');

const renderLink = (item: FooterLinkItem) => {
const content = (
<>
<span className={styles.linkText}>{item.label}</span>
<span className={styles.linkUnderline} aria-hidden="true" />
</>
);
return item.href ? (
<a
key={item.label}
href={item.href}
target="_blank"
rel="noopener noreferrer"
className={styles.link}
>
{content}
</a>
) : (
<Link key={item.label} to={item.to ?? '#'} className={styles.link}>{content}</Link>
);
};

return (
<footer className={styles.footer}>
<div className={styles.gradientLine} aria-hidden="true" />
<div className={styles.container}>
<div className={styles.grid}>
{/* Brand column */}
<div className={styles.brandColumn}>
<Link to="/" className={styles.logo}>
<img
src={logoSrc}
alt="XDC Network"
className="dark:hidden"
width="120"
height="32"
/>
<img
src={logoSrcDark}
alt="XDC Network"
className="hidden dark:block"
width="120"
height="32"
/>
</Link>
<p className={styles.tagline}>
Built for global finance. Enterprise-grade hybrid blockchain powering trade, tokenization, and decentralized infrastructure.
</p>
<div className={styles.socials}>
{socialLinks.map(({ icon: Icon, href, label }) => (
<a
key={label}
href={href}
target="_blank"
rel="noopener noreferrer"
aria-label={label}
className={styles.socialLink}
>
<Icon className={styles.socialIcon} />
</a>
))}
</div>
</div>

{/* Link columns */}
{footerLinks.map((column) => (
<div key={column.title} className={styles.linkColumn}>
<h3 className={styles.columnTitle}>{column.title}</h3>
<div className={styles.linkList}>{column.items.map(renderLink)}</div>
</div>
))}
</div>

{/* Bottom bar */}
<div className={styles.bottom}>
<div className={styles.builtWith}>
Built with <Heart className={styles.heart} /> by the XDC community
</div>
<div className={styles.copyright}>
Copyright © {new Date().getFullYear()} XDC Network. All rights reserved.
</div>
</div>
</div>
</footer>
);
}
Loading