create-m5kdev 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/__tests__/create.smoke.test.d.ts +1 -0
- package/dist/src/__tests__/create.smoke.test.js +56 -0
- package/dist/src/__tests__/create.test.d.ts +1 -0
- package/dist/src/__tests__/create.test.js +55 -0
- package/dist/src/__tests__/runCli.test.d.ts +1 -0
- package/dist/src/__tests__/runCli.test.js +44 -0
- package/dist/src/__tests__/strings.test.d.ts +1 -0
- package/dist/src/__tests__/strings.test.js +24 -0
- package/dist/src/constants.d.ts +3 -0
- package/dist/src/constants.js +9 -0
- package/dist/src/create.d.ts +7 -0
- package/dist/src/create.js +36 -0
- package/dist/src/fs.d.ts +5 -0
- package/dist/src/fs.js +60 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +9 -0
- package/dist/src/paths.d.ts +1 -0
- package/dist/src/paths.js +14 -0
- package/dist/src/prompts.d.ts +2 -0
- package/dist/src/prompts.js +55 -0
- package/dist/src/runCli.d.ts +9 -0
- package/dist/src/runCli.js +107 -0
- package/dist/src/strings.d.ts +6 -0
- package/dist/src/strings.js +47 -0
- package/dist/src/types.d.ts +18 -0
- package/dist/src/types.js +2 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +38 -0
- package/templates/minimal-app/.gitignore.tpl +9 -0
- package/templates/minimal-app/AGENTS.md.tpl +29 -0
- package/templates/minimal-app/README.md.tpl +35 -0
- package/templates/minimal-app/apps/email/package.json.tpl +27 -0
- package/templates/minimal-app/apps/email/src/components/BaseEmail.tsx.tpl +117 -0
- package/templates/minimal-app/apps/email/src/emails/accountDeletionEmail.tsx.tpl +26 -0
- package/templates/minimal-app/apps/email/src/emails/organizationInviteEmail.tsx.tpl +31 -0
- package/templates/minimal-app/apps/email/src/emails/passwordResetEmail.tsx.tpl +25 -0
- package/templates/minimal-app/apps/email/src/emails/verificationEmail.tsx.tpl +26 -0
- package/templates/minimal-app/apps/email/src/index.ts.tpl +32 -0
- package/templates/minimal-app/apps/email/tsconfig.json.tpl +11 -0
- package/templates/minimal-app/apps/server/AGENTS.md.tpl +30 -0
- package/templates/minimal-app/apps/server/drizzle/seed.ts.tpl +111 -0
- package/templates/minimal-app/apps/server/drizzle/sync.ts.tpl +18 -0
- package/templates/minimal-app/apps/server/drizzle.config.ts.tpl +38 -0
- package/templates/minimal-app/apps/server/package.json.tpl +50 -0
- package/templates/minimal-app/apps/server/src/db.ts.tpl +33 -0
- package/templates/minimal-app/apps/server/src/index.ts.tpl +34 -0
- package/templates/minimal-app/apps/server/src/lib/auth.ts.tpl +172 -0
- package/templates/minimal-app/apps/server/src/lib/localEmailService.ts.tpl +58 -0
- package/templates/minimal-app/apps/server/src/modules/posts/posts.db.ts.tpl +23 -0
- package/templates/minimal-app/apps/server/src/modules/posts/posts.repository.ts.tpl +106 -0
- package/templates/minimal-app/apps/server/src/modules/posts/posts.service.ts.tpl +150 -0
- package/templates/minimal-app/apps/server/src/modules/posts/posts.trpc.ts.tpl +44 -0
- package/templates/minimal-app/apps/server/src/repository.ts.tpl +6 -0
- package/templates/minimal-app/apps/server/src/service.ts.tpl +12 -0
- package/templates/minimal-app/apps/server/src/trpc.ts.tpl +11 -0
- package/templates/minimal-app/apps/server/src/types.ts.tpl +3 -0
- package/templates/minimal-app/apps/server/src/utils/trpc.ts.tpl +27 -0
- package/templates/minimal-app/apps/server/tsconfig.json.tpl +13 -0
- package/templates/minimal-app/apps/server/tsup.config.ts.tpl +9 -0
- package/templates/minimal-app/apps/shared/.env.example.tpl +17 -0
- package/templates/minimal-app/apps/shared/.env.tpl +19 -0
- package/templates/minimal-app/apps/shared/package.json.tpl +24 -0
- package/templates/minimal-app/apps/shared/src/modules/posts/posts.constants.ts.tpl +5 -0
- package/templates/minimal-app/apps/shared/src/modules/posts/posts.schema.ts.tpl +76 -0
- package/templates/minimal-app/apps/shared/tsconfig.json.tpl +12 -0
- package/templates/minimal-app/apps/webapp/AGENTS.md.tpl +18 -0
- package/templates/minimal-app/apps/webapp/index.html.tpl +22 -0
- package/templates/minimal-app/apps/webapp/package.json.tpl +52 -0
- package/templates/minimal-app/apps/webapp/src/App.tsx.tpl +13 -0
- package/templates/minimal-app/apps/webapp/src/Layout.tsx.tpl +139 -0
- package/templates/minimal-app/apps/webapp/src/Providers.tsx.tpl +28 -0
- package/templates/minimal-app/apps/webapp/src/Router.tsx.tpl +53 -0
- package/templates/minimal-app/apps/webapp/src/components/TrpcQueryProvider.tsx.tpl +61 -0
- package/templates/minimal-app/apps/webapp/src/hero.ts.tpl +99 -0
- package/templates/minimal-app/apps/webapp/src/index.css.tpl +75 -0
- package/templates/minimal-app/apps/webapp/src/main.tsx.tpl +26 -0
- package/templates/minimal-app/apps/webapp/src/modules/posts/PostsRoute.tsx.tpl +650 -0
- package/templates/minimal-app/apps/webapp/src/utils/i18n.ts.tpl +13 -0
- package/templates/minimal-app/apps/webapp/src/utils/trpc.ts.tpl +4 -0
- package/templates/minimal-app/apps/webapp/src/vite-env.d.ts.tpl +1 -0
- package/templates/minimal-app/apps/webapp/translations/en/blog-app.json.tpl +107 -0
- package/templates/minimal-app/apps/webapp/tsconfig.json.tpl +16 -0
- package/templates/minimal-app/apps/webapp/vite.config.ts.tpl +31 -0
- package/templates/minimal-app/biome.json.tpl +76 -0
- package/templates/minimal-app/package.json.tpl +21 -0
- package/templates/minimal-app/pnpm-workspace.yaml.tpl +58 -0
- package/templates/minimal-app/turbo.json.tpl +26 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { APP_NAME } from "{{PACKAGE_SCOPE}}/shared/modules/app/app.constants";
|
|
2
|
+
import { Button, Chip } from "@heroui/react";
|
|
3
|
+
import { authClient } from "@m5kdev/frontend/modules/auth/auth.lib";
|
|
4
|
+
import { useSession } from "@m5kdev/frontend/modules/auth/hooks/useSession";
|
|
5
|
+
import { useTheme } from "@m5kdev/web-ui/components/theme-provider";
|
|
6
|
+
import {
|
|
7
|
+
BookTextIcon,
|
|
8
|
+
LogOutIcon,
|
|
9
|
+
MoonStarIcon,
|
|
10
|
+
NotebookTextIcon,
|
|
11
|
+
SunMediumIcon,
|
|
12
|
+
} from "lucide-react";
|
|
13
|
+
import { useTranslation } from "react-i18next";
|
|
14
|
+
import { NavLink, Outlet } from "react-router";
|
|
15
|
+
|
|
16
|
+
export function Layout() {
|
|
17
|
+
const { data: session } = useSession();
|
|
18
|
+
const { theme, setTheme } = useTheme();
|
|
19
|
+
const { t } = useTranslation("blog-app");
|
|
20
|
+
|
|
21
|
+
const userName = session?.user?.name || session?.user?.email || "Editor";
|
|
22
|
+
const userEmail = session?.user?.email || "";
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className="min-h-screen bg-canvas text-foreground">
|
|
26
|
+
<div className="mx-auto flex min-h-screen max-w-[1600px] flex-col gap-6 px-4 py-4 lg:flex-row lg:px-6">
|
|
27
|
+
<aside className="w-full rounded-[32px] border border-white/60 bg-white/70 p-5 shadow-[0_24px_60px_rgba(81,50,24,0.14)] backdrop-blur xl:w-[320px]">
|
|
28
|
+
<div className="flex items-start justify-between gap-4">
|
|
29
|
+
<div>
|
|
30
|
+
<p className="text-[0.68rem] font-semibold uppercase tracking-[0.32em] text-amber-700/80">
|
|
31
|
+
{t("layout.brand.eyebrow")}
|
|
32
|
+
</p>
|
|
33
|
+
<h1 className="mt-3 font-editorial text-4xl leading-none text-ink">{APP_NAME}</h1>
|
|
34
|
+
<p className="mt-3 max-w-[22ch] text-sm leading-6 text-ink/70">
|
|
35
|
+
{t("layout.brand.tagline")}
|
|
36
|
+
</p>
|
|
37
|
+
</div>
|
|
38
|
+
<div className="rounded-full border border-amber-200 bg-amber-50/90 p-3 text-amber-700">
|
|
39
|
+
<NotebookTextIcon className="h-5 w-5" />
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div className="mt-8 rounded-[28px] border border-amber-200/70 bg-amber-50/85 p-4">
|
|
44
|
+
<p className="text-[0.68rem] font-semibold uppercase tracking-[0.28em] text-amber-700/80">
|
|
45
|
+
{t("layout.workspace.eyebrow")}
|
|
46
|
+
</p>
|
|
47
|
+
<p className="mt-3 font-editorial text-2xl leading-none text-ink">
|
|
48
|
+
{t("layout.workspace.title")}
|
|
49
|
+
</p>
|
|
50
|
+
<p className="mt-3 text-sm leading-6 text-ink/75">{t("layout.workspace.body")}</p>
|
|
51
|
+
<div className="mt-4 flex flex-wrap gap-2">
|
|
52
|
+
<Chip color="warning" variant="flat">
|
|
53
|
+
{t("layout.workspace.local")}
|
|
54
|
+
</Chip>
|
|
55
|
+
<Chip color="success" variant="flat">
|
|
56
|
+
{t("layout.workspace.auth")}
|
|
57
|
+
</Chip>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<nav className="mt-8 grid gap-2">
|
|
62
|
+
<NavLink
|
|
63
|
+
to="/posts"
|
|
64
|
+
className={({ isActive }) =>
|
|
65
|
+
isActive
|
|
66
|
+
? "group flex items-center gap-3 rounded-[22px] border border-emerald-300 bg-emerald-950 px-4 py-3 text-emerald-50"
|
|
67
|
+
: "group flex items-center gap-3 rounded-[22px] border border-transparent bg-transparent px-4 py-3 text-ink/80 transition hover:border-amber-200 hover:bg-white/70"
|
|
68
|
+
}
|
|
69
|
+
>
|
|
70
|
+
<BookTextIcon className="h-4 w-4" />
|
|
71
|
+
<span className="font-medium">{t("layout.navigation.posts")}</span>
|
|
72
|
+
</NavLink>
|
|
73
|
+
</nav>
|
|
74
|
+
|
|
75
|
+
<div className="mt-8 rounded-[28px] border border-white/60 bg-stone-950 px-4 py-4 text-stone-100 shadow-[0_16px_32px_rgba(23,19,20,0.2)]">
|
|
76
|
+
<p className="text-xs uppercase tracking-[0.24em] text-stone-400">
|
|
77
|
+
{t("layout.account.eyebrow")}
|
|
78
|
+
</p>
|
|
79
|
+
<p className="mt-3 text-lg font-semibold">{userName}</p>
|
|
80
|
+
<p className="mt-1 text-sm text-stone-300">{userEmail}</p>
|
|
81
|
+
<div className="mt-4 flex items-center gap-2">
|
|
82
|
+
<Button
|
|
83
|
+
radius="full"
|
|
84
|
+
size="sm"
|
|
85
|
+
variant="flat"
|
|
86
|
+
className="bg-stone-800 text-stone-100"
|
|
87
|
+
onPress={() => setTheme(theme === "dark" ? "light" : "dark")}
|
|
88
|
+
>
|
|
89
|
+
{theme === "dark" ? (
|
|
90
|
+
<SunMediumIcon className="h-4 w-4" />
|
|
91
|
+
) : (
|
|
92
|
+
<MoonStarIcon className="h-4 w-4" />
|
|
93
|
+
)}
|
|
94
|
+
{theme === "dark" ? t("layout.account.light") : t("layout.account.dark")}
|
|
95
|
+
</Button>
|
|
96
|
+
<Button
|
|
97
|
+
radius="full"
|
|
98
|
+
size="sm"
|
|
99
|
+
color="danger"
|
|
100
|
+
variant="flat"
|
|
101
|
+
onPress={() => {
|
|
102
|
+
authClient.signOut();
|
|
103
|
+
window.location.assign("/login");
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
<LogOutIcon className="h-4 w-4" />
|
|
107
|
+
{t("layout.account.signOut")}
|
|
108
|
+
</Button>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
</aside>
|
|
112
|
+
|
|
113
|
+
<main className="flex min-h-[calc(100vh-2rem)] flex-1 flex-col rounded-[32px] border border-white/60 bg-white/72 shadow-[0_24px_60px_rgba(81,50,24,0.14)] backdrop-blur">
|
|
114
|
+
<header className="flex flex-wrap items-center justify-between gap-4 border-b border-white/70 px-6 py-5">
|
|
115
|
+
<div>
|
|
116
|
+
<p className="text-[0.68rem] font-semibold uppercase tracking-[0.28em] text-emerald-800/70">
|
|
117
|
+
{t("layout.header.eyebrow")}
|
|
118
|
+
</p>
|
|
119
|
+
<h2 className="mt-2 font-editorial text-4xl leading-none text-ink">
|
|
120
|
+
{t("layout.header.title")}
|
|
121
|
+
</h2>
|
|
122
|
+
</div>
|
|
123
|
+
<Chip
|
|
124
|
+
radius="full"
|
|
125
|
+
className="border border-emerald-300 bg-emerald-50 px-4 py-5 text-emerald-900"
|
|
126
|
+
variant="bordered"
|
|
127
|
+
>
|
|
128
|
+
{t("layout.header.runtime")}
|
|
129
|
+
</Chip>
|
|
130
|
+
</header>
|
|
131
|
+
|
|
132
|
+
<div className="flex-1 px-4 py-4 sm:px-6 sm:py-6">
|
|
133
|
+
<Outlet />
|
|
134
|
+
</div>
|
|
135
|
+
</main>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { HeroUIProvider } from "@heroui/react";
|
|
2
|
+
import { AuthProvider } from "@m5kdev/frontend/modules/auth/components/AuthProvider";
|
|
3
|
+
import { DialogProvider } from "@m5kdev/web-ui/components/DialogProvider";
|
|
4
|
+
import { ThemeProvider } from "@m5kdev/web-ui/components/theme-provider";
|
|
5
|
+
import { AppLoader } from "@m5kdev/web-ui/modules/app/components/AppLoader";
|
|
6
|
+
import { useHref, useNavigate } from "react-router";
|
|
7
|
+
import { Toaster } from "sonner";
|
|
8
|
+
import { TrpcQueryProvider } from "@/components/TrpcQueryProvider";
|
|
9
|
+
import { Router } from "./Router";
|
|
10
|
+
|
|
11
|
+
export function Providers() {
|
|
12
|
+
const navigate = useNavigate();
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<HeroUIProvider navigate={navigate} useHref={useHref}>
|
|
16
|
+
<ThemeProvider defaultTheme="light" storageKey="m5kdev-theme">
|
|
17
|
+
<AuthProvider loader={<AppLoader />}>
|
|
18
|
+
<TrpcQueryProvider>
|
|
19
|
+
<DialogProvider>
|
|
20
|
+
<Router />
|
|
21
|
+
</DialogProvider>
|
|
22
|
+
<Toaster richColors closeButton />
|
|
23
|
+
</TrpcQueryProvider>
|
|
24
|
+
</AuthProvider>
|
|
25
|
+
</ThemeProvider>
|
|
26
|
+
</HeroUIProvider>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { APP_NAME } from "{{PACKAGE_SCOPE}}/shared/modules/app/app.constants";
|
|
2
|
+
import { useSession } from "@m5kdev/frontend/modules/auth/hooks/useSession";
|
|
3
|
+
import { AuthRouter } from "@m5kdev/web-ui/modules/auth/components/AuthRouter";
|
|
4
|
+
import type { ReactNode } from "react";
|
|
5
|
+
import { Navigate, Route, Routes } from "react-router";
|
|
6
|
+
import { PostsRoute } from "@/modules/posts/PostsRoute";
|
|
7
|
+
import { Layout } from "./Layout";
|
|
8
|
+
|
|
9
|
+
function ProtectedRoutes({ children }: { children: ReactNode }) {
|
|
10
|
+
const { data: session } = useSession();
|
|
11
|
+
|
|
12
|
+
if (!session) {
|
|
13
|
+
return <Navigate to="/login" replace />;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return <>{children}</>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function AuthHeader() {
|
|
20
|
+
return (
|
|
21
|
+
<div className="text-center">
|
|
22
|
+
<p className="text-[0.68rem] font-semibold uppercase tracking-[0.32em] text-amber-700/80">
|
|
23
|
+
Editorial Workspace
|
|
24
|
+
</p>
|
|
25
|
+
<h1 className="mt-3 font-editorial text-4xl text-ink">{APP_NAME}</h1>
|
|
26
|
+
<p className="mt-3 text-sm text-ink/70">
|
|
27
|
+
A minimal m5kdev starter with auth, tRPC, and one polished posts module.
|
|
28
|
+
</p>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function Router() {
|
|
34
|
+
return (
|
|
35
|
+
<Routes>
|
|
36
|
+
{AuthRouter({
|
|
37
|
+
header: <AuthHeader />,
|
|
38
|
+
})}
|
|
39
|
+
|
|
40
|
+
<Route
|
|
41
|
+
path="/"
|
|
42
|
+
element={
|
|
43
|
+
<ProtectedRoutes>
|
|
44
|
+
<Layout />
|
|
45
|
+
</ProtectedRoutes>
|
|
46
|
+
}
|
|
47
|
+
>
|
|
48
|
+
<Route index element={<Navigate to="/posts" replace />} />
|
|
49
|
+
<Route path="posts" element={<PostsRoute />} />
|
|
50
|
+
</Route>
|
|
51
|
+
</Routes>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { AppRouter } from "{{PACKAGE_SCOPE}}/server/types";
|
|
2
|
+
import { transformer } from "@m5kdev/commons/utils/trpc";
|
|
3
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
4
|
+
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
|
5
|
+
import { createTRPCClient, httpBatchLink } from "@trpc/client";
|
|
6
|
+
import { type ReactNode, useState } from "react";
|
|
7
|
+
import { TRPCProvider } from "@/utils/trpc";
|
|
8
|
+
|
|
9
|
+
let browserQueryClient: QueryClient | undefined;
|
|
10
|
+
|
|
11
|
+
function createQueryClient() {
|
|
12
|
+
return new QueryClient({
|
|
13
|
+
defaultOptions: {
|
|
14
|
+
queries: {
|
|
15
|
+
staleTime: 60 * 1000,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getQueryClient() {
|
|
22
|
+
if (typeof window === "undefined") {
|
|
23
|
+
return createQueryClient();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!browserQueryClient) {
|
|
27
|
+
browserQueryClient = createQueryClient();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return browserQueryClient;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function TrpcQueryProvider({ children }: { children: ReactNode }) {
|
|
34
|
+
const queryClient = getQueryClient();
|
|
35
|
+
const [trpcClient] = useState(() =>
|
|
36
|
+
createTRPCClient<AppRouter>({
|
|
37
|
+
links: [
|
|
38
|
+
httpBatchLink({
|
|
39
|
+
url: `${import.meta.env.VITE_SERVER_URL}/trpc`,
|
|
40
|
+
fetch(url, options) {
|
|
41
|
+
return fetch(url, {
|
|
42
|
+
...options,
|
|
43
|
+
body: options?.body ? (options.body as BodyInit | null | undefined) : undefined,
|
|
44
|
+
credentials: "include",
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
transformer,
|
|
48
|
+
}),
|
|
49
|
+
],
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<QueryClientProvider client={queryClient}>
|
|
55
|
+
<TRPCProvider trpcClient={trpcClient} queryClient={queryClient}>
|
|
56
|
+
{children}
|
|
57
|
+
</TRPCProvider>
|
|
58
|
+
<ReactQueryDevtools client={queryClient} />
|
|
59
|
+
</QueryClientProvider>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { heroui } from "@heroui/react";
|
|
2
|
+
|
|
3
|
+
export default heroui({
|
|
4
|
+
themes: {
|
|
5
|
+
light: {
|
|
6
|
+
colors: {
|
|
7
|
+
primary: {
|
|
8
|
+
"50": "#edf7f4",
|
|
9
|
+
"100": "#cfeadd",
|
|
10
|
+
"200": "#adddc5",
|
|
11
|
+
"300": "#88cfac",
|
|
12
|
+
"400": "#61c091",
|
|
13
|
+
"500": "#1f4f46",
|
|
14
|
+
"600": "#19433b",
|
|
15
|
+
"700": "#12362f",
|
|
16
|
+
"800": "#0c2923",
|
|
17
|
+
"900": "#051a16",
|
|
18
|
+
DEFAULT: "#1f4f46",
|
|
19
|
+
foreground: "#fff8ef",
|
|
20
|
+
},
|
|
21
|
+
secondary: {
|
|
22
|
+
"50": "#fff4e5",
|
|
23
|
+
"100": "#fce2bd",
|
|
24
|
+
"200": "#f8cf95",
|
|
25
|
+
"300": "#f4bc6d",
|
|
26
|
+
"400": "#efaa45",
|
|
27
|
+
"500": "#c7782d",
|
|
28
|
+
"600": "#a16024",
|
|
29
|
+
"700": "#7b491b",
|
|
30
|
+
"800": "#553112",
|
|
31
|
+
"900": "#301a09",
|
|
32
|
+
DEFAULT: "#c7782d",
|
|
33
|
+
foreground: "#fff9f2",
|
|
34
|
+
},
|
|
35
|
+
background: "#f4ede2",
|
|
36
|
+
foreground: "#171314",
|
|
37
|
+
content1: {
|
|
38
|
+
DEFAULT: "#fffaf1",
|
|
39
|
+
foreground: "#171314",
|
|
40
|
+
},
|
|
41
|
+
content2: {
|
|
42
|
+
DEFAULT: "#f4ede2",
|
|
43
|
+
foreground: "#171314",
|
|
44
|
+
},
|
|
45
|
+
focus: "#1f4f46",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
dark: {
|
|
49
|
+
colors: {
|
|
50
|
+
primary: {
|
|
51
|
+
"50": "#e2f3ef",
|
|
52
|
+
"100": "#beded7",
|
|
53
|
+
"200": "#98c8bc",
|
|
54
|
+
"300": "#71b2a1",
|
|
55
|
+
"400": "#4b9c86",
|
|
56
|
+
"500": "#2d8470",
|
|
57
|
+
"600": "#236959",
|
|
58
|
+
"700": "#194e42",
|
|
59
|
+
"800": "#10332b",
|
|
60
|
+
"900": "#061815",
|
|
61
|
+
DEFAULT: "#71b2a1",
|
|
62
|
+
foreground: "#071613",
|
|
63
|
+
},
|
|
64
|
+
secondary: {
|
|
65
|
+
"50": "#fff4e5",
|
|
66
|
+
"100": "#f6dcb0",
|
|
67
|
+
"200": "#eec379",
|
|
68
|
+
"300": "#e6aa42",
|
|
69
|
+
"400": "#d88d27",
|
|
70
|
+
"500": "#b97221",
|
|
71
|
+
"600": "#935a1b",
|
|
72
|
+
"700": "#6d4214",
|
|
73
|
+
"800": "#472b0d",
|
|
74
|
+
"900": "#231406",
|
|
75
|
+
DEFAULT: "#e6aa42",
|
|
76
|
+
foreground: "#211306",
|
|
77
|
+
},
|
|
78
|
+
background: "#12100f",
|
|
79
|
+
foreground: "#f7f1e8",
|
|
80
|
+
content1: {
|
|
81
|
+
DEFAULT: "#1d1917",
|
|
82
|
+
foreground: "#f7f1e8",
|
|
83
|
+
},
|
|
84
|
+
content2: {
|
|
85
|
+
DEFAULT: "#26211f",
|
|
86
|
+
foreground: "#f7f1e8",
|
|
87
|
+
},
|
|
88
|
+
focus: "#71b2a1",
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
layout: {
|
|
93
|
+
radius: {
|
|
94
|
+
small: "18px",
|
|
95
|
+
medium: "24px",
|
|
96
|
+
large: "32px",
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/* biome-ignore-all lint/suspicious/noUnknownAtRules: Tailwind v4 */
|
|
2
|
+
|
|
3
|
+
@import url("https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@500;600;700&family=Manrope:wght@400;500;600;700;800&display=swap");
|
|
4
|
+
@import "tailwindcss";
|
|
5
|
+
@import "tw-animate-css";
|
|
6
|
+
|
|
7
|
+
@plugin './hero.ts';
|
|
8
|
+
|
|
9
|
+
@source "./src/**/*.{ts,tsx}";
|
|
10
|
+
@source "../../node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}";
|
|
11
|
+
@source "../../node_modules/@m5kdev/web-ui/dist/src/**/*.{js,ts,jsx,tsx}";
|
|
12
|
+
|
|
13
|
+
@custom-variant dark (&:is(.dark *));
|
|
14
|
+
|
|
15
|
+
:root {
|
|
16
|
+
--canvas: #f4ede2;
|
|
17
|
+
--ink: #171314;
|
|
18
|
+
--muted-ink: #6f635c;
|
|
19
|
+
--panel: #fffaf1;
|
|
20
|
+
--panel-strong: #f8efdf;
|
|
21
|
+
--line: rgba(124, 93, 55, 0.18);
|
|
22
|
+
--halo: rgba(231, 182, 100, 0.28);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.dark {
|
|
26
|
+
--canvas: #12100f;
|
|
27
|
+
--ink: #f7f1e8;
|
|
28
|
+
--muted-ink: #b9aba0;
|
|
29
|
+
--panel: #1d1917;
|
|
30
|
+
--panel-strong: #26211f;
|
|
31
|
+
--line: rgba(255, 240, 219, 0.12);
|
|
32
|
+
--halo: rgba(77, 162, 143, 0.22);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@theme inline {
|
|
36
|
+
--color-canvas: var(--canvas);
|
|
37
|
+
--color-ink: var(--ink);
|
|
38
|
+
--color-muted-ink: var(--muted-ink);
|
|
39
|
+
--color-panel: var(--panel);
|
|
40
|
+
--color-panel-strong: var(--panel-strong);
|
|
41
|
+
--color-line: var(--line);
|
|
42
|
+
--color-halo: var(--halo);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@layer base {
|
|
46
|
+
* {
|
|
47
|
+
@apply border-line;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
html,
|
|
51
|
+
body,
|
|
52
|
+
#root {
|
|
53
|
+
min-height: 100%;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
body {
|
|
57
|
+
@apply bg-canvas text-ink;
|
|
58
|
+
background-image:
|
|
59
|
+
radial-gradient(circle at top left, rgba(246, 189, 96, 0.16), transparent 28%),
|
|
60
|
+
radial-gradient(circle at 80% 10%, rgba(31, 79, 70, 0.14), transparent 22%),
|
|
61
|
+
linear-gradient(180deg, rgba(255, 250, 241, 0.36), rgba(244, 237, 226, 0.1));
|
|
62
|
+
font-family: "Manrope", "Helvetica Neue", Arial, sans-serif;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
button,
|
|
66
|
+
input,
|
|
67
|
+
textarea,
|
|
68
|
+
select {
|
|
69
|
+
font: inherit;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.font-editorial {
|
|
74
|
+
font-family: "Cormorant Garamond", Georgia, serif;
|
|
75
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { PostHogConfig } from "posthog-js";
|
|
2
|
+
import { PostHogProvider } from "posthog-js/react";
|
|
3
|
+
import { StrictMode } from "react";
|
|
4
|
+
import { createRoot } from "react-dom/client";
|
|
5
|
+
import { App } from "./App";
|
|
6
|
+
import "./index.css";
|
|
7
|
+
import "./utils/i18n";
|
|
8
|
+
|
|
9
|
+
const options = {
|
|
10
|
+
api_host: import.meta.env.VITE_PUBLIC_POSTHOG_HOST,
|
|
11
|
+
defaults: "2025-05-24",
|
|
12
|
+
} satisfies Partial<PostHogConfig>;
|
|
13
|
+
|
|
14
|
+
const rootElement = document.getElementById("root");
|
|
15
|
+
|
|
16
|
+
if (!rootElement) {
|
|
17
|
+
throw new Error("Root element #root not found.");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
createRoot(rootElement).render(
|
|
21
|
+
<StrictMode>
|
|
22
|
+
<PostHogProvider apiKey={import.meta.env.VITE_PUBLIC_POSTHOG_KEY ?? "demo"} options={options}>
|
|
23
|
+
<App />
|
|
24
|
+
</PostHogProvider>
|
|
25
|
+
</StrictMode>
|
|
26
|
+
);
|