【02】从0到1构建AI生成思维导图应用 – 编写主页
大家好!最近自己做了一个完全免费的AI生成思维导图的网站,支持下载,编辑和对接微信公众号,可以在这里体验:https://lt2mind.zeabur.app/
上一章:https://blog.csdn.net/m0_56699208/article/details/140006789?spm=1001.2014.3001.5501
构建好nextjs,并配置了tailwindcss和shadcn-ui后,我们就可以开始编写主页的前端代码了:
在编写代码之前,先通过 shadcn-ui
安装一些组件:
npx shadcn-ui@latest add button input tooltip textarea skeleton
安装 react-simple-typewriter
打字机效果插件:
npm install react-simple-typewriter
修改layout.tsx,改变元数据和字体:
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
const inter = Inter({
subsets: ["latin"],
display: "swap",
adjustFontFallback: false,
});
export const metadata: Metadata = {
title: "LT2Mind",
description:
"Transform your text and links into beautiful mind maps with ease.",
icons: {
icon: '/favicon.ico'
}
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
</body>
</html>
);
}
在 globals.css
里配置一些通用样式:
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
.bg {
@apply bg-gradient-to-r from-blue-100 via-purple-100 to-pink-100
}
在 components 文件夹下创建一个 hero 组件,作为应用的主页:
hero.tsx:
"use client";
import { useState } from "react";
import { Textarea } from "./ui/textarea";
import { Button } from "@/components/ui/button";
import Link from "next/link";
import { Typewriter } from "react-simple-typewriter";
import { Skeleton } from "@/components/ui/skeleton";
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../components/ui/tooltip";
export default function Hero() {
const { isSignedIn } = useUser();
const [imgUrl, setImgUrl] = useState("");
const [editUrl, setEditUrl] = useState("");
const [loading, setLoading] = useState(false);
const handleSubmit = async (event: any) => {
event.preventDefault(); // Prevent the default form submission behavior
if (!isSignedIn) {
//router.push('/sign-in');
return;
}
setLoading(true);
const inputValue = event.target.elements.textOrLink.value; // Get the value from the textarea
const result = await toMind(inputValue); // Call the toMind function with the input value
if (result) {
setImgUrl(result.imgUrl);
setEditUrl(result.editUrl);
}
setLoading(false);
};
return (
<div className="flex flex-col bg">
<header className="text-blue-500">
<section className="container max-w-5xl px-4 md:px-6 py-6 md:py-10">
<div className="text-center space-y-4 sm:mt-4">
<h1 className="text-4xl md:text-6xl font-bold tracking-widest">
LT2MIND
</h1>
<p className="text-lg md:text-xl max-w-3xl mx-auto italic">
<Typewriter
words={[
"Transform your text and links into beautiful mind maps with ease.",
]}
loop={1}
cursor
cursorStyle=""
typeSpeed={20}
deleteSpeed={50}
delaySpeed={1000}
/>
</p>
</div>
</section>
<section className="py-12 md:py-20">
<div className="container max-w-5xl px-4 md:px-6 grid grid-cols-1 md:grid-cols-2 gap-8">
<div className="space-y-4">
<h2 className="text-3xl font-bold text-purple-400">
Convert to Mind Map
</h2>
<p className="text-muted-foreground">
Enter your text or link and let LT2Mind do the rest. You may
wait for one minute or more for the transition to complete.
</p>
<form className="flex gap-2" onSubmit={handleSubmit}>
<div className="flex flex-col gap-2 h-[40vh] w-full">
<Textarea
name="textOrLink"
placeholder="Enter text or link..."
className="shadow-sm focus:border-none flex-1 resize-none bg"
style={{
outline: "none",
overflow: "auto",
scrollbarWidth: "none",
msOverflowStyle: "none",
}}
/>
<style jsx>{`
textarea::-webkit-scrollbar {
display: none;
}
`}</style>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div>
<Button
type="submit"
className="mt-2 w-full bg-pink-200 text-purple-500 hover:bg-pink-100"
disabled={!isSignedIn}
>
Convert
</Button>
</div>
</TooltipTrigger>
{!isSignedIn && (
<TooltipContent>
You need to sign in to convert
</TooltipContent>
)}
</Tooltip>
</TooltipProvider>
</div>
</form>
</div>
{loading ? (
<div className="flex flex-col items-center justify-center gap-2">
<div className="animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-purple-500"></div>
<p className="text-lg text-purple-400">converting...</p>
</div>
) : imgUrl ? (
<div className="flex flex-col items-center justify-center gap-2">
<img
src={imgUrl}
alt="Image 1"
className="w-full h-full object-contain shadow-sm"
/>
<div className="flex gap-x-4 w-full justify-center">
<Link href={editUrl} target="_blank" className="w-full">
<Button className="bg-pink-200 text-purple-500 w-full hover:bg-pink-100">
Edit in TreeMind
</Button>
</Link>
<a
href={imgUrl}
download="mindmap.jpeg"
target="_blank"
className="w-full"
>
<Button className="bg-pink-200 text-purple-500 w-full hover:bg-pink-100">
Download
</Button>
</a>
</div>
</div>
) : (
<div className="flex flex-col items-center justify-center gap-2">
<p className="text-lg text-purple-400">
Waiting For Conversion
<Typewriter
words={["..."]}
loop={true}
cursor
cursorStyle=""
typeSpeed={200}
deleteSpeed={50}
delaySpeed={2000}
/>
</p>
</div>
)}
</div>
</section>
</header>
</div>
);
}
这时,你应该能得到这样一个页面,并且有了打字机的动画效果: