create-warlock 5.2.4 → 5.3.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.
Files changed (39) hide show
  1. package/esm/commands/create-warlock-app/index.mjs +1 -1
  2. package/esm/commands/create-warlock-app/index.mjs.map +1 -1
  3. package/esm/helpers/app.mjs +44 -17
  4. package/esm/helpers/app.mjs.map +1 -1
  5. package/package.json +2 -2
  6. package/templates/warlock/eslint.config.js +98 -98
  7. package/templates/warlock/postcss.config.mjs +6 -0
  8. package/templates/warlock/src/app/contact/controllers/contact.controller.ts +21 -0
  9. package/templates/warlock/src/app/contact/routes.ts +4 -0
  10. package/templates/warlock/src/app/{shared → home}/controllers/home-page.controller.ts +1 -7
  11. package/templates/warlock/src/app/home/services/home.service.ts +86 -0
  12. package/templates/warlock/src/app/locale/controllers/locale.controller.ts +16 -0
  13. package/templates/warlock/src/app/locale/routes.ts +4 -0
  14. package/templates/warlock/src/shared/contact.schema.ts +7 -0
  15. package/templates/warlock/src/shared/locale.schema.ts +11 -0
  16. package/templates/warlock/src/shared/locales.ts +7 -0
  17. package/templates/warlock/src/web/404.css +113 -0
  18. package/templates/warlock/src/web/404.page.tsx +23 -0
  19. package/templates/warlock/src/web/app.css +3 -0
  20. package/templates/warlock/src/web/home/components/contact-form-controls.tsx +42 -0
  21. package/templates/warlock/src/web/home/components/contact-section.tsx +108 -0
  22. package/templates/warlock/src/web/home/components/content-sections.tsx +61 -0
  23. package/templates/warlock/src/web/home/components/hero-section.tsx +68 -0
  24. package/templates/warlock/src/web/home/components/home-footer.tsx +35 -0
  25. package/templates/warlock/src/web/home/components/home-header.tsx +26 -0
  26. package/templates/warlock/src/web/home/components/logo.tsx +19 -0
  27. package/templates/warlock/src/web/home/components/runtime-preview.tsx +75 -0
  28. package/templates/warlock/src/web/home/hooks/use-contact-form.ts +57 -0
  29. package/templates/warlock/src/web/home/hooks/use-locale-switcher.ts +35 -0
  30. package/templates/warlock/src/web/home/index.page.tsx +44 -0
  31. package/templates/warlock/src/web/home/register.ts +30 -0
  32. package/templates/warlock/src/web/home/styles/home.css +1185 -0
  33. package/templates/warlock/src/web/root.tsx +49 -0
  34. package/templates/warlock/src/web/shared/utils/set-form-errors.ts +60 -0
  35. package/templates/warlock/tsconfig.json +3 -1
  36. package/templates/warlock/public/home.css +0 -523
  37. package/templates/warlock/src/app/shared/components/HomePageComponent.tsx +0 -229
  38. package/templates/warlock/src/app/shared/controllers/home-page.controller.tsx +0 -17
  39. /package/templates/warlock/src/app/{shared → home}/routes.ts +0 -0
@@ -0,0 +1,49 @@
1
+ import type { AppProps } from "@warlock.js/web";
2
+ import { Head, Scripts } from "@warlock.js/web";
3
+ import "./app.css";
4
+
5
+ /**
6
+ * The application root.
7
+ *
8
+ * NOT async, and it receives no request/response: it renders on the server and
9
+ * again in the browser during hydration, where neither exists.
10
+ */
11
+ export default function App({ children }: AppProps) {
12
+ return (
13
+ <html lang="en">
14
+ <head>
15
+ {/*
16
+ Placement only. The framework injects the page's `metadata`, the
17
+ stylesheet and preload tags for this route, and the canonical links
18
+ into <head> by default — <Head /> just says WHERE they land.
19
+
20
+ Do not add a <title> here: the page's `metadata` owns it, and a root
21
+ that emits one too produces two.
22
+ */}
23
+ <Head />
24
+ <link rel="icon" href="data:," />
25
+ </head>
26
+ <body>
27
+ {/*
28
+ REQUIRED — this is the hydration mount point, not a styling wrapper.
29
+
30
+ The browser runtime looks up `#root` and hydrates that element only.
31
+ Remove this div, or rename the id, and the page still renders from the
32
+ server but never becomes interactive: the runtime throws in the console
33
+ and nothing on screen changes.
34
+
35
+ Wrap it in your own markup freely, and put anything that must live
36
+ outside the hydrated tree (a static footer, a portal target) outside
37
+ it — just keep an element with `id="root"` around {children}.
38
+ */}
39
+ <div id="root">{children}</div>
40
+ {/*
41
+ The hydration payload and module tags. Written explicitly because
42
+ placement occasionally matters — a CSP nonce, or ordering against
43
+ your own scripts.
44
+ */}
45
+ <Scripts />
46
+ </body>
47
+ </html>
48
+ );
49
+ }
@@ -0,0 +1,60 @@
1
+ import type { FormInterface } from "@mongez/react-form";
2
+
3
+ export type FormErrorResponseKeys = {
4
+ errors?: string;
5
+ input?: string;
6
+ error?: string;
7
+ };
8
+
9
+ type ErrorRecord = Record<string, string>;
10
+
11
+ function isRecord(value: unknown): value is Record<string, unknown> {
12
+ return typeof value === "object" && value !== null && !Array.isArray(value);
13
+ }
14
+
15
+ export function setFormErrors(
16
+ error: unknown,
17
+ form: Pick<FormInterface, "setErrors">,
18
+ keys: FormErrorResponseKeys = {},
19
+ ): boolean {
20
+ const errorsKey = keys.errors ?? "errors";
21
+ const inputKey = keys.input ?? "input";
22
+ const errorKey = keys.error ?? "error";
23
+
24
+ try {
25
+ if (!isRecord(error) || !isRecord(error.body)) {
26
+ return false;
27
+ }
28
+
29
+ const errors = error.body[errorsKey];
30
+ const fieldErrors: ErrorRecord = {};
31
+
32
+ if (Array.isArray(errors)) {
33
+ for (const issue of errors) {
34
+ if (!isRecord(issue)) continue;
35
+
36
+ const input = issue[inputKey];
37
+ const message = issue[errorKey];
38
+
39
+ if (typeof input === "string" && input && typeof message === "string") {
40
+ fieldErrors[input] = message;
41
+ }
42
+ }
43
+ } else if (isRecord(errors)) {
44
+ for (const [input, message] of Object.entries(errors)) {
45
+ if (input && typeof message === "string") {
46
+ fieldErrors[input] = message;
47
+ }
48
+ }
49
+ }
50
+
51
+ if (Object.keys(fieldErrors).length === 0) {
52
+ return false;
53
+ }
54
+
55
+ form.setErrors(fieldErrors);
56
+ return true;
57
+ } catch {
58
+ return false;
59
+ }
60
+ }
@@ -22,7 +22,9 @@
22
22
  "moduleResolution": "bundler",
23
23
  "resolveJsonModule": true,
24
24
  "paths": {
25
- "app/*": ["./src/app/*"]
25
+ "web/*": ["./src/web/*"],
26
+ "app/*": ["./src/app/*"],
27
+ "@shared/*": ["./src/shared/*"]
26
28
  },
27
29
  "noEmit": true
28
30
  },
@@ -1,523 +0,0 @@
1
- /* Warlock.js Homepage Styles */
2
-
3
- * {
4
- margin: 0;
5
- padding: 0;
6
- box-sizing: border-box;
7
- }
8
-
9
- body {
10
- font-family:
11
- "Inter",
12
- -apple-system,
13
- BlinkMacSystemFont,
14
- "Segoe UI",
15
- Roboto,
16
- Oxygen,
17
- Ubuntu,
18
- Cantarell,
19
- sans-serif;
20
- background: #0a0a0f;
21
- color: #e4e4e7;
22
- line-height: 1.6;
23
- overflow-x: hidden;
24
- }
25
-
26
- .warlock-home {
27
- min-height: 100vh;
28
- position: relative;
29
- overflow: hidden;
30
- }
31
-
32
- /* Animated Background */
33
- .warlock-home::before {
34
- content: "";
35
- position: fixed;
36
- top: -50%;
37
- left: -50%;
38
- width: 200%;
39
- height: 200%;
40
- background:
41
- radial-gradient(
42
- circle at 20% 50%,
43
- rgba(234, 179, 8, 0.08) 0%,
44
- transparent 50%
45
- ),
46
- radial-gradient(
47
- circle at 80% 80%,
48
- rgba(34, 197, 94, 0.06) 0%,
49
- transparent 50%
50
- ),
51
- radial-gradient(
52
- circle at 40% 20%,
53
- rgba(234, 179, 8, 0.05) 0%,
54
- transparent 50%
55
- );
56
- animation: backgroundFloat 20s ease-in-out infinite;
57
- z-index: 0;
58
- }
59
-
60
- @keyframes backgroundFloat {
61
- 0%,
62
- 100% {
63
- transform: translate(0, 0) rotate(0deg);
64
- }
65
- 33% {
66
- transform: translate(30px, -30px) rotate(1deg);
67
- }
68
- 66% {
69
- transform: translate(-20px, 20px) rotate(-1deg);
70
- }
71
- }
72
-
73
- /* Container */
74
- .warlock-container {
75
- max-width: 1200px;
76
- margin: 0 auto;
77
- padding: 0 2rem;
78
- position: relative;
79
- z-index: 1;
80
- }
81
-
82
- /* Header */
83
- .warlock-header {
84
- padding: 2rem 0;
85
- display: flex;
86
- justify-content: space-between;
87
- align-items: center;
88
- border-bottom: 1px solid rgba(255, 255, 255, 0.05);
89
- backdrop-filter: blur(10px);
90
- }
91
-
92
- .warlock-logo {
93
- display: flex;
94
- align-items: center;
95
- gap: 1rem;
96
- font-size: 1.5rem;
97
- font-weight: 700;
98
- color: #fbbf24;
99
- text-decoration: none;
100
- transition: transform 0.3s ease;
101
- }
102
-
103
- .warlock-logo:hover {
104
- transform: scale(1.05);
105
- }
106
-
107
- .warlock-logo-icon {
108
- font-size: 2rem;
109
- animation: pulse 2s ease-in-out infinite;
110
- }
111
-
112
- @keyframes pulse {
113
- 0%,
114
- 100% {
115
- opacity: 1;
116
- transform: scale(1);
117
- }
118
- 50% {
119
- opacity: 0.8;
120
- transform: scale(1.1);
121
- }
122
- }
123
-
124
- .warlock-nav {
125
- display: flex;
126
- gap: 2rem;
127
- align-items: center;
128
- }
129
-
130
- .warlock-nav a {
131
- color: #a1a1aa;
132
- text-decoration: none;
133
- font-weight: 500;
134
- transition: color 0.3s ease;
135
- position: relative;
136
- }
137
-
138
- .warlock-nav a::after {
139
- content: "";
140
- position: absolute;
141
- bottom: -4px;
142
- left: 0;
143
- width: 0;
144
- height: 2px;
145
- background: linear-gradient(90deg, #fbbf24, #22c55e);
146
- transition: width 0.3s ease;
147
- }
148
-
149
- .warlock-nav a:hover {
150
- color: #fbbf24;
151
- }
152
-
153
- .warlock-nav a:hover::after {
154
- width: 100%;
155
- }
156
-
157
- /* Hero Section */
158
- .warlock-hero {
159
- padding: 8rem 0;
160
- text-align: center;
161
- }
162
-
163
- .warlock-hero h1 {
164
- font-size: 4.5rem;
165
- font-weight: 900;
166
- margin-bottom: 1.5rem;
167
- background: linear-gradient(135deg, #fbbf24 0%, #22c55e 100%);
168
- -webkit-background-clip: text;
169
- -webkit-text-fill-color: transparent;
170
- background-clip: text;
171
- line-height: 1.2;
172
- animation: fadeInUp 0.8s ease-out;
173
- }
174
-
175
- .warlock-hero p {
176
- font-size: 1.5rem;
177
- color: #a1a1aa;
178
- margin-bottom: 3rem;
179
- max-width: 700px;
180
- margin-left: auto;
181
- margin-right: auto;
182
- animation: fadeInUp 0.8s ease-out 0.2s both;
183
- }
184
-
185
- @keyframes fadeInUp {
186
- from {
187
- opacity: 0;
188
- transform: translateY(30px);
189
- }
190
- to {
191
- opacity: 1;
192
- transform: translateY(0);
193
- }
194
- }
195
-
196
- .warlock-cta {
197
- display: flex;
198
- gap: 1.5rem;
199
- justify-content: center;
200
- flex-wrap: wrap;
201
- animation: fadeInUp 0.8s ease-out 0.4s both;
202
- }
203
-
204
- .warlock-btn {
205
- padding: 1rem 2.5rem;
206
- font-size: 1.1rem;
207
- font-weight: 600;
208
- text-decoration: none;
209
- border-radius: 12px;
210
- transition: all 0.3s ease;
211
- display: inline-flex;
212
- align-items: center;
213
- gap: 0.5rem;
214
- position: relative;
215
- overflow: hidden;
216
- }
217
-
218
- .warlock-btn::before {
219
- content: "";
220
- position: absolute;
221
- top: 50%;
222
- left: 50%;
223
- width: 0;
224
- height: 0;
225
- border-radius: 50%;
226
- background: rgba(255, 255, 255, 0.2);
227
- transform: translate(-50%, -50%);
228
- transition:
229
- width 0.6s ease,
230
- height 0.6s ease;
231
- }
232
-
233
- .warlock-btn:hover::before {
234
- width: 300px;
235
- height: 300px;
236
- }
237
-
238
- .warlock-btn-primary {
239
- background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%);
240
- color: #0a0a0f;
241
- box-shadow: 0 10px 30px rgba(251, 191, 36, 0.3);
242
- }
243
-
244
- .warlock-btn-primary:hover {
245
- transform: translateY(-3px);
246
- box-shadow: 0 15px 40px rgba(251, 191, 36, 0.4);
247
- }
248
-
249
- .warlock-btn-secondary {
250
- background: rgba(255, 255, 255, 0.05);
251
- color: #e4e4e7;
252
- border: 2px solid rgba(255, 255, 255, 0.1);
253
- backdrop-filter: blur(10px);
254
- }
255
-
256
- .warlock-btn-secondary:hover {
257
- background: rgba(255, 255, 255, 0.1);
258
- border-color: rgba(251, 191, 36, 0.3);
259
- transform: translateY(-3px);
260
- }
261
-
262
- /* Features Section */
263
- .warlock-features {
264
- padding: 6rem 0;
265
- }
266
-
267
- .warlock-section-title {
268
- text-align: center;
269
- font-size: 3rem;
270
- font-weight: 800;
271
- margin-bottom: 1rem;
272
- background: linear-gradient(135deg, #fbbf24 0%, #22c55e 100%);
273
- -webkit-background-clip: text;
274
- -webkit-text-fill-color: transparent;
275
- background-clip: text;
276
- }
277
-
278
- .warlock-section-subtitle {
279
- text-align: center;
280
- font-size: 1.25rem;
281
- color: #71717a;
282
- margin-bottom: 4rem;
283
- }
284
-
285
- .warlock-features-grid {
286
- display: grid;
287
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
288
- gap: 2rem;
289
- }
290
-
291
- .warlock-feature-card {
292
- background: rgba(255, 255, 255, 0.02);
293
- border: 1px solid rgba(255, 255, 255, 0.05);
294
- border-radius: 16px;
295
- padding: 2.5rem;
296
- transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
297
- backdrop-filter: blur(10px);
298
- position: relative;
299
- overflow: hidden;
300
- }
301
-
302
- .warlock-feature-card::before {
303
- content: "";
304
- position: absolute;
305
- top: 0;
306
- left: 0;
307
- right: 0;
308
- height: 3px;
309
- background: linear-gradient(90deg, #fbbf24, #22c55e);
310
- transform: scaleX(0);
311
- transform-origin: left;
312
- transition: transform 0.4s ease;
313
- }
314
-
315
- .warlock-feature-card:hover::before {
316
- transform: scaleX(1);
317
- }
318
-
319
- .warlock-feature-card:hover {
320
- transform: translateY(-8px);
321
- background: rgba(255, 255, 255, 0.05);
322
- border-color: rgba(251, 191, 36, 0.2);
323
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
324
- }
325
-
326
- .warlock-feature-icon {
327
- font-size: 3rem;
328
- margin-bottom: 1.5rem;
329
- display: block;
330
- }
331
-
332
- .warlock-feature-card h3 {
333
- font-size: 1.5rem;
334
- font-weight: 700;
335
- margin-bottom: 1rem;
336
- color: #fbbf24;
337
- }
338
-
339
- .warlock-feature-card p {
340
- color: #a1a1aa;
341
- line-height: 1.8;
342
- }
343
-
344
- /* Quick Start Section */
345
- .warlock-quickstart {
346
- padding: 6rem 0;
347
- background: rgba(255, 255, 255, 0.01);
348
- border-top: 1px solid rgba(255, 255, 255, 0.05);
349
- border-bottom: 1px solid rgba(255, 255, 255, 0.05);
350
- }
351
-
352
- .warlock-code-block {
353
- background: rgba(0, 0, 0, 0.4);
354
- border: 1px solid rgba(255, 255, 255, 0.1);
355
- border-radius: 16px;
356
- padding: 2rem;
357
- margin-top: 2rem;
358
- position: relative;
359
- overflow: hidden;
360
- backdrop-filter: blur(20px);
361
- }
362
-
363
- .warlock-code-header {
364
- display: flex;
365
- align-items: center;
366
- gap: 0.5rem;
367
- margin-bottom: 1.5rem;
368
- padding-bottom: 1rem;
369
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
370
- }
371
-
372
- .warlock-code-dot {
373
- width: 12px;
374
- height: 12px;
375
- border-radius: 50%;
376
- }
377
-
378
- .warlock-code-dot:nth-child(1) {
379
- background: #ef4444;
380
- }
381
- .warlock-code-dot:nth-child(2) {
382
- background: #f59e0b;
383
- }
384
- .warlock-code-dot:nth-child(3) {
385
- background: #22c55e;
386
- }
387
-
388
- .warlock-code-content {
389
- font-family: "Fira Code", "Courier New", monospace;
390
- font-size: 1rem;
391
- line-height: 2;
392
- color: #e4e4e7;
393
- }
394
-
395
- .warlock-code-line {
396
- display: block;
397
- transition: background 0.2s ease;
398
- padding: 0.25rem 0.5rem;
399
- border-radius: 4px;
400
- }
401
-
402
- .warlock-code-line:hover {
403
- background: rgba(251, 191, 36, 0.1);
404
- }
405
-
406
- .warlock-code-comment {
407
- color: #71717a;
408
- }
409
-
410
- .warlock-code-command {
411
- color: #22c55e;
412
- }
413
-
414
- .warlock-code-flag {
415
- color: #fbbf24;
416
- }
417
-
418
- .warlock-code-string {
419
- color: #06b6d4;
420
- }
421
-
422
- /* Footer */
423
- .warlock-footer {
424
- padding: 4rem 0 2rem;
425
- text-align: center;
426
- border-top: 1px solid rgba(255, 255, 255, 0.05);
427
- }
428
-
429
- .warlock-social-links {
430
- display: flex;
431
- justify-content: center;
432
- gap: 2rem;
433
- margin-bottom: 2rem;
434
- }
435
-
436
- .warlock-social-link {
437
- display: inline-flex;
438
- align-items: center;
439
- gap: 0.75rem;
440
- padding: 1rem 2rem;
441
- background: rgba(255, 255, 255, 0.03);
442
- border: 1px solid rgba(255, 255, 255, 0.1);
443
- border-radius: 12px;
444
- color: #e4e4e7;
445
- text-decoration: none;
446
- font-weight: 600;
447
- transition: all 0.3s ease;
448
- backdrop-filter: blur(10px);
449
- }
450
-
451
- .warlock-social-link:hover {
452
- background: rgba(255, 255, 255, 0.08);
453
- border-color: rgba(251, 191, 36, 0.3);
454
- transform: translateY(-3px);
455
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
456
- }
457
-
458
- .warlock-social-icon {
459
- font-size: 1.5rem;
460
- }
461
-
462
- .warlock-footer-text {
463
- color: #71717a;
464
- font-size: 0.95rem;
465
- }
466
-
467
- .warlock-footer-text a {
468
- color: #fbbf24;
469
- text-decoration: none;
470
- transition: color 0.3s ease;
471
- }
472
-
473
- .warlock-footer-text a:hover {
474
- color: #f59e0b;
475
- }
476
-
477
- /* Responsive Design */
478
- @media (max-width: 768px) {
479
- .warlock-hero h1 {
480
- font-size: 3rem;
481
- }
482
-
483
- .warlock-hero p {
484
- font-size: 1.25rem;
485
- }
486
-
487
- .warlock-section-title {
488
- font-size: 2.25rem;
489
- }
490
-
491
- .warlock-features-grid {
492
- grid-template-columns: 1fr;
493
- }
494
-
495
- .warlock-nav {
496
- display: none;
497
- }
498
-
499
- .warlock-social-links {
500
- flex-direction: column;
501
- gap: 1rem;
502
- }
503
-
504
- .warlock-cta {
505
- flex-direction: column;
506
- }
507
-
508
- .warlock-btn {
509
- width: 100%;
510
- justify-content: center;
511
- }
512
- }
513
-
514
- /* Smooth Scroll */
515
- html {
516
- scroll-behavior: smooth;
517
- }
518
-
519
- /* Selection */
520
- ::selection {
521
- background: rgba(251, 191, 36, 0.3);
522
- color: #fbbf24;
523
- }