vietc/web/src/components/SetupGuide.tsx
2026-07-04 17:18:22 +07:00

379 lines
19 KiB
TypeScript

import React, { useState } from 'react';
import { motion, AnimatePresence } from 'motion/react';
import { Copy, Check, Terminal, Shield, Cpu, RefreshCw, Layers, GitBranch, Hammer } from 'lucide-react';
import { SetupStep } from '../types';
type TabId = 'mint_ubuntu' | 'arch' | 'fedora' | 'dev';
export default function SetupGuide() {
const [activeTab, setActiveTab] = useState<TabId>('mint_ubuntu');
const [copiedText, setCopiedText] = useState<string | null>(null);
const handleCopy = (text: string, id: string) => {
navigator.clipboard.writeText(text);
setCopiedText(id);
setTimeout(() => setCopiedText(null), 2000);
};
const installSteps: Record<Exclude<TabId, 'dev'>, SetupStep[]> = {
mint_ubuntu: [
{
id: 1,
title: "Cài đặt VietC (Pre-built)",
description: "Chạy lệnh dưới đây để tự động tải về, cài đặt phụ thuộc và biên dịch VietC trên hệ thống của bạn.",
command: `git clone https://github.com/vndangkhoa/vietc.git /tmp/vietc \\
&& cd /tmp/vietc && sudo ./install.sh`,
notes: "Script tự động phát hiện distro, cài đặt dependencies, build và cấu hình udev rules cho uinput."
},
{
id: 2,
title: "Gỡ cài đặt (Uninstall)",
description: "Xoá hoàn toàn VietC khỏi hệ thống, bao gồm binary, service và udev rules.",
command: `curl -sSL https://raw.githubusercontent.com/vndangkhoa/vietc/main/uninstall.sh | sudo bash`,
notes: "Lệnh này sẽ xoá /usr/local/bin/vietc, systemd service và các file cấu hình."
}
],
arch: [
{
id: 1,
title: "Cài đặt VietC (Pre-built)",
description: "Tự động clone, build và cài đặt VietC trên Arch Linux.",
command: `git clone https://github.com/vndangkhoa/vietc.git /tmp/vietc \\
&& cd /tmp/vietc && sudo ./install.sh`,
notes: "Script hỗ trợ pacman, tự động cài đặt base-devel và các thư viện cần thiết."
},
{
id: 2,
title: "Gỡ cài đặt (Uninstall)",
description: "Xoá VietC hoàn toàn khỏi hệ thống Arch.",
command: `curl -sSL https://raw.githubusercontent.com/vndangkhoa/vietc/main/uninstall.sh | sudo bash`,
}
],
fedora: [
{
id: 1,
title: "Cài đặt VietC (Pre-built)",
description: "Tự động clone, build và cài đặt VietC trên Fedora.",
command: `git clone https://github.com/vndangkhoa/vietc.git /tmp/vietc \\
&& cd /tmp/vietc && sudo ./install.sh`,
notes: "Script hỗ trợ dnf, tự động cài đặt Development Tools và thư viện X11."
},
{
id: 2,
title: "Gỡ cài đặt (Uninstall)",
description: "Xoá VietC hoàn toàn khỏi hệ thống Fedora.",
command: `curl -sSL https://raw.githubusercontent.com/vndangkhoa/vietc/main/uninstall.sh | sudo bash`,
}
]
};
const devSteps: SetupStep[] = [
{
id: 1,
title: "Clone mã nguồn",
description: "Nhánh main chứa code mới nhất.",
command: `git clone https://github.com/vndangkhoa/vietc.git
cd vietc`,
},
{
id: 2,
title: "Cài đặt Rust (nếu chưa có)",
description: "Dùng rustup để cài Rust toolchain mới nhất.",
command: `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"`,
notes: "Kiểm tra với 'rustc --version' và 'cargo --version'."
},
{
id: 3,
title: "Cài đặt hệ thống phụ thuộc",
description: "Thư viện dev cho X11, evdev và dbus.",
command: `sudo apt install build-essential pkg-config libx11-dev libxtst-dev \\
libevdev-dev libdbus-1-dev libwayland-dev wl-clipboard`,
notes: "Trên Fedora: dnf install; trên Arch: pacman -S. Xem install.sh để biết chi tiết."
},
{
id: 4,
title: "Biên dịch (debug)",
description: "Build nhanh không tối ưu, phù hợp khi phát triển.",
command: `cargo build`,
notes: "Binary ở target/debug/vietc. Chạy thử: ./target/debug/vietc"
},
{
id: 5,
title: "Biên dịch (release - tối ưu)",
description: "Build với tối ưu hóa cho hiệu năng cao nhất.",
command: `cargo build --release`,
notes: "Binary ở target/release/vietc. Chạy thử: ./target/release/vietc"
},
{
id: 6,
title: "Cấp quyền uinput",
description: "VietC cần quyền ghi /dev/uinput. Thêm user vào group input và uinput.",
command: `sudo gpasswd -a $USER input
sudo groupadd -f uinput
sudo gpasswd -a $USER uinput
echo 'KERNEL=="uinput", GROUP="uinput", MODE="0660", OPTIONS+="static_node=uinput"' | sudo tee /etc/udev/rules.d/99-vietc.rules
sudo udevadm control --reload-rules && sudo udevadm trigger`,
notes: "Đăng xuất và đăng nhập lại (hoặc reboot) để group có hiệu lực."
},
{
id: 7,
title: "Chạy thử (không cần cài đặt)",
description: "Chạy trực tiếp từ thư mục build, không cần systemd service.",
command: `./target/release/vietc`,
notes: "Tắt bằng Ctrl+C. Có thể chạy ở chế độ nền với '&' và dùng 'fg' để đưa lên foreground."
}
];
const tabs: { id: TabId; label: string; icon?: React.ReactNode }[] = [
{ id: 'mint_ubuntu', label: 'Mint / Ubuntu' },
{ id: 'arch', label: 'Arch Linux' },
{ id: 'fedora', label: 'Fedora' },
{ id: 'dev', label: 'Dev Build', icon: <Hammer size={12} /> },
];
return (
<div id="setup-guide" className="py-16 bg-[#0a0b0d] border-t border-white/10 scroll-mt-20">
<div className="max-w-6xl mx-auto px-4 sm:px-6">
{/* Section Header */}
<div className="text-center max-w-3xl mx-auto mb-12">
<motion.div
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-emerald-500/10 border border-emerald-500/20 text-emerald-400 text-xs font-mono mb-4"
>
<Terminal size={12} className="text-emerald-400" />
<span>NATIVE LINUX INTEGRATION</span>
</motion.div>
<motion.h2
initial={{ opacity: 0, y: 15 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.1 }}
className="text-3xl sm:text-4xl font-serif text-white tracking-tight"
>
Hướng Dẫn Cài Đt <span className="text-transparent bg-clip-text bg-gradient-to-r from-emerald-400 to-teal-400 italic">VietC</span>
</motion.h2>
<p className="mt-4 text-slate-400 text-sm sm:text-base">
VietC bộ mức thấp (System & Application level) không phụ thuộc IBus hay Fcitx5, việc cài đt sẽ tác đng trực tiếp lên driver uinput hệ thống đ đt tốc đ tuyệt đi.
</p>
</div>
{/* Tabs */}
<div className="flex justify-center mb-10">
<div className="bg-white/[0.02] p-1.5 rounded-xl border border-white/10 flex gap-2 w-full max-w-2xl">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`flex-1 py-2.5 rounded-lg text-xs font-semibold tracking-wide transition-all cursor-pointer flex items-center justify-center gap-1.5 ${
activeTab === tab.id
? 'bg-emerald-500 text-[#0a0b0d] font-bold shadow-[0_0_15px_rgba(16,185,129,0.25)]'
: 'text-slate-400 hover:text-slate-200 hover:bg-white/5'
}`}
>
{tab.icon}
{tab.label}
</button>
))}
</div>
</div>
{/* Content */}
{activeTab === 'dev' ? (
<div>
<div className="flex items-center gap-2 mb-6">
<GitBranch size={18} className="text-emerald-400" />
<h3 className="text-lg font-semibold text-slate-100">Build từ nguồn (dành cho Developer)</h3>
</div>
<p className="text-slate-400 text-sm mb-8 max-w-3xl">
Các bước dưới đây hướng dẫn bạn tự biên dịch VietC từ source, chạy thử không cần cài đt
system-wide. Phù hợp cho developer muốn đóng góp hoặc tùy chỉnh.
</p>
<div className="space-y-6">
{devSteps.map((step, idx) => (
<motion.div
key={`dev-step-${step.id}`}
initial={{ opacity: 0, x: -15 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ delay: idx * 0.08 }}
className="relative bg-white/[0.02] rounded-2xl border border-white/5 p-5 sm:p-6 lg:p-8 hover:border-emerald-500/30 transition-all group"
>
{idx !== devSteps.length - 1 && (
<div className="absolute left-[33px] sm:left-[37px] top-[75px] bottom-[-35px] w-0.5 bg-white/5 pointer-events-none group-hover:bg-emerald-500/15 transition-all" />
)}
<div className="flex items-start gap-4 sm:gap-6">
<div className="flex-shrink-0 w-10 h-10 sm:w-12 sm:h-12 rounded-full bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center text-emerald-400 font-mono font-bold text-sm sm:text-base shadow-inner">
0{step.id}
</div>
<div className="flex-1 min-w-0">
<h3 className="text-base sm:text-lg font-sans font-semibold text-slate-100 mb-2">
{step.title}
</h3>
<p className="text-slate-400 text-xs sm:text-sm leading-relaxed mb-4">
{step.description}
</p>
{step.command && (
<div className="relative rounded-xl overflow-hidden bg-[#0d0e12] border border-white/10 shadow-2xl font-mono text-xs text-slate-300 group/term">
<div className="flex items-center justify-between px-4 py-2 bg-[#0a0b0d] border-b border-white/5">
<div className="flex items-center gap-1.5">
<div className="w-2.5 h-2.5 rounded-full bg-rose-500/60" />
<div className="w-2.5 h-2.5 rounded-full bg-amber-500/60" />
<div className="w-2.5 h-2.5 rounded-full bg-emerald-500/60" />
<span className="ml-2 text-[10px] text-slate-500 font-mono font-medium">BASH TERMINAL</span>
</div>
<button
onClick={() => handleCopy(step.command!, `dev-${step.id}`)}
className="p-1 rounded hover:bg-white/5 text-slate-400 hover:text-slate-200 transition-colors cursor-pointer"
title="Sao chép lệnh"
>
{copiedText === `dev-${step.id}` ? (
<Check size={14} className="text-emerald-400" />
) : (
<Copy size={14} />
)}
</button>
</div>
<div className="p-4 overflow-x-auto whitespace-pre leading-5 selection:bg-emerald-500/30 selection:text-white">
{step.command}
</div>
</div>
)}
{step.notes && (
<div className="mt-3 flex gap-2 p-3.5 rounded-xl bg-emerald-950/15 border border-emerald-500/10 text-xs text-emerald-300/90">
<Shield size={14} className="flex-shrink-0 mt-0.5 text-emerald-400" />
<div className="leading-relaxed">
<span className="font-semibold text-emerald-400">Lưu ý:</span> {step.notes}
</div>
</div>
)}
</div>
</div>
</motion.div>
))}
</div>
</div>
) : (
<div className="space-y-8">
{installSteps[activeTab].map((step, idx) => (
<motion.div
key={`${activeTab}-${step.id}`}
initial={{ opacity: 0, x: -15 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ delay: idx * 0.1 }}
className="relative bg-white/[0.02] rounded-2xl border border-white/5 p-5 sm:p-6 lg:p-8 hover:border-emerald-500/30 transition-all group"
>
{idx !== installSteps[activeTab].length - 1 && (
<div className="absolute left-[33px] sm:left-[37px] top-[75px] bottom-[-45px] w-0.5 bg-white/5 pointer-events-none group-hover:bg-emerald-500/15 transition-all" />
)}
<div className="flex items-start gap-4 sm:gap-6">
<div className="flex-shrink-0 w-10 h-10 sm:w-12 sm:h-12 rounded-full bg-white/5 border border-white/10 flex items-center justify-center text-emerald-400 font-mono font-bold text-sm sm:text-base shadow-inner group-hover:border-emerald-500/30 group-hover:bg-emerald-500/10 transition-all">
0{step.id}
</div>
<div className="flex-1 min-w-0">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2 mb-3">
<h3 className="text-base sm:text-lg font-sans font-semibold text-slate-100 group-hover:text-emerald-300 transition-colors">
{step.title}
</h3>
</div>
<p className="text-slate-400 text-xs sm:text-sm leading-relaxed mb-4">
{step.description}
</p>
{step.command && (
<div className="relative rounded-xl overflow-hidden bg-[#0d0e12] border border-white/10 shadow-2xl font-mono text-xs text-slate-300 group/term">
<div className="flex items-center justify-between px-4 py-2 bg-[#0a0b0d] border-b border-white/5">
<div className="flex items-center gap-1.5">
<div className="w-2.5 h-2.5 rounded-full bg-rose-500/60" />
<div className="w-2.5 h-2.5 rounded-full bg-amber-500/60" />
<div className="w-2.5 h-2.5 rounded-full bg-emerald-500/60" />
<span className="ml-2 text-[10px] text-slate-500 font-mono font-medium">BASH TERMINAL</span>
</div>
<button
onClick={() => handleCopy(step.command!, `${activeTab}-${step.id}`)}
className="p-1 rounded hover:bg-white/5 text-slate-400 hover:text-slate-200 transition-colors cursor-pointer"
title="Sao chép lệnh"
>
{copiedText === `${activeTab}-${step.id}` ? (
<Check size={14} className="text-emerald-400" />
) : (
<Copy size={14} />
)}
</button>
</div>
<div className="p-4 overflow-x-auto whitespace-pre leading-5 selection:bg-emerald-500/30 selection:text-white">
{step.command}
</div>
</div>
)}
{step.notes && (
<div className="mt-3 flex gap-2 p-3.5 rounded-xl bg-emerald-950/15 border border-emerald-500/10 text-xs text-emerald-300/90">
<Shield size={14} className="flex-shrink-0 mt-0.5 text-emerald-400" />
<div className="leading-relaxed">
<span className="font-semibold text-emerald-400">Lưu ý:</span> {step.notes}
</div>
</div>
)}
</div>
</div>
</motion.div>
))}
</div>
)}
{/* Architecture graphic */}
<div className="mt-16 bg-gradient-to-br from-white/[0.03] to-transparent p-6 sm:p-8 rounded-3xl border border-white/10">
<h3 className="text-lg sm:text-xl font-semibold text-slate-100 flex items-center gap-2 mb-6">
<Layers className="text-emerald-400" size={18} />
<span> Hình Hoạt Đng Khác Biệt của VietC</span>
</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="p-5 rounded-2xl bg-[#0d0e12] border border-white/5">
<div className="w-8 h-8 rounded-lg bg-red-500/10 border border-red-500/20 text-red-400 flex items-center justify-center font-bold text-xs mb-3">
OLD
</div>
<h4 className="text-sm font-semibold text-slate-200 mb-2">IBus / Fcitx5</h4>
<p className="text-slate-400 text-xs leading-relaxed">
Hoạt đng Application Layer qua chế giao tiếp DBus phức tạp. Khi trong Terminal o, các lệnh Backspace/Delete giả lập thường bị trễ hoặc nuốt tự gây lỗi nhân đôi hoặc mất chữ.
</p>
</div>
<div className="p-5 rounded-2xl bg-[#0d0e12] border border-white/5">
<div className="w-8 h-8 rounded-lg bg-emerald-500/10 border border-emerald-500/20 text-emerald-400 flex items-center justify-center font-bold text-xs mb-3">
NEW
</div>
<h4 className="text-sm font-semibold text-slate-200 mb-2">VietC (uinput + evdev)</h4>
<p className="text-slate-400 text-xs leading-relaxed">
Chặn (grab) sự kiện gốc từ bàn phím vật thông qua driver <code className="text-emerald-400 font-mono">evdev</code>, sau đó tự tính toán bằng State Machine xuất ra bàn phím o mới thông qua <code className="text-emerald-400 font-mono">uinput</code>.
</p>
</div>
<div className="p-5 rounded-2xl bg-emerald-950/15 border border-emerald-500/20">
<div className="w-8 h-8 rounded-lg bg-emerald-500/20 text-emerald-300 flex items-center justify-center font-bold text-xs mb-3">
WIN
</div>
<h4 className="text-sm font-semibold text-emerald-300 mb-2">Trải Nghiệm "Như Bay"</h4>
<p className="text-emerald-400/80 text-xs leading-relaxed">
Đ trễ phản hồi phím <span className="text-white font-semibold">Keystroke: 0ms</span> giải phóng nút <span className="text-white font-semibold">&lt;1ms</span>. tiếng Việt gốc 100% không bị lag, không kén Terminal nào (Alacritty, Kitty, GNOME Terminal, v.v.).
</p>
</div>
</div>
</div>
</div>
</div>
);
}