vite and react scaffold

This commit is contained in:
2025-11-19 10:10:26 +02:00
parent 8be356d23f
commit d3742ce97e
5 changed files with 7656 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>cruciverb</title>
<meta name="description" content="Daily crossword puzzles in five sizes, four game modes, cryptic clues, multiplayer rooms, party games, and a puzzle editor. Self-hosted, federated, CC0." />
<!-- open graph -->
<meta property="og:type" content="website" />
<meta property="og:title" content="cruciverb" />
<meta property="og:description" content="Daily crossword puzzles in five sizes, four game modes, cryptic clues, multiplayer rooms, party games, and a puzzle editor. Self-hosted, federated, CC0." />
<meta property="og:site_name" content="cruciverb" />
<meta property="og:image" content="/og-image.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="cruciverb - a crossword a day" />
<!-- twitter card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="cruciverb" />
<meta name="twitter:description" content="Daily crossword puzzles in five sizes. Self-hosted, federated, CC0 public domain." />
<meta name="twitter:image" content="/og-image.png" />
<!-- theme color for browsers -->
<meta name="theme-color" content="#111111" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<!-- additional seo -->
<meta name="author" content="cruciverb" />
<meta name="keywords" content="crossword, puzzle, daily crossword, crossword generator, self-hosted, federated, activitypub, multiplayer, party game, cryptic crossword" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+7485
View File
File diff suppressed because it is too large Load Diff
+40
View File
@@ -0,0 +1,40 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build && vite build --config vite.config.embed.ts",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@tabler/icons-react": "^3.40.0",
"canvas-confetti": "^1.9.4",
"i18next": "^25.10.10",
"i18next-browser-languagedetector": "^8.2.1",
"motion": "^12.38.0",
"qrcode.react": "^4.2.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-i18next": "^16.6.6",
"tesseract.js": "^7.0.0",
"vite-plugin-pwa": "^1.2.0",
"zustand": "^5.0.12"
},
"devDependencies": {
"@eslint/js": "^9.39.4",
"@types/node": "^24.12.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"eslint": "^9.39.4",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.4.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.57.0",
"vite": "^8.0.1"
}
}
+23
View File
@@ -0,0 +1,23 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
build: {
outDir: 'dist/embed',
emptyOutDir: true,
rollupOptions: {
input: 'src/embed.tsx',
output: {
entryFileNames: 'embed.js',
assetFileNames: 'embed.[ext]',
},
},
},
server: {
port: 5174,
proxy: {
'/api': 'http://localhost:8090',
},
},
})
+70
View File
@@ -0,0 +1,70 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { VitePWA } from 'vite-plugin-pwa'
export default defineConfig({
plugins: [
react(),
VitePWA({
registerType: 'autoUpdate',
includeAssets: ['favicon.svg', 'models/**/*'],
manifest: {
name: 'cruciverb',
short_name: 'cruciverb',
description: 'a crossword a day',
theme_color: '#111111',
background_color: '#111111',
display: 'standalone',
start_url: '/',
icons: [
{ src: '/favicon.svg', sizes: 'any', type: 'image/svg+xml', purpose: 'any maskable' },
],
},
workbox: {
globPatterns: ['**/*.{js,css,html,svg,woff2,ttf}'],
runtimeCaching: [
{
urlPattern: /^\/api\/puzzles\/daily\/.*/,
handler: 'NetworkFirst',
options: {
cacheName: 'daily-puzzles',
expiration: { maxEntries: 20, maxAgeSeconds: 86400 },
networkTimeoutSeconds: 5,
},
},
{
urlPattern: /^\/api\/me$/,
handler: 'NetworkFirst',
options: {
cacheName: 'session',
expiration: { maxEntries: 1, maxAgeSeconds: 3600 },
networkTimeoutSeconds: 3,
},
},
{
urlPattern: /^\/api\/.*/,
handler: 'NetworkOnly',
},
{
urlPattern: /^\/feeds\/.*/,
handler: 'NetworkFirst',
options: {
cacheName: 'feeds',
expiration: { maxEntries: 5, maxAgeSeconds: 3600 },
},
},
],
},
}),
],
server: {
port: 5173,
proxy: {
'/api': { target: 'http://localhost:8090', ws: true },
'/feeds': { target: 'http://localhost:8090' },
'/.well-known': { target: 'http://localhost:8090' },
'/nodeinfo': { target: 'http://localhost:8090' },
'/inbox': { target: 'http://localhost:8090' },
},
},
})