continue re-design

This commit is contained in:
Developers Digest
2025-09-05 13:06:17 -04:00
parent b96d048dbd
commit 836b085f75
270 changed files with 32269 additions and 5182 deletions
@@ -0,0 +1,34 @@
import { AnimatePresence, motion } from "motion/react";
import AnimatedWidth from "@/components/shared/layout/animated-width";
import ArrowRight from "@/components/app/(home)/sections/hero-input/_svg/ArrowRight";
import Button from "@/components/shared/button/Button";
export default function HeroInputSubmitButton({
dirty,
}: {
dirty: boolean;
}) {
return (
<Button className="hero-input-button !p-0 bg-heat-100 hover:bg-heat-200" size="large" variant="primary">
<AnimatedWidth>
<AnimatePresence initial={false} mode="popLayout">
<motion.div
animate={{ opacity: 1, x: 0, filter: "blur(0px)" }}
exit={{ opacity: 0, x: -10, filter: "blur(2px)" }}
initial={{ opacity: 0, x: 10, filter: "blur(2px)" }}
key={dirty ? "dirty" : "clean"}
>
{dirty ? (
<div className="py-8 w-126 text-center text-white">Re-imagine Site</div>
) : (
<div className="w-60 py-8 flex-center">
<ArrowRight />
</div>
)}
</motion.div>
</AnimatePresence>
</AnimatedWidth>
</Button>
);
}
@@ -0,0 +1,84 @@
"use client";
import Link from "next/link";
import { useState } from "react";
import Globe from "./_svg/Globe";
import HeroInputSubmitButton from "./Button/Button";
import HeroInputTabsMobile from "./Tabs/Mobile/Mobile";
import HeroInputTabs from "./Tabs/Tabs";
import AsciiExplosion from "@/components/shared/effects/flame/ascii-explosion";
import { Endpoint } from "@/components/shared/Playground/Context/types";
export default function HeroInput() {
const [tab, setTab] = useState<Endpoint>(Endpoint.Scrape);
const [url, setUrl] = useState<string>("");
return (
<div className="max-w-552 mx-auto w-full z-[11] lg:z-[2] rounded-20 lg:-mt-76">
<div
className="overlay bg-accent-white"
style={{
boxShadow:
"0px 0px 44px 0px rgba(0, 0, 0, 0.02), 0px 88px 56px -20px rgba(0, 0, 0, 0.03), 0px 56px 56px -20px rgba(0, 0, 0, 0.02), 0px 32px 32px -20px rgba(0, 0, 0, 0.03), 0px 16px 24px -12px rgba(0, 0, 0, 0.03), 0px 0px 0px 1px rgba(0, 0, 0, 0.05), 0px 0px 0px 10px #F9F9F9",
}}
/>
<label className="p-16 flex gap-8 items-center w-full relative border-b border-black-alpha-5">
<Globe />
<input
className="w-full bg-transparent text-body-input text-accent-black placeholder:text-black-alpha-48"
placeholder="https://example.com"
type="text"
value={url}
onChange={(e) => setUrl(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
(
document.querySelector(
".hero-input-button",
) as HTMLButtonElement
)?.click();
}
}}
/>
</label>
<div className="p-10 flex justify-between items-center relative">
<HeroInputTabs
setTab={setTab}
tab={tab}
allowedModes={[
Endpoint.Scrape,
Endpoint.Search,
Endpoint.Map,
Endpoint.Crawl,
]}
/>
<HeroInputTabsMobile
setTab={setTab}
tab={tab}
allowedModes={[
Endpoint.Scrape,
Endpoint.Search,
Endpoint.Map,
Endpoint.Crawl,
]}
/>
<Link
className="contents"
href={`/playground?endpoint=${tab}&url=${url}&autorun=true`}
>
<HeroInputSubmitButton dirty={url.length > 0} />
</Link>
</div>
<div className="h-248 top-84 cw-768 pointer-events-none absolute overflow-clip -z-10">
<AsciiExplosion className="-top-200" />
</div>
</div>
);
}
@@ -0,0 +1,196 @@
//@ts-nocheck
import { animate, AnimatePresence, cubicBezier, motion } from "motion/react";
import { useEffect, useRef, useState } from "react";
import { tabs } from "@/components/app/(home)/sections/hero-input/Tabs/Tabs";
import { cn } from "@/utils/cn";
import { Endpoint } from "@/components/shared/Playground/Context/types";
export default function HeroInputTabsMobile(props: {
setTab: (tab: Endpoint) => void;
tab: Endpoint;
allowedModes?: Endpoint[];
}) {
// Filter tabs based on allowedModes if provided
const visibleTabs = props.allowedModes
? tabs.filter((tab) => props.allowedModes.includes(tab.value))
: tabs;
const activeTab = visibleTabs.find((tab) => tab.value === props.tab)!;
const [isOpen, setIsOpen] = useState(false);
const ref = useRef<HTMLButtonElement>(null);
useEffect(() => {
if (window.innerWidth > 996) {
return;
}
document.addEventListener("click", (e) => {
if (ref.current && e.composedPath().includes(ref.current)) {
return;
}
setIsOpen(false);
});
}, []);
return (
<>
<button
className="py-8 px-10 flex items-center rounded-10 inside-border before:border-black-alpha-4 relative lg:hidden gap-4"
ref={ref}
onClick={() => setIsOpen(!isOpen)}
>
<activeTab.icon size={24} alwaysHeat />
<div className="px-6 text-label-medium">{activeTab.label}</div>
<svg
className={cn(
"transition-all duration-200",
isOpen ? "rotate-180 text-accent-black" : "text-black-alpha-48",
)}
fill="none"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.4001 10.2L12.0001 13.8L15.6001 10.2"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.25"
/>
</svg>
</button>
<AnimatePresence mode="popLayout">
{isOpen && (
<motion.div
animate={{ opacity: 1, filter: "blur(0px)" }}
className="absolute z-[1001] top-[calc(100%-4px)] left-[calc(50%-(50vw-6px))] w-[calc(100vw-12px)]"
exit={{ opacity: 0, filter: "blur(2px)" }}
initial={{ opacity: 0, filter: "blur(2px)" }}
transition={{
duration: 0.2,
ease: cubicBezier(0.25, 0.1, 0.25, 1.0),
}}
>
<div
className="mx-auto w-full p-4 max-w-366 rounded-16 bg-accent-white"
style={{
boxShadow:
"0 32px 40px 6px rgba(0, 0, 0, 0.02), 0 12px 32px 0 rgba(0, 0, 0, 0.02), 0 24px 32px -8px rgba(0, 0, 0, 0.02), 0 8px 16px -2px rgba(0, 0, 0, 0.02), 0 0 0 1px rgba(0, 0, 0, 0.04)",
}}
>
<div className="py-10 px-12 text-label-small text-black-alpha-48">
Output
</div>
<MenuItems
setTab={props.setTab}
tab={props.tab}
visibleTabs={visibleTabs}
/>
</div>
</motion.div>
)}
</AnimatePresence>
</>
);
}
function MenuItems(props: {
tab: Endpoint;
setTab: (tab: Endpoint) => void;
visibleTabs: typeof tabs;
}) {
const backgroundRef = useRef<HTMLDivElement>(null);
return (
<div className="relative">
<div
className="absolute top-0 opacity-0 left-0 bg-black-alpha-4 rounded-12 w-full pointer-events-none"
ref={backgroundRef}
/>
{props.visibleTabs.map((tab) => (
<div
className="text-label-small select-none cursor-pointer flex gap-12 py-12 px-16"
key={tab.value}
onClick={() => {
animate(
backgroundRef.current!,
{
scaleX: [1, 0.99, 1],
scaleY: [1, 0.96, 1],
opacity: [1, 0.9, 1],
},
{
ease: cubicBezier(0.165, 0.84, 0.44, 1),
duration: 0.15,
},
);
props.setTab(tab.value);
}}
onMouseEnter={async (e) => {
const child = e.currentTarget as HTMLElement;
if (backgroundRef.current?.getBoundingClientRect().height === 0) {
backgroundRef.current!.style.height = child.offsetHeight + "px";
}
if (getComputedStyle(backgroundRef.current!).opacity === "0") {
await animate(
backgroundRef.current!,
{
y: child.offsetTop,
},
{
ease: cubicBezier(0.165, 0.84, 0.44, 1),
duration: 0.01,
},
);
}
animate(backgroundRef.current!, { scale: 0.995 }).then(() =>
animate(backgroundRef.current!, { scale: 1 }),
);
animate(
backgroundRef.current!,
{
y: child.offsetTop,
opacity: 1,
height: child.offsetHeight + "px",
},
{
ease: cubicBezier(0.165, 0.84, 0.44, 1),
duration: 0.2,
},
);
}}
onMouseLeave={() => {
animate(
backgroundRef.current!,
{
opacity: 0,
},
{
ease: cubicBezier(0.165, 0.84, 0.44, 1),
duration: 0.2,
},
);
}}
>
<div className="size-24 p-2">
<tab.icon size={20} alwaysHeat />
</div>
<div className="px-6 text-label-medium">{tab.label}</div>
</div>
))}
</div>
);
}
@@ -0,0 +1,183 @@
import { animate } from "motion";
import { Fragment, useRef } from "react";
import EndpointsSearch from "@/components/app/(home)/sections/endpoints/EndpointsSearch/EndpointsSearch";
import EndpointsCrawl from "@/components/app/(home)/sections/endpoints/EndpointsCrawl/EndpointsCrawl";
import EndpointsMap from "@/components/app/(home)/sections/endpoints/EndpointsMap/EndpointsMap";
import EndpointsScrape from "@/components/app/(home)/sections/endpoints/EndpointsScrape/EndpointsScrape";
import EndpointsExtract from "@/components/app/(home)/sections/endpoints/EndpointsExtract/EndpointsExtract";
import { cn } from "@/utils/cn";
import Tooltip from "@/components/ui/shadcn/tooltip";
import { Endpoint } from "@/components/shared/Playground/Context/types";
export const tabs = [
{
label: "Scrape",
value: Endpoint.Scrape,
action: "scraping",
description:
"Scrapes only the specified URL without crawling subpages. Outputs the content from the page.",
icon: EndpointsScrape,
},
{
label: "Search",
value: Endpoint.Search,
description: "Search the web and get full content from results",
action: "searching",
icon: EndpointsSearch,
new: true,
},
{
label: "Map",
value: Endpoint.Map,
action: "mapping",
description: "Attempts to output all website's urls in a few seconds.",
icon: EndpointsMap,
},
{
label: "Crawl",
value: Endpoint.Crawl,
action: "crawling",
description:
"Crawls a URL and all its accessible subpages, outputting the content from each page.",
icon: EndpointsCrawl,
},
{
label: "Extract",
value: Endpoint.Extract,
action: "extracting",
description:
"Extract structured data from pages using LLMs. Provide URLs and a schema to get organized data.",
icon: EndpointsExtract,
new: true,
},
];
export default function HeroInputTabs(props: {
setTab: (tab: Endpoint) => void;
tab: Endpoint;
disabled?: boolean;
allowedModes?: Endpoint[];
}) {
// Filter tabs based on allowedModes if provided
const visibleTabs = props.allowedModes
? tabs.filter((tab) => props.allowedModes!.includes(tab.value))
: tabs;
const activeIndex = visibleTabs.findIndex((tab) => tab.value === props.tab);
const backgroundRef = useRef<HTMLDivElement>(null);
return (
<div
className="bg-black-alpha-4 flex items-center rounded-10 p-2 relative lg-max:hidden"
style={{
boxShadow:
"0px 6px 12px 0px rgba(0, 0, 0, 0.02) inset, 0px 0.75px 0.75px 0px rgba(0, 0, 0, 0.02) inset, 0px 0.25px 0.25px 0px rgba(0, 0, 0, 0.04) inset",
}}
>
<div
className="absolute top-2 left-2 h-32 bg-accent-white rounded-8 w-89"
ref={backgroundRef}
style={{
boxShadow:
"0px 6px 12px -3px rgba(0, 0, 0, 0.04), 0px 3px 6px -1px rgba(0, 0, 0, 0.04), 0px 1px 2px 0px rgba(0, 0, 0, 0.04), 0px 0.5px 0.5px 0px rgba(0, 0, 0, 0.06)",
}}
/>
{visibleTabs.map((tab, index) => (
<Fragment key={tab.value}>
{index > 0 && (
<div
className={cn(
"px-2 transition-all",
!(index !== activeIndex && index !== activeIndex + 1) &&
"opacity-0",
)}
>
<div className="w-1 h-12 bg-black-alpha-5" />
</div>
)}
<button
className={cn(
"text-label-medium p-6 relative transition-all group flex items-center",
tab.value === props.tab
? "text-accent-black"
: "text-black-alpha-56",
!tab.new && "pr-4",
)}
key={tab.value}
ref={(element) => {
if (element && backgroundRef.current) {
if (activeIndex === index) {
animate(
backgroundRef.current,
{
x: element.offsetLeft - 2,
width: element.offsetWidth - 1,
},
{
type: "spring",
stiffness: 200,
damping: 23,
},
);
}
}
}}
onClick={(e) => {
props.setTab(tab.value);
const t = e.target as HTMLElement;
const target =
t instanceof HTMLButtonElement
? t
: (t.closest("button") as HTMLButtonElement);
if (backgroundRef.current) {
animate(backgroundRef.current, { scale: 0.975 }).then(() =>
animate(backgroundRef.current!, { scale: 1 }),
);
animate(
backgroundRef.current,
{
x: target.offsetLeft - 2,
width: target.offsetWidth - 1,
},
{
type: "spring",
stiffness: 250,
damping: 25,
},
);
}
}}
>
{tab.icon && <tab.icon active={tab.value === props.tab} />}
<span className="px-6"> {tab.label}</span>
{tab.new && (
<div
className={cn(
"py-2 px-6 rounded-4 text-[12px]/[16px] font-[450] transition-all",
tab.value === props.tab
? "bg-heat-12 text-heat-100"
: "bg-black-alpha-4 text-black-alpha-56",
)}
>
New
</div>
)}
<Tooltip delay={0.25} description={tab.description} offset={-8} />
</button>
</Fragment>
))}
</div>
);
}
@@ -0,0 +1,19 @@
export default function ArrowRight() {
return (
<svg
fill="none"
height="20"
viewBox="0 0 20 20"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.6667 4.79163L16.875 9.99994M16.875 9.99994L11.6667 15.2083M16.875 9.99994H3.125"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
/>
</svg>
);
}
@@ -0,0 +1,19 @@
export default function Globe() {
return (
<svg
fill="none"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 19.7083C16.2572 19.7083 19.7083 16.2572 19.7083 12C19.7083 7.74276 16.2572 4.29163 12 4.29163M12 19.7083C7.74276 19.7083 4.29163 16.2572 4.29163 12C4.29163 7.74276 7.74276 4.29163 12 4.29163M12 19.7083C10.044 19.7083 8.45829 16.2572 8.45829 12C8.45829 7.74276 10.044 4.29163 12 4.29163M12 19.7083C13.956 19.7083 15.5416 16.2572 15.5416 12C15.5416 7.74276 13.956 4.29163 12 4.29163M19.5 12H4.49996"
stroke="#262626"
strokeLinecap="square"
strokeOpacity="0.32"
strokeWidth="1.25"
/>
</svg>
);
}