xypriss 9.10.13 → 9.10.14

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.
Files changed (39) hide show
  1. package/CONTRIBUTING.md +35 -19
  2. package/LICENSE +56 -8
  3. package/README.md +2 -2
  4. package/dist/cjs/src/ConfigurationManager.js +13 -0
  5. package/dist/cjs/src/ConfigurationManager.js.map +1 -1
  6. package/dist/cjs/src/server/ServerFactory.js +1 -4
  7. package/dist/cjs/src/server/ServerFactory.js.map +1 -1
  8. package/dist/cjs/src/server/const/default.js +1 -2
  9. package/dist/cjs/src/server/const/default.js.map +1 -1
  10. package/dist/cjs/src/server/core/HttpServer.js +12 -2
  11. package/dist/cjs/src/server/core/HttpServer.js.map +1 -1
  12. package/dist/cjs/src/server/core/SendFileHandler.js.map +1 -1
  13. package/dist/cjs/src/server/core/http/HttpErrorHandler.js +6 -1
  14. package/dist/cjs/src/server/core/http/HttpErrorHandler.js.map +1 -1
  15. package/dist/cjs/src/server/handlers/NotFoundHandler.js +10 -12
  16. package/dist/cjs/src/server/handlers/NotFoundHandler.js.map +1 -1
  17. package/dist/cjs/src/server/handlers/templates/notFoundTemplate.js +390 -645
  18. package/dist/cjs/src/server/handlers/templates/notFoundTemplate.js.map +1 -1
  19. package/dist/cjs/src/types/NotFoundConfig.js +2 -1
  20. package/dist/cjs/src/types/NotFoundConfig.js.map +1 -1
  21. package/dist/esm/src/ConfigurationManager.js +13 -0
  22. package/dist/esm/src/ConfigurationManager.js.map +1 -1
  23. package/dist/esm/src/server/ServerFactory.js +1 -4
  24. package/dist/esm/src/server/ServerFactory.js.map +1 -1
  25. package/dist/esm/src/server/const/default.js +1 -2
  26. package/dist/esm/src/server/const/default.js.map +1 -1
  27. package/dist/esm/src/server/core/HttpServer.js +12 -2
  28. package/dist/esm/src/server/core/HttpServer.js.map +1 -1
  29. package/dist/esm/src/server/core/SendFileHandler.js.map +1 -1
  30. package/dist/esm/src/server/core/http/HttpErrorHandler.js +6 -1
  31. package/dist/esm/src/server/core/http/HttpErrorHandler.js.map +1 -1
  32. package/dist/esm/src/server/handlers/NotFoundHandler.js +10 -12
  33. package/dist/esm/src/server/handlers/NotFoundHandler.js.map +1 -1
  34. package/dist/esm/src/server/handlers/templates/notFoundTemplate.js +390 -645
  35. package/dist/esm/src/server/handlers/templates/notFoundTemplate.js.map +1 -1
  36. package/dist/esm/src/types/NotFoundConfig.js +2 -1
  37. package/dist/esm/src/types/NotFoundConfig.js.map +1 -1
  38. package/dist/index.d.ts +6 -1
  39. package/package.json +2 -1
@@ -1,653 +1,398 @@
1
1
  'use strict';
2
2
 
3
3
  function notFoundTemplate(d) {
4
- const html = `<!DOCTYPE html>
5
- <html lang="en" class="${d.themeClass}">
6
- <head>
7
- <meta charset="UTF-8" />
8
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
9
- <title>${d.title}</title>
10
- <link rel="icon" href="${d.faviconUrl}" type="image/x-icon" />
11
- <style>
12
- * {
13
- margin: 0;
14
- padding: 0;
15
- box-sizing: border-box;
16
- }
17
-
18
- /* Détection automatique du thème via CSS */
19
- @media (prefers-color-scheme: dark) {
20
- html.auto {
21
- color-scheme: dark;
22
- }
23
- }
24
-
25
- @media (prefers-color-scheme: light) {
26
- html.auto {
27
- color-scheme: light;
28
- }
29
- }
30
-
31
- body {
32
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
33
- min-height: 100vh;
34
- display: flex;
35
- flex-direction: column;
36
- align-items: center;
37
- justify-content: center;
38
- padding: 1rem;
39
- background: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 50%, #a5b4fc 100%);
40
- transition: background 0.3s ease;
41
- }
42
-
43
- /* Mode sombre */
44
- html.dark body,
45
- html.auto body {
46
- background: linear-gradient(135deg, #1e293b 0%, #0f172a 50%, #020617 100%);
47
- }
48
-
49
- /* Mode clair spécifique */
50
- html.light body {
51
- background: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 50%, #a5b4fc 100%);
52
- }
53
-
54
- /* Override auto en mode clair */
55
- @media (prefers-color-scheme: light) {
56
- html.auto body {
57
- background: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 50%, #a5b4fc 100%);
58
- }
59
- }
60
-
61
- .container {
62
- width: 100%;
63
- max-width: 28rem;
64
- animation: fadeInUp 0.8s ease-out forwards;
65
- }
66
-
67
- .card {
68
- background: rgba(255, 255, 255, 0.95);
69
- border-radius: 1rem;
70
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
71
- padding: 3rem 2rem;
72
- text-align: center;
73
- width: 100%;
74
- transform: scale(1);
75
- transition: all 0.3s ease;
76
- border: 1px solid rgba(203, 213, 225, 0.3);
77
- }
78
-
79
- /* Mode sombre - Card avec effet glow */
80
- html.dark .card,
81
- html.auto .card {
82
- background: rgba(15, 23, 42, 0.95);
83
- box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5),
84
- 0 0 40px rgba(59, 130, 246, 0.15);
85
- border: 1px solid rgba(59, 130, 246, 0.2);
86
- }
87
-
88
- /* Mode clair spécifique pour la card */
89
- html.light .card {
90
- background: rgba(255, 255, 255, 0.95);
91
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
92
- border: 1px solid rgba(203, 213, 225, 0.3);
93
- }
94
-
95
- @media (prefers-color-scheme: light) {
96
- html.auto .card {
97
- background: rgba(255, 255, 255, 0.95);
98
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
99
- border: 1px solid rgba(203, 213, 225, 0.3);
100
- }
101
- }
102
-
103
- .card:hover {
104
- transform: scale(1.02);
105
- box-shadow: 0 15px 40px rgba(0, 0, 0, 0.12);
106
- }
107
-
108
- html.dark .card:hover,
109
- html.auto .card:hover {
110
- transform: scale(1.02);
111
- box-shadow: 0 25px 60px -12px rgba(59, 130, 246, 0.3),
112
- 0 0 60px rgba(59, 130, 246, 0.25);
113
- border: 1px solid rgba(59, 130, 246, 0.4);
114
- }
115
-
116
- @media (prefers-color-scheme: light) {
117
- html.auto .card:hover {
118
- transform: scale(1.02);
119
- box-shadow: 0 15px 40px rgba(0, 0, 0, 0.12);
120
- }
121
- }
122
-
123
- .branding-container {
124
- display: flex;
125
- justify-content: center;
126
- align-items: center;
127
- margin-bottom: 3rem;
128
- }
129
-
130
- .branding {
131
- display: flex;
132
- align-items: center;
133
- gap: 0.75rem;
134
- }
135
-
136
- .logo {
137
- width: 2.5rem;
138
- height: 2.5rem;
139
- border-radius: 0.5rem;
140
- background: linear-gradient(135deg, #22d3ee 0%, #3b82f6 50%, #9333ea 100%);
141
- display: flex;
142
- align-items: center;
143
- justify-content: center;
144
- box-shadow: 0 10px 15px -3px rgba(59, 130, 246, 0.3);
145
- animation: pulse-glow 2s ease-in-out infinite;
146
- }
147
-
148
- html.dark .logo,
149
- html.auto .logo {
150
- box-shadow: 0 10px 20px rgba(59, 130, 246, 0.4),
151
- 0 0 30px rgba(59, 130, 246, 0.2);
152
- }
153
-
154
- @media (prefers-color-scheme: light) {
155
- html.auto .logo {
156
- box-shadow: 0 10px 15px -3px rgba(59, 130, 246, 0.3);
157
- }
158
- }
159
-
160
- .logo-text {
161
- color: white;
162
- font-weight: bold;
163
- font-size: 1.25rem;
164
- }
165
-
166
- .brand-name {
167
- font-size: 1.875rem;
168
- font-weight: bold;
169
- overflow: hidden;
170
- border-right: 3px solid #22d3ee;
171
- white-space: nowrap;
172
- animation: typing 2s steps(7, end) forwards, blink 0.75s step-end infinite;
173
- animation-delay: 0.5s;
174
- width: 0;
175
- }
176
-
177
- .brand-gradient {
178
- background: linear-gradient(90deg, #60a5fa 0%, #a78bfa 33%, #f9a8d4 66%, #fbbf24 100%);
179
- background-size: 200% 200%;
180
- background-clip: text;
181
- -webkit-background-clip: text;
182
- -webkit-text-fill-color: transparent;
183
- animation: rainbow 3s ease infinite;
184
- }
185
-
186
- .error-number {
187
- animation: float 3s ease-in-out infinite;
188
- margin-bottom: 1.5rem;
189
- }
190
-
191
- .error-number h1 {
192
- font-size: 6rem;
193
- font-weight: bold;
194
- background: linear-gradient(90deg, #60a5fa 0%, #a78bfa 50%, #f9a8d4 100%);
195
- background-clip: text;
196
- -webkit-background-clip: text;
197
- -webkit-text-fill-color: transparent;
198
- filter: drop-shadow(0 10px 8px rgb(0 0 0 / 0.04));
199
- }
200
-
201
- html.dark .error-number h1,
202
- html.auto .error-number h1 {
203
- filter: drop-shadow(0 0 20px rgba(96, 165, 250, 0.4));
204
- }
205
-
206
- @media (prefers-color-scheme: light) {
207
- html.auto .error-number h1 {
208
- filter: drop-shadow(0 10px 8px rgb(0 0 0 / 0.04));
209
- }
210
- }
211
-
212
- .title {
213
- font-size: 1.875rem;
214
- font-weight: bold;
215
- color: #1e293b;
216
- margin-bottom: 1rem;
217
- animation: fadeInUp 0.8s ease-out forwards;
218
- animation-delay: 0.1s;
219
- opacity: 0;
220
- }
221
-
222
- html.dark .title,
223
- html.auto .title {
224
- color: #f1f5f9;
225
- }
226
-
227
- @media (prefers-color-scheme: light) {
228
- html.auto .title {
229
- color: #1e293b;
230
- }
231
- }
232
-
233
- .description {
234
- color: #475569;
235
- margin-bottom: 0.5rem;
236
- animation: fadeInUp 0.8s ease-out forwards;
237
- animation-delay: 0.2s;
238
- opacity: 0;
239
- }
240
-
241
- html.dark .description,
242
- html.auto .description {
243
- color: #94a3b8;
244
- }
245
-
246
- @media (prefers-color-scheme: light) {
247
- html.auto .description {
248
- color: #475569;
249
- }
250
- }
251
-
252
- .method {
253
- color: #3b82f6;
254
- font-weight: 600;
255
- }
256
-
257
- html.dark .method,
258
- html.auto .method {
259
- color: #60a5fa;
260
- }
261
-
262
- @media (prefers-color-scheme: light) {
263
- html.auto .method {
264
- color: #3b82f6;
265
- }
266
- }
267
-
268
- .path {
269
- color: #ec4899;
270
- font-weight: 600;
271
- word-break: break-all;
272
- }
273
-
274
- html.dark .path,
275
- html.auto .path {
276
- color: #f9a8d4;
277
- }
278
-
279
- @media (prefers-color-scheme: light) {
280
- html.auto .path {
281
- color: #ec4899;
282
- }
283
- }
284
-
285
- .subdescription {
286
- color: #64748b;
287
- font-size: 0.875rem;
288
- margin-bottom: 2rem;
289
- animation: fadeInUp 0.8s ease-out forwards;
290
- animation-delay: 0.3s;
291
- opacity: 0;
292
- }
293
-
294
- @media (prefers-color-scheme: light) {
295
- html.auto .subdescription {
296
- color: #64748b;
297
- }
298
- }
299
-
300
- .app-name {
301
- color: #475569;
302
- font-weight: 500;
303
- }
304
-
305
- html.dark .app-name,
306
- html.auto .app-name {
307
- color: #94a3b8;
308
- }
309
-
310
- @media (prefers-color-scheme: light) {
311
- html.auto .app-name {
312
- color: #475569;
313
- }
314
- }
315
-
316
- .button {
317
- display: inline-block;
318
- background: linear-gradient(90deg, #3b82f6 0%, #9333ea 100%);
319
- color: white;
320
- font-weight: 600;
321
- padding: 0.75rem 2rem;
322
- border-radius: 0.5rem;
323
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
324
- transform: scale(1);
325
- transition: all 0.3s ease;
326
- text-decoration: none;
327
- cursor: pointer;
328
- border: none;
329
- animation: fadeInUp 0.8s ease-out forwards;
330
- animation-delay: 0.3s;
331
- opacity: 0;
332
- }
333
-
334
- .button:hover {
335
- background: linear-gradient(90deg, #2563eb 0%, #7c3aed 100%);
336
- box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
337
- transform: scale(1.05);
338
- }
339
-
340
- html.dark .button:hover,
341
- html.auto .button:hover {
342
- box-shadow: 0 20px 25px -5px rgba(59, 130, 246, 0.3),
343
- 0 0 20px rgba(59, 130, 246, 0.2);
344
- }
345
-
346
- @media (prefers-color-scheme: light) {
347
- html.auto .button:hover {
348
- box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
349
- }
350
- }
351
-
352
- .button:active {
353
- transform: scale(0.95);
354
- }
355
-
356
- .contact {
357
- margin-top: 1.5rem;
358
- animation: fadeInUp 0.8s ease-out forwards;
359
- animation-delay: 0.3s;
360
- opacity: 0;
361
- }
362
-
363
- .contact-text {
364
- color: #64748b;
365
- font-size: 0.875rem;
366
- }
367
-
368
- .contact-link {
369
- color: #3b82f6;
370
- transition: color 0.2s ease;
371
- text-decoration: none;
372
- }
373
-
374
- html.dark .contact-link,
375
- html.auto .contact-link {
376
- color: #60a5fa;
377
- }
378
-
379
- @media (prefers-color-scheme: light) {
380
- html.auto .contact-link {
381
- color: #3b82f6;
382
- }
383
- }
384
-
385
- .contact-link:hover {
386
- color: #2563eb;
387
- }
388
-
389
- html.dark .contact-link:hover,
390
- html.auto .contact-link:hover {
391
- color: #93c5fd;
392
- }
393
-
394
- @media (prefers-color-scheme: light) {
395
- html.auto .contact-link:hover {
396
- color: #2563eb;
397
- }
398
- }
399
-
400
- .footer {
401
- margin-top: 1.5rem;
402
- }
403
-
404
- .footer-text {
405
- color: #64748b;
406
- font-size: 0.875rem;
407
- }
408
-
409
- html.dark .footer-text,
410
- html.auto .footer-text {
411
- color: #94a3b8;
412
- }
413
-
414
- @media (prefers-color-scheme: light) {
415
- html.auto .footer-text {
416
- color: #64748b;
417
- }
418
- }
419
-
420
- .footer-link {
421
- color: #3b82f6;
422
- font-weight: 600;
423
- word-break: break-all;
424
- transition: color 0.2s ease;
425
- text-decoration: none;
426
- }
427
-
428
- html.dark .footer-link,
429
- html.auto .footer-link {
430
- color: #60a5fa;
431
- }
432
-
433
- @media (prefers-color-scheme: light) {
434
- html.auto .footer-link {
435
- color: #3b82f6;
436
- }
437
- }
438
-
439
- .footer-link:hover {
440
- color: #8b5cf6;
441
- }
442
-
443
- html.dark .footer-link:hover,
444
- html.auto .footer-link:hover {
445
- color: #a78bfa;
446
- }
447
-
448
- @media (prefers-color-scheme: light) {
449
- html.auto .footer-link:hover {
450
- color: #8b5cf6;
451
- }
452
- }
453
-
454
- .decorative {
455
- margin-top: 1.5rem;
456
- display: flex;
457
- justify-content: center;
458
- gap: 0.5rem;
459
- }
460
-
461
- .dot {
462
- width: 0.5rem;
463
- height: 0.5rem;
464
- border-radius: 50%;
465
- animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
466
- }
467
-
468
- .dot-red {
469
- background: #ef4444;
470
- }
471
-
472
- .dot-blue {
473
- background: #3b82f6;
474
- animation-delay: 0.1s;
475
- }
476
-
477
- .dot-green {
478
- background: #10b981;
479
- animation-delay: 0.2s;
480
- }
481
-
482
- @keyframes float {
483
- 0%, 100% {
484
- transform: translateY(0px);
485
- }
486
- 50% {
487
- transform: translateY(-20px);
488
- }
489
- }
490
-
491
- @keyframes fadeInUp {
492
- from {
493
- opacity: 0;
494
- transform: translateY(30px);
495
- }
496
- to {
497
- opacity: 1;
498
- transform: translateY(0);
499
- }
500
- }
501
-
502
- @keyframes pulse-glow {
503
- 0%, 100% {
504
- box-shadow: 0 0 20px rgba(96, 165, 250, 0.3);
505
- }
506
- 50% {
507
- box-shadow: 0 0 40px rgba(96, 165, 250, 0.6);
508
- }
509
- }
510
-
511
- @keyframes typing {
512
- from {
513
- width: 0;
514
- }
515
- to {
516
- width: 100%;
517
- }
518
- }
519
-
520
- @keyframes blink {
521
- 50% {
522
- border-color: transparent;
523
- }
524
- }
525
-
526
- @keyframes rainbow {
527
- 0% {
528
- background-position: 0% 50%;
529
- }
530
- 50% {
531
- background-position: 100% 50%;
532
- }
533
- 100% {
534
- background-position: 0% 50%;
535
- }
536
- }
537
-
538
- @keyframes pulse {
539
- 0%, 100% {
540
- opacity: 1;
541
- }
542
- 50% {
543
- opacity: 0.5;
544
- }
545
- }
546
-
547
- @media (min-width: 768px) {
548
- .card {
549
- padding: 3rem;
550
- }
551
-
552
- .error-number h1 {
553
- font-size: 8rem;
554
- }
555
-
556
- .title {
557
- font-size: 1.875rem;
558
- }
559
-
560
- .brand-name {
561
- font-size: 1.875rem;
562
- }
563
- }
564
-
565
- ${d.customCSS}
566
- </style>
567
- </head>
568
- <body>
569
- <div class="container">
570
- <div class="card">
571
- <!-- Branding XyPriss -->
572
- <div class="branding-container">
573
- <div class="branding">
574
- <div class="logo">
575
- <span class="logo-text">XP</span>
576
- </div>
577
- <h3 class="brand-name">
578
- <span class="brand-gradient">XyPriss</span>
579
- </h3>
580
- </div>
581
- </div>
582
-
583
- <!-- 404 Number with floating animation -->
584
- <div class="error-number">
585
- <h1>404</h1>
586
- </div>
587
-
588
- <!-- Title -->
589
- <h2 class="title">Page Not Found</h2>
590
-
591
- <!-- Description -->
592
- <p class="description">
593
- ${d.message
594
- ? d.message
595
- : `Cannot
596
- <span class="method">${d.requestedMethod}</span>
597
- <span class="path">${d.requestedPath}</span>`}
598
- </p>
599
-
600
- ${d.appName
601
- ? `<p class="subdescription">
602
- We've searched everywhere, but the page you're looking for
603
- seems to have vanished or been moved from
604
- <span class="method">${d.appName}</span>.
605
- </p>`
606
- : ""}
607
-
608
- <!-- Button -->
609
- ${d.redirectText && d.redirectTo
610
- ? `<a
611
- class="button"
612
- href="${d.redirectTo}"
613
- >
614
- ${d.redirectText}
615
- </a>`
616
- : ""}
617
-
618
- <!-- Contact section -->
619
- ${d.contactEmail
620
- ? ` <div class="contact">
621
- <span class="contact-text">
622
- Need help? Contact us at
623
- <a href="mailto:${d.contactEmail}" class="contact-link"
624
- >${d.contactEmail}</a
625
- >
626
- </span>
627
- </div>`
628
- : ""}
629
-
630
- <!-- By XyPriss -->
631
- <div class="footer">
632
- <small class="footer-text">
633
- By
634
- <a href="https://XyPriss.nehonix.com" class="footer-link"
635
- >XyPriss</a
636
- >
637
- </small>
638
- </div>
639
-
640
- <!-- Decorative elements -->
641
- <div class="decorative">
642
- <div class="dot dot-red"></div>
643
- <div class="dot dot-blue"></div>
644
- <div class="dot dot-green"></div>
645
- </div>
4
+ const contactBlock = d.contactEmail
5
+ ? `
6
+ <div class="help-card a5">
7
+ <div style="display:flex;align-items:center;gap:15px;">
8
+ <div class="help-icon">
9
+ <svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
10
+ <circle cx="12" cy="12" r="10"/>
11
+ <circle cx="12" cy="12" r="4"/>
12
+ <line x1="4.93" y1="4.93" x2="7.76" y2="7.76"/>
13
+ <line x1="16.24" y1="16.24" x2="19.07" y2="19.07"/>
14
+ <line x1="16.24" y1="7.76" x2="19.07" y2="4.93"/>
15
+ <line x1="4.93" y1="19.07" x2="7.76" y2="16.24"/>
16
+ </svg>
17
+ </div>
18
+ <div>
19
+ <div style="font-size:15px;font-weight:700;margin-bottom:4px;">Need help?</div>
20
+ <p class="help-text">
21
+ If you believe this is a mistake or need assistance,<br>
22
+ feel free to <a href="mailto:${d.contactEmail}" style="color:var(--blue);text-decoration:none;">contact us</a>.
23
+ </p>
646
24
  </div>
25
+ </div>
26
+ <a href="mailto:${d.contactEmail}" class="btn-contact">
27
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
28
+ <path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/>
29
+ <polyline points="22,6 12,13 2,6"/>
30
+ </svg>
31
+ Contact us
32
+ </a>
33
+ </div>
34
+ `
35
+ : "";
36
+ const messageText = d.message ||
37
+ `The resource you're looking for might have been removed,<br>renamed, or is temporarily unavailable.`;
38
+ const redirectUrl = d.redirectTo || "/";
39
+ const redirectLabel = d.redirectText || "Go to Homepage";
40
+ const scriptBlock = d.redirectScript
41
+ ? `<script>${d.redirectScript}</script>`
42
+ : "";
43
+ const customCssBlock = d.customCSS ? `<style>${d.customCSS}</style>` : "";
44
+ const faviconBlock = d.faviconUrl
45
+ ? `<link rel="icon" href="${d.faviconUrl}" type="image/x-icon" />`
46
+ : "";
47
+ const html = `<!DOCTYPE html>
48
+ <html lang="en" data-theme="${d.mode || "system"}">
49
+ <head>
50
+ <meta charset="UTF-8" />
51
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
52
+ <title>${d.title || "404 – Page Not Found | " + d.appName}</title>
53
+ ${faviconBlock}
54
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.js"></script>
55
+ <link href="https://fonts.googleapis.com/css2?family=Rajdhani:wght@500;600;700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
56
+ <style>
57
+ /* ── Theming Variables ── */
58
+ :root {
59
+ --blue: #1a9fff;
60
+ }
61
+
62
+ /* Dark theme (default or explicit) */
63
+ :root[data-theme="dark"], :root[data-theme="system"] {
64
+ --bg: #04091a;
65
+ --text-main: #fff;
66
+ --text-dim1: rgba(255,255,255,0.82);
67
+ --text-dim2: rgba(255,255,255,0.52);
68
+ --text-dim3: rgba(255,255,255,0.48);
69
+ --text-dim4: rgba(255,255,255,0.46);
70
+ --text-dim5: rgba(255,255,255,0.4);
71
+ --text-dim6: rgba(255,255,255,0.38);
72
+ --text-dim7: rgba(255,255,255,0.32);
73
+ --text-dim8: rgba(255,255,255,0.3);
74
+ --border-main: rgba(30, 120, 220, 0.18);
75
+ --border-btn: rgba(255,255,255,0.18);
76
+ --border-nav: rgba(255,255,255,0.16);
77
+ --bg-panther: url('https://dll.nehonix.com/assets/XyPriss/xp-template-bgimg.png');
78
+ --grad-before-1: #04091a;
79
+ --grad-before-2: rgba(4,9,26,.85);
80
+ --grad-before-3: rgba(4,9,26,.35);
81
+ --grad-after-2: rgba(4,9,26,.25);
82
+ --help-card-bg: rgba(255,255,255,0.03);
83
+ --help-card-border: var(--border-main);
84
+ --btn-contact-text: #fff;
85
+ }
86
+
87
+ /* Light theme overrides */
88
+ :root[data-theme="light"] {
89
+ --bg: #f5f8fa;
90
+ --text-main: #04091a;
91
+ --text-dim1: rgba(4,9,26,0.82);
92
+ --text-dim2: rgba(4,9,26,0.62);
93
+ --text-dim3: rgba(4,9,26,0.48);
94
+ --text-dim4: rgba(4,9,26,0.66);
95
+ --text-dim5: rgba(4,9,26,0.5);
96
+ --text-dim6: rgba(4,9,26,0.38);
97
+ --text-dim7: rgba(4,9,26,0.42);
98
+ --text-dim8: rgba(4,9,26,0.3);
99
+ --border-main: rgba(30, 120, 220, 0.18);
100
+ --border-btn: rgba(4,9,26,0.18);
101
+ --border-nav: rgba(4,9,26,0.16);
102
+ --bg-panther: url('https://dll.nehonix.com/assets/XyPriss/xp-template-bgimg-light.png');
103
+ --grad-before-1: #f5f8fa;
104
+ --grad-before-2: rgba(245,248,250,.85);
105
+ --grad-before-3: rgba(245,248,250,.35);
106
+ --grad-after-2: rgba(245,248,250,.25);
107
+ --help-card-bg: rgba(0,0,0,0.03);
108
+ --help-card-border: rgba(0,0,0,0.08);
109
+ --btn-contact-text: #04091a;
110
+ }
111
+
112
+ @media (prefers-color-scheme: light) {
113
+ :root[data-theme="system"] {
114
+ --bg: #f5f8fa;
115
+ --text-main: #04091a;
116
+ --text-dim1: rgba(4,9,26,0.82);
117
+ --text-dim2: rgba(4,9,26,0.62);
118
+ --text-dim3: rgba(4,9,26,0.48);
119
+ --text-dim4: rgba(4,9,26,0.66);
120
+ --text-dim5: rgba(4,9,26,0.5);
121
+ --text-dim6: rgba(4,9,26,0.38);
122
+ --text-dim7: rgba(4,9,26,0.42);
123
+ --text-dim8: rgba(4,9,26,0.3);
124
+ --border-btn: rgba(4,9,26,0.18);
125
+ --border-nav: rgba(4,9,26,0.16);
126
+ --bg-panther: url('https://dll.nehonix.com/assets/XyPriss/xp-template-bgimg-light.png');
127
+ --grad-before-1: #f5f8fa;
128
+ --grad-before-2: rgba(245,248,250,.85);
129
+ --grad-before-3: rgba(245,248,250,.35);
130
+ --grad-after-2: rgba(245,248,250,.25);
131
+ --help-card-bg: rgba(0,0,0,0.03);
132
+ --help-card-border: rgba(0,0,0,0.08);
133
+ --btn-contact-text: #04091a;
134
+ }
135
+ }
136
+
137
+ * { box-sizing: border-box; margin: 0; padding: 0; }
138
+ html, body { height: 100%; background: var(--bg); font-family: 'Inter', sans-serif; color: var(--text-main); overflow-x: hidden; }
139
+
140
+ .page { position: relative; min-height: 100vh; display: flex; flex-direction: column; }
141
+
142
+ /* ── Background ── */
143
+ .bg-panther {
144
+ position: absolute; top: 0; right: 0;
145
+ width: 60%; height: 100%;
146
+ background: var(--bg-panther) center center / cover no-repeat;
147
+ pointer-events: none; z-index: 0;
148
+ }
149
+ .bg-panther::before {
150
+ content: ''; position: absolute; inset: 0;
151
+ background: linear-gradient(to right,
152
+ var(--grad-before-1) 0%,
153
+ var(--grad-before-2) 25%,
154
+ var(--grad-before-3) 50%,
155
+ transparent 70%
156
+ );
157
+ }
158
+ .bg-panther::after {
159
+ content: ''; position: absolute; inset: 0;
160
+ background: linear-gradient(to top, var(--grad-before-1) 0%, var(--grad-after-2) 14%, transparent 30%);
161
+ }
162
+
163
+ .content { position: relative; z-index: 10; flex: 1; display: flex; flex-direction: column; }
164
+
165
+ /* ── Logo (text only) ── */
166
+ .logo-name {
167
+ font-family: 'Rajdhani', sans-serif;
168
+ font-size: 25px; font-weight: 700; letter-spacing: .4px; line-height: 1;
169
+ }
170
+ .logo-name .xy { color: var(--blue); }
171
+ .logo-sub {
172
+ font-size: 7.5px; letter-spacing: 1.4px; font-weight: 600; margin-top: 3px;
173
+ color: var(--text-dim6);
174
+ text-transform: uppercase;
175
+ }
176
+
177
+ /* ── Nav back button ── */
178
+ .nav-back {
179
+ display: inline-flex; align-items: center; gap: 8px;
180
+ padding: 9px 20px;
181
+ border: 1px solid var(--border-nav);
182
+ border-radius: 8px; font-size: 14px; font-weight: 500;
183
+ color: var(--text-dim1); text-decoration: none; background: transparent;
184
+ transition: border-color .2s, color .2s;
185
+ }
186
+ .nav-back:hover { border-color: var(--blue); color: var(--blue); }
187
+
188
+ /* ── Badge ── */
189
+ .badge {
190
+ display: inline-flex; align-items: center; gap: 8px;
191
+ border: 1px solid rgba(30,120,220,0.38);
192
+ border-radius: 5px; padding: 5px 13px;
193
+ font-family: 'Rajdhani', sans-serif;
194
+ font-size: 11.5px; letter-spacing: 2.8px; font-weight: 600;
195
+ color: var(--text-dim2);
196
+ background: rgba(0,120,220,0.05);
197
+ }
198
+
199
+ /* ── Headlines ── */
200
+ .h1-white {
201
+ font-family: 'Rajdhani', sans-serif;
202
+ font-size: clamp(40px, 4.6vw, 64px);
203
+ font-weight: 700; line-height: 1.08; color: var(--text-main);
204
+ }
205
+ .h1-blue {
206
+ font-family: 'Rajdhani', sans-serif;
207
+ font-size: clamp(40px, 4.6vw, 64px);
208
+ font-weight: 700; line-height: 1.08; font-style: italic;
209
+ color: var(--blue);
210
+ text-shadow: 0 0 26px rgba(26,159,255,0.42);
211
+ }
212
+
213
+ /* ── PATH line — inline after sub-text ── */
214
+ .path-line {
215
+ display: flex; align-items: center; gap: 8px;
216
+ margin-top: 16px;
217
+ font-size: 14px; color: var(--text-dim3); line-height: 1.7;
218
+ }
219
+ .path-token {
220
+ font-family: 'JetBrains Mono', monospace;
221
+ font-size: 13px; color: rgba(26,159,255,0.88);
222
+ background: rgba(26,159,255,0.07);
223
+ border: 1px solid rgba(26,159,255,0.2);
224
+ border-radius: 5px; padding: 2px 10px;
225
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 340px;
226
+ }
227
+ .path-icon { color: var(--text-dim8); flex-shrink: 0; }
228
+
229
+ .sub-text { font-size: 14.5px; line-height: 1.7; color: var(--text-dim4); margin-top: 6px; }
230
+
231
+ /* ── Buttons ── */
232
+ .btn-primary {
233
+ display: inline-flex; align-items: center; gap: 8px;
234
+ padding: 12px 24px;
235
+ background: linear-gradient(135deg, #1a9fff 0%, #005fcc 100%);
236
+ border: 1px solid rgba(26,159,255,0.32);
237
+ border-radius: 8px; font-size: 14.5px; font-weight: 600;
238
+ color: #fff; text-decoration: none;
239
+ box-shadow: 0 4px 20px rgba(26,159,255,0.26);
240
+ transition: all .22s ease;
241
+ }
242
+ .btn-primary:hover { background: linear-gradient(135deg,#33aaff,#0077ee); box-shadow: 0 6px 28px rgba(26,159,255,0.4); transform: translateY(-1px); }
243
+
244
+ .btn-secondary {
245
+ display: inline-flex; align-items: center; gap: 8px;
246
+ padding: 11px 24px;
247
+ border: 1px solid var(--border-btn);
248
+ border-radius: 8px; font-size: 14.5px; font-weight: 600;
249
+ color: var(--text-main); text-decoration: none; background: transparent;
250
+ transition: all .22s ease;
251
+ }
252
+ .btn-secondary:hover { border-color: var(--blue); color: var(--blue); background: rgba(26,159,255,0.05); }
253
+
254
+ /* ── Help card ── */
255
+ .help-card {
256
+ background: var(--help-card-bg);
257
+ border: 1px solid var(--help-card-border);
258
+ border-radius: 11px; padding: 22px 26px;
259
+ display: flex; align-items: center; justify-content: space-between; gap: 18px;
260
+ margin-top: 42px;
261
+ }
262
+ .help-icon {
263
+ width: 46px; height: 46px; border-radius: 50%;
264
+ border: 1.8px solid var(--blue);
265
+ display: flex; align-items: center; justify-content: center;
266
+ flex-shrink: 0; color: var(--blue);
267
+ box-shadow: 0 0 13px rgba(26,159,255,0.26), inset 0 0 8px rgba(26,159,255,0.07);
268
+ }
269
+ .btn-contact {
270
+ display: inline-flex; align-items: center; gap: 8px;
271
+ padding: 10px 20px;
272
+ border: 1px solid rgba(26,159,255,0.3);
273
+ border-radius: 8px; font-size: 14px; font-weight: 600;
274
+ color: var(--btn-contact-text); text-decoration: none;
275
+ background: rgba(26,159,255,0.06);
276
+ transition: all .2s ease; white-space: nowrap;
277
+ }
278
+ .btn-contact:hover { background: rgba(26,159,255,0.14); border-color: var(--blue); }
279
+ .help-text { font-size:13.5px; color:var(--text-dim4); line-height:1.65; }
280
+
281
+ /* ── Footer ── */
282
+ .footer { border-top: 1px solid rgba(26,159,255,0.09); }
283
+ .footer-link { color: var(--blue); text-decoration: none; font-size: 13.5px; font-weight: 500; }
284
+ .footer-link:hover { text-decoration: underline; }
285
+ .footer-dim { color: var(--text-dim7); font-size: 13.5px; }
286
+
287
+ .server-chip {
288
+ font-family: 'JetBrains Mono', monospace;
289
+ font-size: 11.5px; color: var(--text-dim5);
290
+ background: rgba(26,159,255,0.06);
291
+ border: 1px solid rgba(26,159,255,0.14);
292
+ border-radius: 4px; padding: 2px 8px;
293
+ }
294
+
295
+ /* Animations */
296
+ @keyframes fadeUp { from { opacity:0; transform:translateY(16px); } to { opacity:1; transform:translateY(0); } }
297
+ @keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
298
+ .a1 { animation: fadeUp .52s ease both .04s; }
299
+ .a2 { animation: fadeUp .52s ease both .13s; }
300
+ .a3 { animation: fadeUp .52s ease both .22s; }
301
+ .a4 { animation: fadeUp .52s ease both .30s; }
302
+ .a5 { animation: fadeUp .52s ease both .38s; }
303
+ .abg { animation: fadeIn .9s ease both; }
304
+ </style>
305
+ ${customCssBlock}
306
+ </head>
307
+ <body>
308
+ <div class="page">
309
+ <div class="bg-panther abg"></div>
310
+ <div class="content">
311
+
312
+ <!-- NAVBAR -->
313
+ <nav style="display:flex;align-items:center;justify-content:space-between;padding:22px 46px;">
314
+ <div>
315
+ <div class="logo-name"><span class="xy">Xy</span>Priss</div>
316
+ <div class="logo-sub">Performance · Security · Productivity</div>
317
+ </div>
318
+ <a href="/" class="nav-back">
319
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
320
+ <path d="M19 12H5M5 12l7-7M5 12l7 7"/>
321
+ </svg>
322
+ Back to home
323
+ </a>
324
+ </nav>
325
+
326
+ <!-- MAIN -->
327
+ <main style="flex:1;display:flex;align-items:center;padding:0 8vw 28px;">
328
+ <div style="max-width:510px; margin-left: 2vw;">
329
+
330
+ <div class="badge a1">
331
+ <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
332
+ <circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>
333
+ </svg>
334
+ 404 &nbsp;·&nbsp; PAGE NOT FOUND &nbsp;
335
+ </div>
336
+
337
+ <div style="margin-top:22px;">
338
+ <div class="h1-white a2" style="word-break: break-all;">
339
+ Cannot <span class="h1-blue">${d.requestedMethod}</span> <span style="font-family: 'JetBrains Mono', monospace; font-size: 0.7em; color: var(--text-dim1);">${d.requestedPath}</span>
340
+ </div>
647
341
  </div>
648
- </body>
649
- </html>
650
- `;
342
+
343
+ <div class="a4">
344
+ <p class="sub-text" style="margin-top: 16px;">
345
+ ${messageText}
346
+ </p>
347
+ </div>
348
+
349
+ <div class="a5" style="display:flex;gap:12px;margin-top:28px;flex-wrap:wrap;">
350
+ <a href="${redirectUrl}" class="btn-primary">
351
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
352
+ <path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/>
353
+ <polyline points="9 22 9 12 15 12 15 22"/>
354
+ </svg>
355
+ ${redirectLabel}
356
+ </a>
357
+ <a href="javascript:history.back()" class="btn-secondary">
358
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
359
+ <path d="M5 12h14M12 5l7 7-7 7"/>
360
+ </svg>
361
+ Go Back
362
+ </a>
363
+ </div>
364
+
365
+ ${contactBlock}
366
+
367
+ </div>
368
+ </main>
369
+
370
+ <!-- FOOTER -->
371
+ <footer class="footer" style="padding:17px 46px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px;">
372
+ <span class="server-chip">${d.appName || "XyPriss"}</span>
373
+
374
+ <span class="footer-dim" id="footer-copy"></span>
375
+
376
+ <div style="display:flex;align-items:center;gap:16px;">
377
+ <a href="https://xypriss.nehonix.com/docs" target="_blank" rel="noopener noreferrer" class="footer-link">Documentation</a>
378
+ <span class="footer-dim">|</span>
379
+ <a href="https://github.com/Nehonix-Team/XyPriss" target="_blank" rel="noopener noreferrer" class="footer-link">GitHub</a>
380
+ </div>
381
+ </footer>
382
+
383
+ </div>
384
+ </div>
385
+
386
+ <script>
387
+ const year = new Date().getFullYear();
388
+ const copy = year > 2024
389
+ ? \`\\u00A9 2024\\u2013\${year} XyPriss. All rights reserved.\`
390
+ : '\\u00A9 2024 XyPriss. All rights reserved.';
391
+ document.getElementById('footer-copy').textContent = copy;
392
+ </script>
393
+ ${scriptBlock}
394
+ </body>
395
+ </html>`;
651
396
  return html;
652
397
  }
653
398