-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathnuxt.config.ts
More file actions
216 lines (213 loc) · 10.9 KB
/
Copy pathnuxt.config.ts
File metadata and controls
216 lines (213 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
const adsEnabled = process.env.NUXT_PUBLIC_ADS_ENABLED !== 'false';
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
devtools: {enabled: true},
modules: ['@pinia/nuxt', '@nuxtjs/i18n'],
i18n: {
// Subdirectories, not a cookie: a language only earns traffic if Google
// has a URL of its own to index for it. English keeps the bare paths so
// nothing it already ranks for moves.
strategy: 'prefix_except_default',
defaultLocale: 'en',
// Message fallback is a vue-i18n option, not a module one -- the
// module's own fallbackLocale belongs to browser detection.
vueI18n: './i18n.config.ts',
// The six markets where pixel art, retro games and indie dev are
// strongest. `language` is what goes in hreflang.
locales: [
{code: 'en', language: 'en', name: 'English', file: 'en.json'},
{code: 'ja', language: 'ja', name: '日本語', file: 'ja.json'},
{code: 'zh', language: 'zh-Hans', name: '简体中文', file: 'zh.json'},
{code: 'ko', language: 'ko', name: '한국어', file: 'ko.json'},
{code: 'es', language: 'es', name: 'Español', file: 'es.json'},
{code: 'pt', language: 'pt-BR', name: 'Português', file: 'pt.json'},
{code: 'ru', language: 'ru', name: 'Русский', file: 'ru.json'},
],
lazy: true,
bundle: {optimizeTranslationDirective: false},
// README/FAQ copy is prose with inline markup, rendered through v-html.
// The compiler rejects HTML in messages by default, which 404s the locale chunk.
compilation: {strictMessage: false, escapeHtml: false},
// No automatic redirect: a visitor who lands on an English URL from
// search should stay on it, or the ranking page bounces them away.
detectBrowserLanguage: false,
baseUrl: 'https://simplepixelart.com',
},
nitro: {
// Pre-compress hashed assets at build so nginx's gzip_static can serve
// them without compressing on every request. Brotli is off: nginx 1.18
// on the box has no brotli module (and Debian ships none for it), so
// the 96 .br files it produced were 664 KB deployed and never read.
// Cloudflare is what would serve brotli, and it cannot -- gzip_static
// hands it a gzip body, which it passes through rather than
// recompressing. Turning gzip_static off would get brotli from the
// edge; that trade is a deploy-side decision, not a build one.
compressPublicAssets: {gzip: true, brotli: false},
routeRules: {
// Old color-tool slugs → keyword-rich slugs (permanent 301).
'/palettes/extract': {redirect: {to: '/palettes/color-palette-from-image', statusCode: 301}},
'/palettes/scheme': {redirect: {to: '/palettes/color-palette-from-color', statusCode: 301}},
// Tools renamed after the thing they are, not the verb (301 so the
// old URLs keep whatever they have earned).
'/generate': {redirect: {to: '/generator', statusCode: 301}},
'/convert': {redirect: {to: '/converter', statusCode: 301}},
},
},
css: [
'./app/assets/css/main.css',
],
typescript: {
tsConfig: {
compilerOptions: {
noUnusedLocals: false,
noUnusedParameters: false
}
}
},
vite: {
build: {
sourcemap: false
}
},
$development: {
nitro: {
routeRules: {
'/**': {
headers: {
'cache-control': 'no-store, no-cache, must-revalidate, max-age=0',
'pragma': 'no-cache',
'expires': '0',
},
},
},
},
vite: {
server: {
headers: {
'cache-control': 'no-store, no-cache, must-revalidate, max-age=0',
},
},
},
},
runtimeConfig: {
api: 'https://touch.ninosaur.com',
public: {
api: process.env.NUXT_PUBLIC_API || 'https://touch.ninosaur.com',
siteUrl: process.env.NUXT_PUBLIC_SITE_URL || 'https://simplepixelart.com',
adsEnabled,
firebase: {
apiKey: "AIzaSyCF1i7uBqd1OEeJEYfLAVgrs9XlKhLk8tQ",
authDomain: "simplepixelart-78b18.firebaseapp.com",
projectId: "simplepixelart-78b18",
storageBucket: "simplepixelart-78b18.firebasestorage.app",
messagingSenderId: "17213734973",
appId: "1:17213734973:web:f589f41ae083d475776a54",
measurementId: "G-2W3W9J4ER3",
},
}
},
app: {
head: {
titleTemplate: '%s - SimplePixelArt.com',
title: "Simple Pixel Art - Create & Discover Pixel Art Online",
link: [
{rel: 'icon', type: 'image/x-icon', href: '/favicon.ico'},
{rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png'},
{rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png'},
{rel: 'icon', type: 'image/png', sizes: '96x96', href: '/favicon-96x96.png'},
{rel: 'icon', type: 'image/png', sizes: '192x192', href: '/android-chrome-192x192.png'},
{rel: 'icon', type: 'image/png', sizes: '512x512', href: '/android-chrome-512x512.png'},
{rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png'},
{rel: 'manifest', href: '/site.webmanifest'},
{rel: 'alternate', type: 'application/rss+xml',
title: 'Simple Pixel Art — new pieces', href: '/rss.xml'},
{rel: 'preconnect', href: 'https://touch.ninosaur.com'},
],
meta: [
{
name: 'viewport',
content: 'width=device-width, initial-scale=1.0'
},
{
name: 'description',
content: "Create and discover amazing pixel art online. Free pixel art editor with advanced tools, daily updates, and a vibrant community of creators. Start your pixel art journey today!"
},
{
name: 'keywords',
content: 'pixel art, pixel editor, pixel art maker, create pixel art, pixel art online, retro art, 8-bit art, pixel drawing, pixel art tools, free pixel art'
},
{
name: 'author',
content: 'SimplePixelArt.com'
},
...(adsEnabled ? [{
name: 'google-adsense-account',
content: 'ca-pub-7842478840527195'
}] : []),
{
name: 'robots',
content: 'index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1'
},
{name: 'apple-touch-fullscreen', content: 'yes'},
{name: 'apple-mobile-web-app-status-bar-style', content: 'default'},
{name: 'theme-color', content: '#6366f1'},
{name: 'msapplication-TileColor', content: '#6366f1'},
// Open Graph tags
{property: 'og:type', content: 'website'},
{property: 'og:site_name', content: 'SimplePixelArt.com'},
{property: 'og:title', content: 'Simple Pixel Art - Create & Discover Pixel Art Online'},
{property: 'og:description', content: 'Create and discover amazing pixel art online. Free pixel art editor with advanced tools and vibrant community.'},
{property: 'og:url', content: 'https://simplepixelart.com'},
{property: 'og:image', content: 'https://simplepixelart.com/og-image.png'},
{property: 'og:image:width', content: '1200'},
{property: 'og:image:height', content: '630'},
{property: 'og:image:alt', content: 'Simple Pixel Art - Create and Discover Pixel Art'},
// Twitter Card tags
{name: 'twitter:card', content: 'summary_large_image'},
{name: 'twitter:site', content: '@comficker'},
{name: 'twitter:creator', content: '@comficker'},
{name: 'twitter:title', content: 'Simple Pixel Art - Create & Discover Pixel Art Online'},
{name: 'twitter:description', content: 'Create and discover amazing pixel art online. Free pixel art editor with advanced tools.'},
{name: 'twitter:image', content: 'https://simplepixelart.com/og-image.png'},
{name: 'twitter:image:alt', content: 'Simple Pixel Art - Create and Discover Pixel Art'},
],
script: [
// Restore the editor's fullscreen layout BEFORE first paint (no
// flash on hard-reload). Scoped to /editor so other pages are
// untouched; the component owns the class from mount onward.
{
innerHTML: `try{if(location.pathname==='/editor'){var v=localStorage.getItem('editor_fullscreen');if(v&&v!=='off')document.documentElement.classList.add('editor-fullscreen')}}catch(e){}`,
type: 'text/javascript',
tagPosition: 'head',
},
...(adsEnabled ? [
// Consent Mode v2 default — denied until user accepts.
{
innerHTML: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
'ad_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied',
'analytics_storage': 'denied',
'functionality_storage': 'granted',
'security_storage': 'granted',
'wait_for_update': 500
});
`,
type: 'text/javascript',
tagPosition: 'head',
},
// NOTE: adsbygoogle.js is no longer loaded here. It is lazy-injected
// by the AdSlot component on mount, so the AdSense script (and any
// Auto Ads it would place) only runs on content-rich pages where an
// AdSlot actually renders — currently `/` and `/art/[id_string]`
// with the hasOriginalContent guard. This satisfies AdSense's
// "no ads on screens without publisher content" policy.
] : []),
],
}
},
})