mirror of
https://github.com/TheRealOwenRees/chess-scribe-website.git
synced 2026-07-23 01:46:57 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
38fc389776
|
|||
|
af6809874c
|
|||
|
ad4d9ab12c
|
|||
|
70d65cfcfd
|
|||
|
9fe1b2043c
|
|||
|
48d0c4e47d
|
|||
|
e30bb5927a
|
|||
|
1d9ee25e1f
|
@@ -1,6 +1,10 @@
|
||||
- favicon and page titles - SEO related things
|
||||
- rearrange buttons / header space - collapse on smaller screens
|
||||
- og / json-sd
|
||||
- Lichess login
|
||||
- logger
|
||||
- contact form
|
||||
- bring sections headers on all pages other than home page further up (reduce top margin / padding)
|
||||
- bring sections headers on all pages other than home page further up (reduce top margin / padding)
|
||||
- set game current position on game load to ply 0 and whatever the FEN is (starting position probably, but could be custom position)
|
||||
- privacy policy - have no access to anything you generate, only keep a count of success/failed pdf generations with timestamp and error message
|
||||
- a11y and lighthouse fixes
|
||||
- check wcag compliance
|
||||
- white background?
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="64.000000pt" height="64.000000pt" viewBox="0 0 64.000000 64.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
|
||||
<g transform="translate(0.000000,64.000000) scale(0.100000,-0.100000)"
|
||||
fill="#6f884d" stroke="black">
|
||||
<path d="M460 605 c0 -8 -7 -15 -15 -15 -10 0 -15 -10 -15 -30 0 -20 5 -30 15
|
||||
-30 20 0 19 -16 -1 -24 -12 -4 -15 -14 -11 -34 3 -15 1 -34 -4 -44 -16 -29 0
|
||||
-48 41 -48 42 0 44 -2 55 -80 4 -25 9 -53 11 -62 5 -16 -2 -18 -46 -18 -40 0
|
||||
-51 3 -47 13 12 32 19 130 9 123 -6 -3 -14 -32 -18 -62 -4 -36 -13 -62 -25
|
||||
-72 -11 -9 -19 -22 -19 -28 0 -7 -7 -14 -15 -18 -8 -3 -15 -17 -15 -31 0 -25
|
||||
0 -25 90 -25 53 0 90 4 90 10 0 6 -33 10 -80 10 -47 0 -80 4 -80 10 0 6 43 10
|
||||
110 10 67 0 110 -4 110 -10 0 -5 -9 -10 -20 -10 -11 0 -20 -4 -20 -10 0 -5 14
|
||||
-10 30 -10 25 0 30 4 30 25 0 14 -7 28 -15 31 -8 4 -15 12 -15 20 0 7 -7 17
|
||||
-16 22 -11 6 -21 35 -29 83 -11 64 -11 76 2 91 9 10 12 22 6 30 -4 7 -8 29 -8
|
||||
48 0 32 -3 35 -32 38 -25 2 -33 8 -33 23 0 10 -7 19 -15 19 -8 0 -15 5 -15 10
|
||||
0 6 7 10 15 10 8 0 15 7 15 15 0 8 5 15 10 15 6 0 10 -7 10 -15 0 -8 7 -15 15
|
||||
-15 8 0 15 -4 15 -10 0 -5 -7 -10 -15 -10 -8 0 -15 -4 -15 -10 0 -5 11 -10 25
|
||||
-10 21 0 25 5 25 30 0 20 -5 30 -15 30 -8 0 -15 7 -15 15 0 10 -10 15 -30 15
|
||||
-20 0 -30 -5 -30 -15z m66 -131 c-3 -9 -6 -20 -6 -25 0 -5 -13 -9 -30 -9 -16
|
||||
0 -30 4 -30 9 0 5 -3 16 -6 25 -5 13 2 16 36 16 34 0 41 -3 36 -16z m14 -64
|
||||
c0 -5 -22 -10 -50 -10 -27 0 -50 5 -50 10 0 6 23 10 50 10 28 0 50 -4 50 -10z
|
||||
m30 -220 c0 -6 -33 -10 -80 -10 -47 0 -80 4 -80 10 0 6 33 10 80 10 47 0 80
|
||||
-4 80 -10z"/>
|
||||
<path d="M118 519 c-56 -29 -78 -80 -78 -179 0 -47 4 -80 10 -80 6 0 10 34 10
|
||||
83 0 64 4 89 20 115 11 17 24 32 30 32 14 0 13 -5 -10 -42 -17 -28 -20 -51
|
||||
-20 -160 0 -79 4 -128 10 -128 6 0 10 49 10 126 0 135 9 169 41 159 22 -7 23
|
||||
-2 8 27 -16 29 -2 34 20 7 22 -25 28 -11 11 22 -9 16 -7 19 14 19 29 0 76 -19
|
||||
76 -32 0 -10 -3 -10 -34 2 -16 6 -27 6 -31 0 -4 -6 -2 -10 3 -10 5 0 30 -16
|
||||
56 -35 48 -37 64 -78 26 -68 -22 6 -28 -12 -7 -20 24 -10 -21 -7 -47 3 -34 13
|
||||
-55 4 -62 -27 -6 -21 2 -33 50 -77 45 -42 56 -58 56 -84 0 -27 -4 -32 -24 -32
|
||||
-17 0 -27 9 -36 30 -13 32 -37 56 -46 47 -4 -3 3 -18 15 -33 11 -15 21 -31 21
|
||||
-35 0 -5 -31 -9 -68 -9 -70 0 -92 -10 -92 -41 0 -9 -7 -19 -15 -23 -17 -6 -21
|
||||
-56 -5 -56 6 0 10 9 10 20 0 19 7 20 150 20 143 0 150 -1 150 -20 0 -11 5 -20
|
||||
10 -20 16 0 12 50 -5 56 -8 4 -15 14 -15 24 0 10 -7 23 -15 30 -8 7 -15 28
|
||||
-15 48 0 30 -8 43 -57 87 -41 38 -54 57 -49 70 4 11 12 15 24 10 46 -18 85
|
||||
-18 98 0 22 30 17 71 -11 94 -20 15 -24 25 -20 44 5 20 1 27 -28 41 -46 21
|
||||
-93 20 -139 -5z m190 -421 c3 -16 -8 -18 -117 -18 -94 0 -121 3 -121 13 0 25
|
||||
14 28 124 25 96 -3 111 -5 114 -20z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.4 KiB |
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"short_name": "TanStack App",
|
||||
"name": "Create TanStack App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
+3
-2
@@ -1,3 +1,4 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://chess-scribe.org/sitemap.xml
|
||||
|
||||
@@ -9,7 +9,7 @@ interface IProps {
|
||||
|
||||
const CustomHeaders = ({ headers, updateHeaders }: IProps) => {
|
||||
return (
|
||||
<div className="mt-4 w-full">
|
||||
<div className="mt-4 w-full max-w-175">
|
||||
<details className="bg-(--neutral-content)/20 p-4 rounded-lg">
|
||||
<summary>
|
||||
Custom Headers (PDF)
|
||||
@@ -19,7 +19,7 @@ const CustomHeaders = ({ headers, updateHeaders }: IProps) => {
|
||||
</p>
|
||||
</summary>
|
||||
<div>
|
||||
<form className="grid place-items-center gap-4 sm:grid-cols-2 mt-2">
|
||||
<form className="flex flex-wrap mt-2 justify-center gap-4">
|
||||
<FormField
|
||||
fieldName="title"
|
||||
type="text"
|
||||
|
||||
@@ -9,14 +9,14 @@ interface IProps {
|
||||
|
||||
const HeaderFields = ({ headers, updateHeaders }: IProps) => {
|
||||
return (
|
||||
<div className="mt-4 w-full">
|
||||
<div className="mt-4 w-full max-w-175">
|
||||
<details className="bg-(--neutral-content)/15 p-4 rounded-lg">
|
||||
<summary>
|
||||
Headers
|
||||
<p className="text-sm font-normal">Edit the game PGN headers</p>
|
||||
</summary>
|
||||
<div>
|
||||
<form className="grid place-items-center gap-4 sm:grid-cols-2 mt-2">
|
||||
<form className="flex flex-wrap mt-2 justify-center gap-4">
|
||||
<FormField
|
||||
fieldName="event"
|
||||
type="text"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import LichessPgnViewer from "lichess-pgn-viewer";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useLichessPgnViewer } from "#/hooks/useLichessPgnViewer.ts";
|
||||
import type { IPosition } from "#/interfaces.ts";
|
||||
|
||||
interface IProps {
|
||||
@@ -8,80 +7,9 @@ interface IProps {
|
||||
}
|
||||
|
||||
const PgnViewer = ({ gamePgn, handlePlyChange }: IProps) => {
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const viewerRef = useRef(null);
|
||||
useLichessPgnViewer({ gamePgn, handlePlyChange });
|
||||
|
||||
// TODO make thin, and pull functions out
|
||||
useEffect(() => {
|
||||
const element: HTMLElement | null = document.querySelector(".lpv-board");
|
||||
if (!element) return;
|
||||
|
||||
viewerRef.current = LichessPgnViewer(element, {
|
||||
pgn: gamePgn,
|
||||
scrollToMove: false,
|
||||
});
|
||||
|
||||
// set initial position - TODO change to on file load
|
||||
// handlePlyChange({
|
||||
// ply: 0,
|
||||
// fen: viewerRef.current?.curData().fen,
|
||||
// });
|
||||
|
||||
const boardButtons = document.querySelectorAll(".lpv__controls");
|
||||
const movesList = document.querySelectorAll(".lpv__moves");
|
||||
if (!boardButtons || !movesList) return;
|
||||
|
||||
const clickHandlerDelay = () => {
|
||||
setTimeout(() => handleClick(), 250);
|
||||
};
|
||||
|
||||
// Hack using DOM manipulation
|
||||
const boardEventListener = () => {
|
||||
const variationTags = document.querySelector("variation");
|
||||
const moves = [...document.querySelectorAll("move")].filter((m) => {
|
||||
if (!variationTags) return m.className !== "empty";
|
||||
return (
|
||||
m.parentNode?.querySelector("variation") && m.className !== "empty"
|
||||
);
|
||||
});
|
||||
const ply = moves.findIndex((m) => m.classList.contains("current")) + 1;
|
||||
return { moves, ply };
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
const { ply } = boardEventListener();
|
||||
const fen = viewerRef.current?.curData().fen;
|
||||
handlePlyChange({ ply, fen });
|
||||
};
|
||||
|
||||
boardButtons.forEach((button) => {
|
||||
button.addEventListener("click", handleClick);
|
||||
button.addEventListener("touchstart", handleClick);
|
||||
});
|
||||
|
||||
movesList.forEach((move) => {
|
||||
move.addEventListener("click", clickHandlerDelay);
|
||||
move.addEventListener("touchstart", clickHandlerDelay);
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (containerRef.current) {
|
||||
containerRef.current = null;
|
||||
}
|
||||
|
||||
boardButtons.forEach((button) => {
|
||||
button.removeEventListener("click", handleClick);
|
||||
button.removeEventListener("touchstart", handleClick);
|
||||
});
|
||||
|
||||
movesList.forEach((move) => {
|
||||
move.removeEventListener("click", clickHandlerDelay);
|
||||
move.removeEventListener("touchstart", clickHandlerDelay);
|
||||
});
|
||||
};
|
||||
}, [gamePgn, handlePlyChange]);
|
||||
|
||||
return <div ref={containerRef} className="lpv-board" />;
|
||||
return <div className="lpv-board" />;
|
||||
};
|
||||
|
||||
export default PgnViewer;
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
export const HOST_URL = "https://chess-scribe.org";
|
||||
|
||||
export const GITHUB_URL =
|
||||
"https://github.com/therealowenrees/chess-scribe-website";
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import LichessPgnViewer from "lichess-pgn-viewer";
|
||||
import { type RefObject, useEffect, useRef } from "react";
|
||||
import type { IPosition } from "#/interfaces.ts";
|
||||
|
||||
type ViewerRef = ReturnType<typeof LichessPgnViewer>;
|
||||
type HandlePlyChange = ({ ply, fen }: IPosition) => void;
|
||||
|
||||
interface IHandleBoardClick {
|
||||
viewerRef: RefObject<ViewerRef | null>;
|
||||
handlePlyChange: HandlePlyChange;
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
gamePgn: string;
|
||||
handlePlyChange: HandlePlyChange;
|
||||
}
|
||||
|
||||
const getBoardEventData = () => {
|
||||
const variationTags = document.querySelector("variation");
|
||||
const moves = [...document.querySelectorAll("move")].filter((m) => {
|
||||
if (!variationTags) return m.className !== "empty";
|
||||
return m.parentNode?.querySelector("variation") && m.className !== "empty";
|
||||
});
|
||||
const ply = moves.findIndex((m) => m.classList.contains("current")) + 1;
|
||||
return { moves, ply };
|
||||
};
|
||||
|
||||
const handleBoardClick = ({
|
||||
viewerRef,
|
||||
handlePlyChange,
|
||||
}: IHandleBoardClick) => {
|
||||
const { ply } = getBoardEventData();
|
||||
const fen = viewerRef.current?.curData().fen ?? "";
|
||||
handlePlyChange({ ply, fen });
|
||||
};
|
||||
|
||||
export const useLichessPgnViewer = ({ gamePgn, handlePlyChange }: IProps) => {
|
||||
const viewerRef = useRef<ViewerRef | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const element: HTMLElement | null = document.querySelector(".lpv-board");
|
||||
if (!element) return;
|
||||
|
||||
viewerRef.current = LichessPgnViewer(element, {
|
||||
pgn: gamePgn,
|
||||
scrollToMove: false,
|
||||
});
|
||||
|
||||
const boardButtons = document.querySelectorAll(".lpv__controls");
|
||||
const movesList = document.querySelectorAll(".lpv__moves");
|
||||
if (!boardButtons || !movesList) return;
|
||||
|
||||
const handleClick = () => handleBoardClick({ viewerRef, handlePlyChange });
|
||||
const clickHandlerDelay = () => setTimeout(() => handleClick, 250);
|
||||
|
||||
boardButtons.forEach((button) => {
|
||||
button.addEventListener("click", handleClick);
|
||||
button.addEventListener("touchstart", handleClick);
|
||||
});
|
||||
|
||||
movesList.forEach((move) => {
|
||||
move.addEventListener("click", clickHandlerDelay);
|
||||
move.addEventListener("touchstart", clickHandlerDelay);
|
||||
});
|
||||
|
||||
return () => {
|
||||
boardButtons.forEach((button) => {
|
||||
button.removeEventListener("click", handleClick);
|
||||
button.removeEventListener("touchstart", handleClick);
|
||||
});
|
||||
|
||||
movesList.forEach((move) => {
|
||||
move.removeEventListener("click", clickHandlerDelay);
|
||||
move.removeEventListener("touchstart", clickHandlerDelay);
|
||||
});
|
||||
};
|
||||
}, [gamePgn, handlePlyChange]);
|
||||
|
||||
return viewerRef;
|
||||
};
|
||||
@@ -46,9 +46,9 @@ const Chessboard = () => {
|
||||
/>
|
||||
</Section>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="grid lg:grid-cols-2 gap-4">
|
||||
<Suspense fallback={<LoadingBoard />}>
|
||||
<div>
|
||||
<div className="mx-auto">
|
||||
<PgnViewer
|
||||
gamePgn={gameState.pgn || ""}
|
||||
handlePlyChange={handlePlyChange}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
const Contact = () => <div>Hello "/contact"!</div>;
|
||||
|
||||
export default Contact;
|
||||
@@ -0,0 +1,25 @@
|
||||
import Contact from "#/pages/Contact/Contact.tsx";
|
||||
|
||||
const ContactContainer = () => {
|
||||
const handleSubmit = async (_prevState: unknown, data: FormData) => {
|
||||
const name = data.get("name");
|
||||
const email = data.get("email");
|
||||
const subject = data.get("subject");
|
||||
const message = data.get("message");
|
||||
|
||||
try {
|
||||
return { success: true, message: "Message sent!" };
|
||||
} catch (error) {
|
||||
// TODO logger
|
||||
console.error("Error sending message:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Failed to send message. Please try again.",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return <Contact handleSubmit={handleSubmit} />;
|
||||
};
|
||||
|
||||
export default ContactContainer;
|
||||
@@ -0,0 +1,90 @@
|
||||
import { useActionState } from "react";
|
||||
import Section from "#/components/Section.tsx";
|
||||
|
||||
interface IFormField {
|
||||
fieldName: string;
|
||||
type: "text" | "email" | "textarea";
|
||||
placeholder: string;
|
||||
label: string;
|
||||
required: boolean;
|
||||
}
|
||||
|
||||
type IProps = {
|
||||
handleSubmit: (
|
||||
prevState: unknown,
|
||||
formData: FormData,
|
||||
) => Promise<{ success: boolean; message: string }>;
|
||||
};
|
||||
|
||||
const FormField = ({
|
||||
type,
|
||||
fieldName,
|
||||
placeholder,
|
||||
label,
|
||||
required,
|
||||
}: IFormField) => (
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor={fieldName}>{label}</label>
|
||||
<input
|
||||
type={type}
|
||||
id={fieldName}
|
||||
name={fieldName}
|
||||
placeholder={placeholder}
|
||||
required={required}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const Contact = ({ handleSubmit }: IProps) => {
|
||||
const [state, formAction, isPending] = useActionState(handleSubmit, null);
|
||||
|
||||
return (
|
||||
<main className="px-4 pb-8 place-self-center min-h-[calc(100vh-226px)]">
|
||||
<Section
|
||||
title="Contact Us"
|
||||
description="Do you have suggestions on how to improve this service? Would you like to get involved with this project? Ask us anything you like."
|
||||
>
|
||||
<form action={formAction}>
|
||||
<FormField
|
||||
type="text"
|
||||
fieldName="name"
|
||||
placeholder="Your name"
|
||||
label="Name"
|
||||
required
|
||||
/>
|
||||
|
||||
<FormField
|
||||
fieldName="email"
|
||||
type="text"
|
||||
placeholder="name@example.com"
|
||||
label="Email"
|
||||
required
|
||||
/>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
name="subject"
|
||||
placeholder="Reason for contacting us"
|
||||
/>
|
||||
|
||||
<textarea
|
||||
name="message"
|
||||
placeholder="Your message here..."
|
||||
required
|
||||
/>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isPending}
|
||||
className="btn btn-primary"
|
||||
>
|
||||
{isPending ? "Sending..." : "Send"}
|
||||
</button>
|
||||
{state && <p>{state.message}</p>}
|
||||
</form>
|
||||
</Section>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
export default Contact;
|
||||
@@ -21,12 +21,24 @@ export const Route = createRootRoute({
|
||||
{
|
||||
title: "ChessScribe",
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
content: "Create PDFs of your chess games from a PGN file",
|
||||
},
|
||||
{
|
||||
name: "keywords",
|
||||
content: "chess, pgn, pdf, chess games, chess notation",
|
||||
},
|
||||
],
|
||||
links: [
|
||||
{
|
||||
rel: "stylesheet",
|
||||
href: appCss,
|
||||
},
|
||||
{
|
||||
rel: "icon",
|
||||
href: "/favicon.svg",
|
||||
},
|
||||
],
|
||||
}),
|
||||
shellComponent: RootDocument,
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { HOST_URL } from "#/config.ts";
|
||||
import Chessboard from "#/pages/Chessboard.tsx";
|
||||
|
||||
export const Route = createFileRoute("/chessboard")({
|
||||
head: () => ({
|
||||
meta: [{ title: "ChessScribe | Chessboard" }],
|
||||
links: [{ rel: "canonical", href: `${HOST_URL}/chessboard` }],
|
||||
}),
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import Contact from "#/pages/Contact.tsx";
|
||||
import { HOST_URL } from "#/config.ts";
|
||||
import ContactContainer from "#/pages/Contact/Contact.container.tsx";
|
||||
|
||||
export const Route = createFileRoute("/contact")({
|
||||
head: () => ({
|
||||
meta: [{ title: "ChessScribe | Contact" }],
|
||||
links: [{ rel: "canonical", href: `${HOST_URL}/contact` }],
|
||||
}),
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <Contact />;
|
||||
return <ContactContainer />;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { HOST_URL } from "#/config.ts";
|
||||
import Home from "#/pages";
|
||||
|
||||
export const Route = createFileRoute("/")({ component: App });
|
||||
export const Route = createFileRoute("/")({
|
||||
head: () => ({
|
||||
meta: [{ title: "ChessScribe | Home" }],
|
||||
links: [{ rel: "canonical", href: HOST_URL }],
|
||||
}),
|
||||
component: App,
|
||||
});
|
||||
|
||||
function App() {
|
||||
return <Home />;
|
||||
|
||||
+26
-16
@@ -1,21 +1,31 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import { devtools } from '@tanstack/devtools-vite'
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import { devtools } from "@tanstack/devtools-vite";
|
||||
|
||||
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
|
||||
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
||||
|
||||
import viteReact from '@vitejs/plugin-react'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import { nitro } from 'nitro/vite'
|
||||
import viteReact from "@vitejs/plugin-react";
|
||||
import { nitro } from "nitro/vite";
|
||||
import { defineConfig } from "vite";
|
||||
import { HOST_URL } from "#/config.ts";
|
||||
|
||||
const config = defineConfig({
|
||||
resolve: { tsconfigPaths: true },
|
||||
plugins: [
|
||||
devtools(),
|
||||
nitro({ rollupConfig: { external: [/^@sentry\//] } }),
|
||||
tailwindcss(),
|
||||
tanstackStart(),
|
||||
viteReact(),
|
||||
],
|
||||
})
|
||||
resolve: { tsconfigPaths: true },
|
||||
plugins: [
|
||||
devtools(),
|
||||
nitro({ rollupConfig: { external: [/^@sentry\//] } }),
|
||||
tailwindcss(),
|
||||
tanstackStart({
|
||||
prerender: {
|
||||
enabled: true,
|
||||
crawlLinks: true,
|
||||
},
|
||||
sitemap: {
|
||||
enabled: true,
|
||||
host: HOST_URL,
|
||||
},
|
||||
}),
|
||||
viteReact(),
|
||||
],
|
||||
});
|
||||
|
||||
export default config
|
||||
export default config;
|
||||
|
||||
Reference in New Issue
Block a user