Compare commits

...
Author SHA1 Message Date
bflatastic 055d9e93e3 download page, buttons, and styling updates 2025-04-23 13:21:05 +07:00
bflatastic 1ebf411751 tsconfig update 2025-04-23 11:48:13 +07:00
bflatastic b33eb130b8 hero update 2025-04-23 11:46:06 +07:00
bflatastic 034c47285e styling updates 2025-04-23 11:46:06 +07:00
bflatastic 877b52bd97 styling updates + videos + logo 2025-04-23 11:46:06 +07:00
bflatastic 0bf28d9ff1 installed source sans 3 font 2025-04-23 11:46:06 +07:00
bflatastic 30a9e0e6e2 installed tailwind 2025-04-23 11:46:06 +07:00
bflatastic ce92aeb4b6 init 2025-04-23 11:46:06 +07:00
35 changed files with 1283 additions and 32 deletions
+22
View File
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Artcraft</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="stylesheet" href="/src/styles.css" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap"
rel="stylesheet"
/>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
@@ -0,0 +1,5 @@
{
"name": "@frontend/artcraft-website",
"version": "0.0.1",
"private": true
}
@@ -0,0 +1,15 @@
const { join } = require('path');
// Note: If you use library-specific PostCSS/Tailwind configuration then you should remove the `postcssConfig` build
// option from your application's configuration (i.e. project.json).
//
// See: https://nx.dev/guides/using-tailwind-css-in-react#step-4:-applying-configuration-to-libraries
module.exports = {
plugins: {
tailwindcss: {
config: join(__dirname, 'tailwind.config.js'),
},
autoprefixer: {},
},
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

@@ -0,0 +1 @@
/* Your styles goes here. */
@@ -0,0 +1,16 @@
import { render } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import App from './app';
describe('App', () => {
it('should render successfully', () => {
const { baseElement } = render(<BrowserRouter><App /></BrowserRouter>);
expect(baseElement).toBeTruthy();
});
it('should have a greeting as the title', () => {
const { getAllByText } = render(<BrowserRouter><App /></BrowserRouter>);
expect(getAllByText(new RegExp('Welcome @frontend/artcraft-website', 'gi')).length > 0).toBeTruthy();
});
});
@@ -0,0 +1,22 @@
import { Route, Routes } from "react-router-dom";
import Landing from "../pages/landing";
import Download from "../pages/download";
import Navbar from "../components/navbar";
import Footer from "../components/footer";
export function App() {
return (
<div className="relative">
<Navbar />
<Routes>
<Route path="/" element={<Landing />} />
<Route path="/download" element={<Download />} />
</Routes>
<Footer />
</div>
);
}
export default App;
@@ -0,0 +1,60 @@
import { Button } from "@storyteller/ui-button";
import {
faApple,
faWindows,
faLinux,
} from "@fortawesome/free-brands-svg-icons";
import { isMobile, isWindows, isMacOs } from "react-device-detect";
import { DOWNLOAD_LINKS } from "../../config/downloads";
interface DownloadButtonProps {
className?: string;
}
export const DownloadButton = ({ className = "" }: DownloadButtonProps) => {
// Helper function to detect Linux
const isLinux =
!isWindows &&
!isMacOs &&
navigator.platform.toLowerCase().includes("linux");
const handleDownload = () => {
const downloadLink = isWindows
? DOWNLOAD_LINKS.WINDOWS
: isMacOs
? DOWNLOAD_LINKS.MACOS
: isLinux
? DOWNLOAD_LINKS.LINUX
: null;
if (downloadLink) {
window.open(downloadLink, "_blank");
}
};
const getIcon = () => {
if (isMobile) return undefined;
if (isWindows) return faWindows;
if (isMacOs) return faApple;
if (isLinux) return faLinux;
return undefined;
};
return (
<Button
className={`rounded-xl px-8 py-4 text-md transition-all duration-300 shadow-lg hover:shadow-blue-500/25 ${className}`}
disabled={isMobile || (!isWindows && !isMacOs && !isLinux)}
icon={getIcon()}
onClick={handleDownload}
>
{isMobile
? "Download on desktop"
: isWindows
? "Download for Windows"
: isMacOs
? "Download for MacOS"
: isLinux
? "Download for Linux"
: "Not available on your device"}
</Button>
);
};
@@ -0,0 +1 @@
export * from "./download-button";
@@ -0,0 +1,78 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faTiktok,
faDiscord,
faYoutube,
} from "@fortawesome/free-brands-svg-icons";
const navigation = {
main: [
{ name: "Home", href: "/" },
{ name: "Download", href: "/download" },
],
social: [
{
name: "Discord",
href: "https://discord.gg/75svZP2Vje",
icon: (props: any) => <FontAwesomeIcon icon={faDiscord} {...props} />,
},
{
name: "YouTube",
href: "https://www.youtube.com/@OfficialArtCraftStudios",
icon: (props: any) => <FontAwesomeIcon icon={faYoutube} {...props} />,
},
{
name: "TikTok",
href: "https://www.tiktok.com/@artcraft.studios",
icon: (props: any) => <FontAwesomeIcon icon={faTiktok} {...props} />,
},
],
};
export default function Example() {
return (
<footer className="bg-[#1F1F23]">
<div className="mx-auto max-w-7xl overflow-hidden px-6 py-16 sm:py-16 lg:px-8 flex flex-col gap-12 items-center">
{/* <Button
icon={faArrowDownToLine}
className="w-fit"
onClick={() => window.open("/download", "_self")}
>
Download ArtCraft
</Button> */}
{/* <nav
aria-label="Footer"
className="flex flex-wrap justify-center gap-x-8 gap-y-3 text-sm/6"
>
{navigation.main.map((item) => (
<a
key={item.name}
href={item.href}
className="text-gray-400 hover:text-white"
>
{item.name}
</a>
))}
</nav> */}
<div>
<div className="flex justify-center gap-x-10">
{navigation.social.map((item) => (
<a
key={item.name}
href={item.href}
target="_blank"
className="text-gray-400 hover:text-gray-300 transition-all"
>
<span className="sr-only">{item.name}</span>
<item.icon aria-hidden="true" className="size-6" />
</a>
))}
</div>
<p className="mt-10 text-center text-sm/6 text-gray-400">
&copy; 2025 ArtCraft. All rights reserved.
</p>
</div>
</div>
</footer>
);
}
@@ -0,0 +1 @@
export { default } from "./footer";
@@ -0,0 +1,3 @@
export * from "./navbar";
export * from "./footer";
export * from "./download-button";
@@ -0,0 +1 @@
export { default } from "./navbar";
@@ -0,0 +1,107 @@
import {
Disclosure,
DisclosureButton,
DisclosurePanel,
} from "@headlessui/react";
import { faBars } from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Button } from "@storyteller/ui-button";
import { twMerge } from "tailwind-merge";
import { useEffect, useState } from "react";
const navigation = [
{ name: "Home", href: "/", current: true },
{ name: "Download", href: "/download", current: false },
];
export default function Navbar() {
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
const handleScroll = () => {
setScrolled(window.scrollY > 0);
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return (
<Disclosure
as="nav"
className={twMerge(
"z-20 fixed top-0 left-0 w-full transition-colors duration-200",
scrolled ? "bg-[#1b1b1f]/70 backdrop-blur-lg" : "bg-transparent"
)}
>
<div className="mx-auto max-w-[1920px] sm:px-6 lg:px-8 px-6 md:px-16 xl:px-32">
<div className="flex h-16 justify-between">
<div className="flex">
<div className="flex shrink-0 items-center">
<a href="/">
<img
alt="ArtCraft"
src="/images/artcraft-logo.png"
className="h-7 w-auto"
/>
</a>
</div>
<div className="hidden md:ml-7 md:flex md:items-center md:space-x-3">
{navigation.map((item) => (
<a
key={item.name}
href={item.href}
aria-current={item.current ? "page" : undefined}
className={twMerge(
item.current
? "text-white/80 hover:text-white"
: "text-white/80 hover:text-white",
"rounded-md px-3 py-2 text-[15px] font-medium transition-all"
)}
>
{item.name}
</a>
))}
</div>
</div>
<div className="flex items-center">
<div className="hidden md:ml-4 md:flex md:shrink-0 md:items-center">
<Button onClick={() => window.open("/download", "_self")}>
Download
</Button>
</div>
<div className="mr-2 -ml-2 flex items-center md:hidden">
{/* Mobile menu button */}
<DisclosureButton className="group relative inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:ring-2 focus:ring-white focus:outline-hidden focus:ring-inset">
<span className="absolute -inset-0.5" />
<span className="sr-only">Open main menu</span>
<FontAwesomeIcon icon={faBars} />
</DisclosureButton>
</div>
</div>
</div>
</div>
<DisclosurePanel className="md:hidden">
<div className="space-y-1 px-2 pt-2 pb-3 sm:px-3">
{navigation.map((item) => (
<DisclosureButton
key={item.name}
as="a"
href={item.href}
aria-current={item.current ? "page" : undefined}
className={twMerge(
item.current
? "bg-gray-900 text-white"
: "text-gray-300 hover:bg-gray-700 hover:text-white",
"block rounded-md px-3 py-2 text-base font-medium"
)}
>
{item.name}
</DisclosureButton>
))}
</div>
</DisclosurePanel>
</Disclosure>
);
}
@@ -0,0 +1,5 @@
export const DOWNLOAD_LINKS = {
WINDOWS: "https://getartcraft.com/", // Replace with actual Windows download link
MACOS: "https://getartcraft.com/", // Replace with actual MacOS download link
LINUX: "https://getartcraft.com/", // Replace with actual Linux download link
} as const;
@@ -0,0 +1,16 @@
import { StrictMode } from 'react';
import { BrowserRouter } from 'react-router-dom';
import * as ReactDOM from 'react-dom/client';
import App from './app/app';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<StrictMode>
<BrowserRouter>
<App/>
</BrowserRouter>
</StrictMode>
);
@@ -0,0 +1,119 @@
import {
faApple,
faWindows,
faLinux,
} from "@fortawesome/free-brands-svg-icons";
import { faArrowDownToLine } from "@fortawesome/pro-solid-svg-icons";
import { DownloadButton } from "../../components/download-button";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Button } from "@storyteller/ui-button";
import { DOWNLOAD_LINKS } from "../../config/downloads";
const Download = () => {
const systemRequirements = [
{
os: "Windows",
icon: faWindows,
requirements: [
"Windows 10 (64-bit) or newer",
"8GB RAM minimum",
"4GB available storage",
],
},
{
os: "macOS",
icon: faApple,
requirements: [
"macOS 12.0 or newer",
"8GB RAM minimum",
"4GB available storage",
],
},
{
os: "Linux",
icon: faLinux,
requirements: [
"Ubuntu 20.04 or newer",
"8GB RAM minimum",
"4GB available storage",
],
},
];
return (
<div className="relative min-h-screen bg-[#101014] text-white bg-dots">
<div className="dotted-pattern absolute inset-0 z-[0] opacity-50" />
{/* Hero Section */}
<div className="relative flex overflow-hidden">
<div className="w-full flex flex-col items-center justify-center text-center pt-40 pb-20 px-10">
<h1 className="relative mb-10 font-bold text-5xl lg:text-7xl">
Download ArtCraft
</h1>
<p className="mb-12 max-w-xl text-lg lg:text-xl leading-relaxed text-gray-300">
Use ArtCraft to easily create AI-powered artwork on your computer,
with canvas editing and 3D scene composition.
</p>
<div className="flex flex-col gap-4 sm:flex-row sm:gap-6 mb-20">
<DownloadButton />
</div>
<div className="w-full max-w-7xl mb-20">
<img
src="/images/3d-interface-preview.jpg"
alt="ArtCraft Interface Preview"
className="w-full rounded-xl border-2 border-white/10"
/>
</div>
{/* System Requirements */}
<div className="w-full max-w-6xl mb-20">
<h2 className="text-3xl font-bold mb-10">System Requirements</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{systemRequirements.map((system, index) => (
<div
key={index}
className="bg-white/10 backdrop-blur-md rounded-xl p-8 flex flex-col gap-4 items-center"
>
<div className="flex items-center justify-center gap-4 mb-6">
<FontAwesomeIcon
icon={system.icon}
className="text-2xl text-white/80"
/>
<h3 className="text-2xl font-semibold">{system.os}</h3>
</div>
<ul className="space-y-3 mb-5">
{system.requirements.map((req, idx) => (
<li key={idx} className="text-gray-300">
{req}
</li>
))}
</ul>
<Button
className="w-fit"
onClick={() => {
const downloadLink =
system.os === "Windows"
? DOWNLOAD_LINKS.WINDOWS
: system.os === "macOS"
? DOWNLOAD_LINKS.MACOS
: DOWNLOAD_LINKS.LINUX;
window.open(downloadLink, "_blank");
}}
icon={faArrowDownToLine}
>
Download
</Button>
</div>
))}
</div>
</div>
</div>
</div>
</div>
);
};
export default Download;
@@ -0,0 +1 @@
export { default } from "./download";
@@ -0,0 +1,2 @@
export * from "./landing";
export * from "./download";
@@ -0,0 +1 @@
export { default } from "./landing";
@@ -0,0 +1,222 @@
import { Button } from "@storyteller/ui-button";
import { DownloadButton } from "../../components/download-button";
const Landing = () => {
// const features = [
// {
// title: "Lorem ipsum dolor sit",
// description:
// "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt.",
// image: "/images/features/ai-creation.jpg",
// tag: "Core",
// },
// {
// title: "Ut enim ad minim",
// description:
// "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea.",
// image: "/images/features/collaboration.jpg",
// tag: "Workflow",
// },
// {
// title: "Duis aute irure dolor",
// description:
// "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla.",
// image: "/images/features/style-models.jpg",
// tag: "Tools",
// },
// ];
const installationSteps = [
{
step: "STEP 1",
title: "Download the app",
description:
"Download and install our application to start creating amazing AI-powered content.",
button: {
text: "Download App",
className:
"rounded-lg bg-[#2D81FF] px-4 py-2 text-sm font-medium hover:bg-[#438AF6]",
variant: "primary" as const,
},
},
{
step: "STEP 2",
title: "Create an account",
description:
"Sign up for an account to access all features and start your journey.",
button: {
text: "Sign up",
className: "text-sm font-medium",
variant: "secondary" as const,
},
},
{
step: "STEP 3",
title: "Start creating",
description: "Begin creating with our AI editor!",
},
];
return (
<div className="relative min-h-screen bg-[#101014] text-white bg-dots">
<div className="dotted-pattern absolute inset-0 z-[0] opacity-50" />
{/* Hero Section */}
<div className="relative flex overflow-hidden xl:items-center xl:pt-0">
<div className="w-full flex flex-col items-center justify-center text-center pt-40 pb-20 px-10">
<div className="relative">
<div className="absolute -inset-1 rounded-lg blur-xl"></div>
<h1 className="relative mb-10 font-bold text-5xl lg:text-7xl">
AI Canvas & Scene Editor
<br />
<span>On your </span>
<span className="relative after:absolute after:bottom-0 after:left-0 after:w-full after:h-[9px] after:bg-primary/60 after:mb-1.5">
local machine
</span>
<span>.</span>
</h1>
</div>
<p className="mb-12 max-w-2xl text-lg lg:text-xl leading-relaxed text-gray-300">
Create stunning artwork with the power of AI. Perfect for artists,
designers, and creators who want to push the boundaries of their
creativity.
</p>
<div className="flex flex-col gap-4 sm:flex-row sm:gap-6">
<DownloadButton />
<Button
className="rounded-xl px-8 py-4 text-md border-2 border-white/10 hover:border-white/20 bg-white/5 hover:bg-white/10 transition-all duration-300 backdrop-blur-md"
onClick={() => window.open("/download", "_self")}
>
Learn more
</Button>
</div>
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[800px] bg-gradient-to-r from-blue-500/20 to-purple-500/20 rounded-full blur-3xl -z-10 animate-pulse-slow" />
</div>
</div>
{/* Video Sections */}
<div className="relative z-10 mx-auto max-w-[1920px] px-4 py-20 sm:px-24 lg:px-32">
{/* First Video Section */}
<div className="grid grid-cols-1 lg:grid-cols-5 items-center gap-16 mb-32 p-5 pr-16 bg-white/10 backdrop-blur-md rounded-[80px]">
<div className="lg:col-span-3 rounded-[60px] overflow-hidden bg-[#1C1C20]">
<video
muted
autoPlay
loop
className="w-full aspect-[4/3] object-cover"
>
<source src="/videos/artcraft-canvas-demo.mp4" type="video/mp4" />
</video>
</div>
<div className="lg:col-span-2 space-y-9">
<div className="space-y-6">
<h2 className="text-5xl font-bold leading-tight">
Draw it, Dream it, AI it
</h2>
<p className="text-lg text-gray-400 leading-relaxed">
Draw freely, arrange images, and create collages on our
intuitive 2D canvas. Once your composition is ready, use AI to
generate a polished artwork that matches your exact layout and
vision. Perfect for sketching ideas and turning them into
stunning finished pieces.
</p>
</div>
<Button className="rounded-lg px-5 py-3.5 text-md bg-[#2D81FF] hover:bg-[#438AF6]">
Start Creating
</Button>
</div>
</div>
{/* Second Video Section */}
<div className="grid grid-cols-1 lg:grid-cols-5 items-center gap-16 p-5 pl-16 bg-white/10 backdrop-blur-md rounded-[80px]">
<div className="lg:col-span-2 order-2 lg:order-1 space-y-9">
<div className="space-y-6">
<h2 className="text-5xl font-bold leading-tight">
Compose in 3D, Bring your ideas to life
</h2>
<p className="text-lg text-gray-400 leading-relaxed">
Compose your perfect 3D scene by arranging elements and
positioning your camera. Our AI understands your scene layout
and generates consistent, high-quality 3D artwork that matches
your composition, including maintaining character consistency
throughout your scenes.
</p>
</div>
<Button className="rounded-lg px-5 py-3.5 text-md bg-[#2D81FF] hover:bg-[#438AF6]">
Learn More
</Button>
</div>
<div className="lg:col-span-3 order-1 lg:order-2 rounded-[60px] overflow-hidden bg-[#1C1C20]">
<video
muted
autoPlay
loop
className="w-full aspect-[4/3] object-cover"
>
<source src="/videos/artcraft-3d-demo.mp4" type="video/mp4" />
</video>
</div>
</div>
</div>
<div className="relative z-10 mx-auto max-w-[1920px] px-4 pt-20 pb-40 sm:px-24 lg:px-32">
{/* Features Section */}
{/* <div className="mb-8 flex items-center justify-between">
<h1 className="text-4xl font-bold">Features</h1>
</div>
<div className="grid gap-6 md:grid-cols-3">
{features.map((item, index) => (
<div
key={index}
className="group cursor-pointer overflow-hidden rounded-xl bg-[#1C1C20] transition hover:bg-[#27272B]"
>
<div className="aspect-video overflow-hidden">
<img
src={item.image}
alt={item.title}
className="h-full w-full object-cover transition group-hover:scale-105"
/>
</div>
<div className="p-6">
<h3 className="mb-2 text-xl font-semibold">{item.title}</h3>
<p className="text-gray-400">{item.description}</p>
</div>
</div>
))}
</div> */}
{/* Installation Steps Section */}
<div>
<h1 className="mb-12 text-5xl font-bold">How to install ArtCraft</h1>
<div className="grid gap-6 md:grid-cols-3">
{installationSteps.map((step, index) => (
<div
key={index}
className="rounded-xl bg-white/10 backdrop-blur-md p-8"
>
<div className="mb-4 w-fit rounded-full bg-white/15 px-3 py-1 text-sm font-medium text-white">
{step.step}
</div>
<h3 className="mb-4 text-2xl font-semibold">{step.title}</h3>
<p className="mb-6 text-gray-400">{step.description}</p>
{step.button && (
<Button
className={step.button.className}
variant={step.button.variant}
>
{step.button.text}
</Button>
)}
</div>
))}
</div>
</div>
</div>
</div>
);
};
export default Landing;
@@ -0,0 +1,18 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
body {
font-family: "Source Sans 3", sans-serif;
color: #ffffff;
background-color: #1b1b1f;
}
}
.bg-dots {
background-image: radial-gradient(#ffffff25 1px, transparent 0);
background-color: #1b1b1f;
background-size: 33px 33px;
@apply bg-center;
}
@@ -0,0 +1,67 @@
const { createGlobPatternsForDependencies } = require("@nx/react/tailwind");
const { join } = require("path");
import colors from "tailwindcss/colors";
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
join(
__dirname,
"{src,pages,components,app}/**/*!(*.stories|*.spec).{ts,tsx,html}"
),
...createGlobPatternsForDependencies(__dirname),
"./app/index.html",
"./app/src/**/*.{js,jsx,ts,tsx}",
"./app/src/*.{js,jsx,ts,tsx}",
"../../libs/components/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
colors: {
transparent: "transparent",
current: "currentColor",
white: colors.white,
gray: colors.gray,
black: colors.black,
blue: colors.blue,
purple: colors.purple,
primary: {
DEFAULT: "#2d81ff",
50: "#eef6ff",
100: "#d9ebff",
200: "#bcdcff",
300: "#8ec7ff",
400: "#59a7ff",
500: "#2d81ff",
600: "#1b63f5",
700: "#144ee1",
800: "#173fb6",
900: "#19398f",
950: "#142457",
},
secondary: {
DEFAULT: "#3E3E41",
50: "#f6f6f6",
100: "#e7e7e7",
200: "#d1d1d1",
300: "#b0b0b0",
400: "#888888",
500: "#6d6d6d",
600: "#5d5d5d",
700: "#4f4f4f",
800: "#454545",
900: "#3d3d3d",
950: "#3E3E41",
},
ui: {
background: "#242424",
panel: "#1F1F1F",
"panel-border": "#3F3F3F",
controls: "#3E3E41",
"controls-button": "#3E3E41",
divider: "#515168",
},
},
},
plugins: [],
};
@@ -0,0 +1,44 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo",
"jsx": "react-jsx",
"lib": ["dom"],
"types": [
"node",
"@nx/react/typings/cssmodule.d.ts",
"@nx/react/typings/image.d.ts",
"vite/client"
],
"rootDir": "src"
},
"exclude": [
"out-tsc",
"dist",
"src/**/*.spec.ts",
"src/**/*.test.ts",
"src/**/*.spec.tsx",
"src/**/*.test.tsx",
"src/**/*.spec.js",
"src/**/*.test.js",
"src/**/*.spec.jsx",
"src/**/*.test.jsx",
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts"
],
"include": [
"src/**/*.js",
"src/**/*.jsx",
"src/**/*.ts",
"src/**/*.tsx",
"src/components/footer"
],
"references": [
{
"path": "../../libs/components/button/tsconfig.lib.json"
}
]
}
@@ -0,0 +1,16 @@
{
"files": [],
"include": [],
"references": [
{
"path": "../../libs/components/button"
},
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
}
],
"extends": "../../tsconfig.base.json"
}
@@ -0,0 +1,36 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./out-tsc/vitest",
"types": [
"vitest/globals",
"vitest/importMeta",
"vite/client",
"node",
"vitest",
"@nx/react/typings/cssmodule.d.ts",
"@nx/react/typings/image.d.ts"
],
"jsx": "react-jsx"
},
"include": [
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx",
"src/**/*.d.ts"
],
"references": [
{
"path": "./tsconfig.app.json"
}
]
}
@@ -0,0 +1,40 @@
/// <reference types='vitest' />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig(() => ({
root: __dirname,
cacheDir: '../../node_modules/.vite/apps/artcraft-website',
server:{
port: 4200,
host: 'localhost',
},
preview:{
port: 4300,
host: 'localhost',
},
plugins: [react()],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
build: {
outDir: './dist',
emptyOutDir: true,
reportCompressedSize: true,
commonjsOptions: {
transformMixedEsModules: true,
},
},
test: {
watch: false,
globals: true,
environment: 'jsdom',
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'],
coverage: {
reportsDirectory: './test-output/vitest/coverage',
provider: 'v8' as const,
}
},
}));
+357 -32
View File
@@ -18,6 +18,7 @@
"@remix-run/serve": "^2.15.0",
"pnpm": "^10.8.1",
"react": "18.3.1",
"react-device-detect": "^2.2.3",
"react-dom": "18.3.1",
"react-hot-toast": "^2.5.2",
"react-router-dom": "6.29.0"
@@ -51,6 +52,7 @@
"@vitejs/plugin-react": "^4.2.0",
"@vitest/coverage-v8": "^3.0.5",
"@vitest/ui": "^3.0.0",
"autoprefixer": "10.4.13",
"eslint": "~8.57.0",
"eslint-config-prettier": "^10.0.0",
"eslint-plugin-import": "2.31.0",
@@ -61,6 +63,8 @@
"jsdom": "~22.1.0",
"jsonc-eslint-parser": "^2.1.0",
"nx": "20.7.2",
"postcss": "8.4.38",
"tailwindcss": "3.4.3",
"tslib": "^2.3.0",
"typescript": "~5.7.2",
"vite": "^6.0.0",
@@ -71,6 +75,10 @@
"vitest": "^3.0.0"
}
},
"apps/artcraft-website": {
"name": "@frontend/artcraft-website",
"version": "0.0.1"
},
"apps/editor2d": {
"version": "0.0.0",
"dependencies": {
@@ -642,6 +650,44 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
"apps/editor2d/node_modules/autoprefixer": {
"version": "10.4.21",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
"integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"browserslist": "^4.24.4",
"caniuse-lite": "^1.0.30001702",
"fraction.js": "^4.3.7",
"normalize-range": "^0.1.2",
"picocolors": "^1.1.1",
"postcss-value-parser": "^4.2.0"
},
"bin": {
"autoprefixer": "bin/autoprefixer"
},
"engines": {
"node": "^10 || ^12 || >=14"
},
"peerDependencies": {
"postcss": "^8.1.0"
}
},
"apps/editor2d/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -944,6 +990,16 @@
"dev": true,
"license": "MIT"
},
"apps/editor2d/node_modules/jiti": {
"version": "1.21.7",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
"dev": true,
"license": "MIT",
"bin": {
"jiti": "bin/jiti.js"
}
},
"apps/editor2d/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -957,6 +1013,67 @@
"node": "*"
}
},
"apps/editor2d/node_modules/postcss": {
"version": "8.5.3",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.8",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"apps/editor2d/node_modules/postcss-import": {
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
"dev": true,
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.0.0",
"read-cache": "^1.0.0",
"resolve": "^1.1.7"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"postcss": "^8.0.0"
}
},
"apps/editor2d/node_modules/postcss-selector-parser": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"dev": true,
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
}
},
"apps/editor2d/node_modules/prettier": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
@@ -1052,6 +1169,44 @@
}
}
},
"apps/editor2d/node_modules/tailwindcss": {
"version": "3.4.17",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
"dev": true,
"license": "MIT",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
"chokidar": "^3.6.0",
"didyoumean": "^1.2.2",
"dlv": "^1.1.3",
"fast-glob": "^3.3.2",
"glob-parent": "^6.0.2",
"is-glob": "^4.0.3",
"jiti": "^1.21.6",
"lilconfig": "^3.1.3",
"micromatch": "^4.0.8",
"normalize-path": "^3.0.0",
"object-hash": "^3.0.0",
"picocolors": "^1.1.1",
"postcss": "^8.4.47",
"postcss-import": "^15.1.0",
"postcss-js": "^4.0.1",
"postcss-load-config": "^4.0.2",
"postcss-nested": "^6.2.0",
"postcss-selector-parser": "^6.1.2",
"resolve": "^1.22.8",
"sucrase": "^3.35.0"
},
"bin": {
"tailwind": "lib/cli.js",
"tailwindcss": "lib/cli.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"apps/editor2d/node_modules/uuid": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
@@ -1687,6 +1842,44 @@
}
}
},
"apps/editor3d/node_modules/autoprefixer": {
"version": "10.4.21",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
"integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"browserslist": "^4.24.4",
"caniuse-lite": "^1.0.30001702",
"fraction.js": "^4.3.7",
"normalize-range": "^0.1.2",
"picocolors": "^1.1.1",
"postcss-value-parser": "^4.2.0"
},
"bin": {
"autoprefixer": "bin/autoprefixer"
},
"engines": {
"node": "^10 || ^12 || >=14"
},
"peerDependencies": {
"postcss": "^8.1.0"
}
},
"apps/editor3d/node_modules/deep-equal": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz",
@@ -1799,6 +1992,35 @@
"node": ">=18"
}
},
"apps/editor3d/node_modules/postcss": {
"version": "8.5.3",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.8",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"apps/editor3d/node_modules/prettier": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
@@ -4759,6 +4981,10 @@
"react": ">=16.3"
}
},
"node_modules/@frontend/artcraft-website": {
"resolved": "apps/artcraft-website",
"link": true
},
"node_modules/@frontend/login": {
"resolved": "libs/login",
"link": true
@@ -12686,6 +12912,34 @@
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
"license": "MIT"
},
"node_modules/@vanilla-extract/integration/node_modules/postcss": {
"version": "8.5.3",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.8",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/@vanilla-extract/integration/node_modules/vite": {
"version": "5.4.18",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.18.tgz",
@@ -13945,9 +14199,9 @@
}
},
"node_modules/autoprefixer": {
"version": "10.4.21",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
"integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
"version": "10.4.13",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz",
"integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==",
"dev": true,
"funding": [
{
@@ -13957,19 +14211,15 @@
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"browserslist": "^4.24.4",
"caniuse-lite": "^1.0.30001702",
"fraction.js": "^4.3.7",
"browserslist": "^4.21.4",
"caniuse-lite": "^1.0.30001426",
"fraction.js": "^4.2.0",
"normalize-range": "^0.1.2",
"picocolors": "^1.1.1",
"picocolors": "^1.0.0",
"postcss-value-parser": "^4.2.0"
},
"bin": {
@@ -26788,9 +27038,9 @@
}
},
"node_modules/postcss": {
"version": "8.5.3",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"version": "8.4.38",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
"funding": [
{
"type": "opencollective",
@@ -26807,9 +27057,9 @@
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.8",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"source-map-js": "^1.2.0"
},
"engines": {
"node": "^10 || ^12 || >=14"
@@ -27568,6 +27818,18 @@
"react": "^15.3.0 || 16 || 17 || 18"
}
},
"node_modules/react-device-detect": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/react-device-detect/-/react-device-detect-2.2.3.tgz",
"integrity": "sha512-buYY3qrCnQVlIFHrC5UcUoAj7iANs/+srdkwsnNjI7anr3Tt7UY6MqNxtMLlr0tMBied0O49UZVK8XKs3ZIiPw==",
"dependencies": {
"ua-parser-js": "^1.0.33"
},
"peerDependencies": {
"react": ">= 0.14.0",
"react-dom": ">= 0.14.0"
}
},
"node_modules/react-docgen": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-7.1.1.tgz",
@@ -29651,34 +29913,34 @@
}
},
"node_modules/tailwindcss": {
"version": "3.4.17",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz",
"integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
"chokidar": "^3.6.0",
"chokidar": "^3.5.3",
"didyoumean": "^1.2.2",
"dlv": "^1.1.3",
"fast-glob": "^3.3.2",
"fast-glob": "^3.3.0",
"glob-parent": "^6.0.2",
"is-glob": "^4.0.3",
"jiti": "^1.21.6",
"lilconfig": "^3.1.3",
"micromatch": "^4.0.8",
"jiti": "^1.21.0",
"lilconfig": "^2.1.0",
"micromatch": "^4.0.5",
"normalize-path": "^3.0.0",
"object-hash": "^3.0.0",
"picocolors": "^1.1.1",
"postcss": "^8.4.47",
"picocolors": "^1.0.0",
"postcss": "^8.4.23",
"postcss-import": "^15.1.0",
"postcss-js": "^4.0.1",
"postcss-load-config": "^4.0.2",
"postcss-nested": "^6.2.0",
"postcss-selector-parser": "^6.1.2",
"resolve": "^1.22.8",
"sucrase": "^3.35.0"
"postcss-load-config": "^4.0.1",
"postcss-nested": "^6.0.1",
"postcss-selector-parser": "^6.0.11",
"resolve": "^1.22.2",
"sucrase": "^3.32.0"
},
"bin": {
"tailwind": "lib/cli.js",
@@ -29711,6 +29973,16 @@
"jiti": "bin/jiti.js"
}
},
"node_modules/tailwindcss/node_modules/lilconfig": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/tailwindcss/node_modules/postcss-import": {
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
@@ -31191,6 +31463,31 @@
"typescript": ">=4.8.4"
}
},
"node_modules/ua-parser-js": {
"version": "1.0.40",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz",
"integrity": "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/ua-parser-js"
},
{
"type": "paypal",
"url": "https://paypal.me/faisalman"
},
{
"type": "github",
"url": "https://github.com/sponsors/faisalman"
}
],
"bin": {
"ua-parser-js": "script/cli.js"
},
"engines": {
"node": "*"
}
},
"node_modules/ufo": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz",
@@ -32303,6 +32600,34 @@
"@esbuild/win32-x64": "0.25.2"
}
},
"node_modules/vite/node_modules/postcss": {
"version": "8.5.3",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.8",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/vitest": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz",
+4
View File
@@ -10,6 +10,7 @@
"@remix-run/serve": "^2.15.0",
"pnpm": "^10.8.1",
"react": "18.3.1",
"react-device-detect": "^2.2.3",
"react-dom": "18.3.1",
"react-hot-toast": "^2.5.2",
"react-router-dom": "6.29.0"
@@ -43,6 +44,7 @@
"@vitejs/plugin-react": "^4.2.0",
"@vitest/coverage-v8": "^3.0.5",
"@vitest/ui": "^3.0.0",
"autoprefixer": "10.4.13",
"eslint": "~8.57.0",
"eslint-config-prettier": "^10.0.0",
"eslint-plugin-import": "2.31.0",
@@ -53,6 +55,8 @@
"jsdom": "~22.1.0",
"jsonc-eslint-parser": "^2.1.0",
"nx": "20.7.2",
"postcss": "8.4.38",
"tailwindcss": "3.4.3",
"tslib": "^2.3.0",
"typescript": "~5.7.2",
"vite": "^6.0.0",
+3
View File
@@ -47,6 +47,9 @@
},
{
"path": "./libs/components/demo-modal"
},
{
"path": "./apps/artcraft-website"
}
]
}