nsbp-cli 0.1.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 (57) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/README.md +150 -0
  3. package/bin/nsbp.js +145 -0
  4. package/package.json +43 -0
  5. package/scripts/sync-template.js +277 -0
  6. package/templates/basic/.prettierignore +5 -0
  7. package/templates/basic/.prettierrc +5 -0
  8. package/templates/basic/README.md +13 -0
  9. package/templates/basic/package.json +101 -0
  10. package/templates/basic/postcss.config.js +7 -0
  11. package/templates/basic/public/favicon.ico +0 -0
  12. package/templates/basic/public/images/test/0.jpg +0 -0
  13. package/templates/basic/public/images/test/1.jpg +0 -0
  14. package/templates/basic/public/images/test/2.jpg +0 -0
  15. package/templates/basic/public/images/test/3.jpg +0 -0
  16. package/templates/basic/public/images/test/4.jpg +0 -0
  17. package/templates/basic/public/images/test/5.jpg +0 -0
  18. package/templates/basic/scripts/start.js +3 -0
  19. package/templates/basic/src/Routers.tsx +40 -0
  20. package/templates/basic/src/client/index.tsx +37 -0
  21. package/templates/basic/src/component/Header.tsx +38 -0
  22. package/templates/basic/src/component/Layout.tsx +24 -0
  23. package/templates/basic/src/component/Loading.tsx +7 -0
  24. package/templates/basic/src/component/Theme.tsx +14 -0
  25. package/templates/basic/src/containers/Home.tsx +435 -0
  26. package/templates/basic/src/containers/Login.tsx +32 -0
  27. package/templates/basic/src/containers/Photo.tsx +162 -0
  28. package/templates/basic/src/css/test.css +13 -0
  29. package/templates/basic/src/css/test.less +8 -0
  30. package/templates/basic/src/css/test2.sass +7 -0
  31. package/templates/basic/src/css/test3.scss +8 -0
  32. package/templates/basic/src/externals/less.d.ts +4 -0
  33. package/templates/basic/src/externals/window.d.ts +3 -0
  34. package/templates/basic/src/reducers/home.ts +17 -0
  35. package/templates/basic/src/reducers/index.ts +26 -0
  36. package/templates/basic/src/reducers/photo.ts +23 -0
  37. package/templates/basic/src/server/index.ts +29 -0
  38. package/templates/basic/src/server/photo.ts +118 -0
  39. package/templates/basic/src/server/utils.tsx +158 -0
  40. package/templates/basic/src/services/home.ts +52 -0
  41. package/templates/basic/src/services/photo.ts +64 -0
  42. package/templates/basic/src/store/constants.ts +4 -0
  43. package/templates/basic/src/store/index.ts +14 -0
  44. package/templates/basic/src/styled/common.ts +26 -0
  45. package/templates/basic/src/styled/component/header.ts +166 -0
  46. package/templates/basic/src/styled/component/layout.ts +10 -0
  47. package/templates/basic/src/styled/home.ts +789 -0
  48. package/templates/basic/src/styled/photo.ts +44 -0
  49. package/templates/basic/src/styled/test.ts +14 -0
  50. package/templates/basic/src/utils/clientConfig.ts +2 -0
  51. package/templates/basic/src/utils/config.ts +7 -0
  52. package/templates/basic/src/utils/fetch.ts +26 -0
  53. package/templates/basic/src/utils/index.ts +45 -0
  54. package/templates/basic/tsconfig.json +18 -0
  55. package/templates/basic/webpack.base.js +262 -0
  56. package/templates/basic/webpack.client.js +26 -0
  57. package/templates/basic/webpack.server.js +33 -0
@@ -0,0 +1,789 @@
1
+ import styled, { keyframes } from 'styled-components'
2
+
3
+ // ============================================
4
+ // 基础容器
5
+ // ============================================
6
+
7
+ export const GlobalStyle = styled.div`
8
+ overflow-x: hidden;
9
+ * {
10
+ box-sizing: border-box;
11
+ margin: 0;
12
+ padding: 0;
13
+ }
14
+
15
+ html, body, #root {
16
+ height: 100%;
17
+ }
18
+
19
+ body {
20
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
21
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
22
+ color: #2d3748;
23
+ -webkit-font-smoothing: antialiased;
24
+ -moz-osx-font-smoothing: grayscale;
25
+ overflow-x: hidden;
26
+ scroll-behavior: smooth;
27
+ }
28
+
29
+ /* 页面加载动画 */
30
+ .page-loader {
31
+ position: fixed;
32
+ top: 0;
33
+ left: 0;
34
+ width: 100%;
35
+ height: 100%;
36
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
37
+ display: flex;
38
+ align-items: center;
39
+ justify-content: center;
40
+ z-index: 9999;
41
+ opacity: 1;
42
+ transition: opacity 0.5s ease;
43
+ }
44
+
45
+ .page-loader.fade-out {
46
+ opacity: 0;
47
+ pointer-events: none;
48
+ }
49
+
50
+ .loader-spinner {
51
+ width: 50px;
52
+ height: 50px;
53
+ border: 3px solid rgba(255, 255, 255, 0.3);
54
+ border-top-color: #ffffff;
55
+ border-radius: 50%;
56
+ animation: spin 1s linear infinite;
57
+ }
58
+
59
+ @keyframes spin {
60
+ to { transform: rotate(360deg); }
61
+ }
62
+
63
+ /* 淡入动画 */
64
+ .fade-in {
65
+ opacity: 0;
66
+ transform: translateY(20px);
67
+ animation: fadeInUp 0.8s ease forwards;
68
+ }
69
+
70
+ @keyframes fadeInUp {
71
+ to {
72
+ opacity: 1;
73
+ transform: translateY(0);
74
+ }
75
+ }
76
+
77
+ a {
78
+ color: #667eea;
79
+ text-decoration: none;
80
+ transition: all 0.3s ease;
81
+
82
+ &:hover {
83
+ color: #764ba2;
84
+ transform: translateY(-1px);
85
+ }
86
+ }
87
+ `
88
+
89
+ export const PageWrapper = styled.div`
90
+ min-height: 100vh;
91
+ display: flex;
92
+ flex-direction: column;
93
+ width: 100%;
94
+ `
95
+
96
+ // ============================================
97
+ // Hero Section(首屏)
98
+ // ============================================
99
+
100
+ export const HeroSection = styled.section`
101
+ display: flex;
102
+ flex-direction: column;
103
+ align-items: center;
104
+ justify-content: center;
105
+ padding: 4rem 0;
106
+ min-height: 85vh;
107
+ text-align: center;
108
+ position: relative;
109
+ overflow: hidden;
110
+
111
+ /* 主背景渐变 */
112
+ &::before {
113
+ content: '';
114
+ position: absolute;
115
+ top: 0;
116
+ left: 0;
117
+ right: 0;
118
+ bottom: 0;
119
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #667eea 100%);
120
+ z-index: -2;
121
+ }
122
+
123
+ /* 几何装饰层 */
124
+ &::after {
125
+ content: '';
126
+ position: absolute;
127
+ top: 0;
128
+ left: 0;
129
+ right: 0;
130
+ bottom: 0;
131
+ background-image:
132
+ radial-gradient(circle at 20% 80%, rgba(255, 255, 255, 0.1) 0%, transparent 50%),
133
+ radial-gradient(circle at 80% 20%, rgba(255, 255, 255, 0.1) 0%, transparent 50%),
134
+ radial-gradient(circle at 40% 40%, rgba(255, 255, 255, 0.05) 0%, transparent 50%);
135
+ z-index: -1;
136
+ }
137
+
138
+ /* 动态光效 */
139
+ .hero-glow {
140
+ position: absolute;
141
+ width: 300px;
142
+ height: 300px;
143
+ border-radius: 50%;
144
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
145
+ filter: blur(40px);
146
+ animation: float 6s ease-in-out infinite;
147
+ }
148
+
149
+ .hero-glow:nth-child(1) {
150
+ top: 20%;
151
+ left: 10%;
152
+ animation-delay: 0s;
153
+ }
154
+
155
+ .hero-glow:nth-child(2) {
156
+ bottom: 20%;
157
+ right: 10%;
158
+ animation-delay: 3s;
159
+ }
160
+
161
+ @keyframes float {
162
+ 0%, 100% { transform: translateY(0px) scale(1); }
163
+ 50% { transform: translateY(-20px) scale(1.1); }
164
+ }
165
+
166
+ @media (max-width: 768px) {
167
+ padding: 3rem 1.5rem;
168
+ min-height: 75vh;
169
+ }
170
+
171
+ @media (max-width: 480px) {
172
+ padding: 2rem 1rem;
173
+ min-height: 65vh;
174
+ }
175
+ `
176
+
177
+ export const HeroContent = styled.div`
178
+ width: 100%;
179
+ position: relative;
180
+ z-index: 1;
181
+ padding: 0;
182
+
183
+ @media (max-width: 768px) {
184
+ padding: 0 1.5rem;
185
+ }
186
+
187
+ /* 移动端隐藏装饰光效以提升性能 */
188
+ @media (max-width: 768px) {
189
+ .hero-glow {
190
+ display: none;
191
+ }
192
+ }
193
+ `
194
+
195
+ export const HeroTitle = styled.h1`
196
+ font-size: clamp(3rem, 6vw, 4.5rem);
197
+ font-weight: 800;
198
+ color: #ffffff;
199
+ margin-bottom: 1rem;
200
+ line-height: 1.2;
201
+ letter-spacing: -0.02em;
202
+ text-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
203
+ position: relative;
204
+ z-index: 2;
205
+ `
206
+
207
+ export const HeroSubtitle = styled.p`
208
+ font-size: clamp(1.5rem, 2.5vw, 2rem);
209
+ color: #ffffff;
210
+ margin: 0 auto 2rem;
211
+ line-height: 1.8;
212
+ max-width: 1200px;
213
+ text-align: center;
214
+ font-weight: 500;
215
+ text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
216
+ position: relative;
217
+ z-index: 2;
218
+ `
219
+
220
+ export const HeroBadge = styled.span`
221
+ display: inline-block;
222
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.1));
223
+ backdrop-filter: blur(15px);
224
+ padding: 0.75rem 1.5rem;
225
+ border-radius: 999px;
226
+ font-size: 0.9rem;
227
+ font-weight: 600;
228
+ color: #ffffff;
229
+ margin-bottom: 1.5rem;
230
+ border: 1px solid rgba(255, 255, 255, 0.2);
231
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
232
+ position: relative;
233
+ overflow: hidden;
234
+
235
+ &::before {
236
+ content: '';
237
+ position: absolute;
238
+ top: 0;
239
+ left: -100%;
240
+ width: 100%;
241
+ height: 100%;
242
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
243
+ animation: shine 3s infinite;
244
+ }
245
+
246
+ @keyframes shine {
247
+ 0% { left: -100%; }
248
+ 100% { left: 100%; }
249
+ }
250
+ `
251
+
252
+ export const HeroStats = styled.div`
253
+ display: grid;
254
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
255
+ gap: 1.5rem;
256
+ margin-top: 3rem;
257
+ width: 100%;
258
+ max-width: 1200px;
259
+ margin-left: auto;
260
+ margin-right: auto;
261
+
262
+ @media (max-width: 768px) {
263
+ grid-template-columns: repeat(2, 1fr);
264
+ gap: 1rem;
265
+ }
266
+
267
+ @media (max-width: 480px) {
268
+ grid-template-columns: 1fr;
269
+ gap: 1rem;
270
+ }
271
+ `
272
+
273
+ export const StatCard = styled.div`
274
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.1));
275
+ backdrop-filter: blur(20px);
276
+ padding: 1.75rem;
277
+ border-radius: 16px;
278
+ border: 1px solid rgba(255, 255, 255, 0.3);
279
+ text-align: center;
280
+ transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
281
+ overflow: hidden;
282
+ max-width: 100%;
283
+ position: relative;
284
+ box-shadow:
285
+ 0 4px 20px rgba(0, 0, 0, 0.1),
286
+ inset 0 1px 0 rgba(255, 255, 255, 0.2);
287
+
288
+ &::before {
289
+ content: '';
290
+ position: absolute;
291
+ top: 0;
292
+ left: 0;
293
+ right: 0;
294
+ height: 1px;
295
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
296
+ }
297
+
298
+ &:hover {
299
+ transform: translateY(-8px) scale(1.02);
300
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.15));
301
+ box-shadow:
302
+ 0 12px 40px rgba(0, 0, 0, 0.15),
303
+ inset 0 1px 0 rgba(255, 255, 255, 0.3);
304
+ }
305
+
306
+ @media (max-width: 768px) {
307
+ padding: 1.5rem;
308
+ }
309
+
310
+ @media (max-width: 480px) {
311
+ padding: 1.25rem;
312
+ }
313
+ `
314
+
315
+ export const StatValue = styled.div`
316
+ font-size: 2.75rem;
317
+ font-weight: 800;
318
+ background: linear-gradient(135deg, #ffffff, #e2e8f0);
319
+ -webkit-background-clip: text;
320
+ -webkit-text-fill-color: transparent;
321
+ background-clip: text;
322
+ margin-bottom: 0.5rem;
323
+ text-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
324
+ `
325
+
326
+ export const StatLabel = styled.div`
327
+ font-size: 0.9rem;
328
+ color: rgba(255, 255, 255, 0.95);
329
+ font-weight: 500;
330
+ text-transform: uppercase;
331
+ letter-spacing: 0.5px;
332
+ `
333
+
334
+ // ============================================
335
+ // 技术特性卡片
336
+ // ============================================
337
+
338
+ export const TechSection = styled.section`
339
+ padding: 6rem 0;
340
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%);
341
+ display: flex;
342
+ flex-direction: column;
343
+ align-items: center;
344
+ width: 100%;
345
+ position: relative;
346
+ overflow: hidden;
347
+
348
+ &::before {
349
+ content: '';
350
+ position: absolute;
351
+ top: 0;
352
+ left: 0;
353
+ right: 0;
354
+ bottom: 0;
355
+ background-image:
356
+ radial-gradient(circle at 10% 20%, rgba(102, 126, 234, 0.05) 0%, transparent 50%),
357
+ radial-gradient(circle at 90% 80%, rgba(118, 75, 162, 0.05) 0%, transparent 50%);
358
+ pointer-events: none;
359
+ }
360
+ `
361
+
362
+ export const SectionHeader = styled.div`
363
+ text-align: center;
364
+ margin-bottom: 4rem;
365
+ `
366
+
367
+ export const SectionTitle = styled.h2`
368
+ font-size: clamp(1.8rem, 4vw, 2.5rem);
369
+ font-weight: 700;
370
+ color: #2d3748;
371
+ margin-bottom: 0.75rem;
372
+ `
373
+
374
+ export const SectionDescription = styled.p`
375
+ font-size: 1.1rem;
376
+ color: #6b7280;
377
+ max-width: 600px;
378
+ margin: 0 auto 2rem;
379
+ line-height: 1.7;
380
+ `
381
+
382
+ export const FeatureGrid = styled.div`
383
+ display: grid;
384
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
385
+ gap: 2rem;
386
+ max-width: 1200px;
387
+ margin: 0 auto;
388
+
389
+ @media (max-width: 768px) {
390
+ grid-template-columns: 1fr;
391
+ gap: 1.5rem;
392
+ }
393
+ `
394
+
395
+ export const FeatureCard = styled.div`
396
+ background: linear-gradient(135deg, #ffffff 0%, #fafbfc 100%);
397
+ border-radius: 20px;
398
+ padding: 2.5rem;
399
+ box-shadow:
400
+ 0 4px 20px rgba(0, 0, 0, 0.08),
401
+ 0 1px 3px rgba(0, 0, 0, 0.04);
402
+ transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
403
+ border: 1px solid rgba(0, 0, 0, 0.06);
404
+ overflow: hidden;
405
+ max-width: 100%;
406
+ position: relative;
407
+
408
+ &::before {
409
+ content: '';
410
+ position: absolute;
411
+ top: 0;
412
+ left: 0;
413
+ right: 0;
414
+ height: 3px;
415
+ background: linear-gradient(90deg, #667eea, #764ba2);
416
+ transform: scaleX(0);
417
+ transform-origin: left;
418
+ transition: transform 0.3s ease;
419
+ }
420
+
421
+ &:hover {
422
+ transform: translateY(-8px) scale(1.02);
423
+ box-shadow:
424
+ 0 20px 40px rgba(0, 0, 0, 0.12),
425
+ 0 8px 16px rgba(0, 0, 0, 0.08);
426
+ border-color: rgba(102, 126, 234, 0.2);
427
+ }
428
+
429
+ &:hover::before {
430
+ transform: scaleX(1);
431
+ }
432
+ `
433
+
434
+ export const CardIcon = styled.div`
435
+ font-size: 3rem;
436
+ margin-bottom: 1.25rem;
437
+ background: linear-gradient(135deg, #667eea, #764ba2);
438
+ -webkit-background-clip: text;
439
+ -webkit-text-fill-color: transparent;
440
+ background-clip: text;
441
+ filter: drop-shadow(0 2px 4px rgba(102, 126, 234, 0.2));
442
+ `
443
+
444
+ export const CardTitle = styled.h3`
445
+ font-size: 1.3rem;
446
+ font-weight: 600;
447
+ color: #2d3748;
448
+ margin-bottom: 0.75rem;
449
+ `
450
+
451
+ export const CardDescription = styled.p`
452
+ font-size: 0.95rem;
453
+ color: #6b7280;
454
+ line-height: 1.7;
455
+ margin-bottom: 1rem;
456
+ `
457
+
458
+ export const CodeExample = styled.pre`
459
+ background: #1e293b;
460
+ color: #f8f8fa;
461
+ padding: 1rem;
462
+ border-radius: 8px;
463
+ overflow-x: auto;
464
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, monospace;
465
+ font-size: 0.85rem;
466
+ line-height: 1.6;
467
+ margin-bottom: 1rem;
468
+ max-width: 100%;
469
+ word-wrap: break-word;
470
+ white-space: pre-wrap;
471
+
472
+ @media (max-width: 768px) {
473
+ overflow-x: hidden;
474
+ }
475
+ `
476
+
477
+ // ============================================
478
+ // 对比表格
479
+ // ============================================
480
+
481
+ export const ComparisonSection = styled.section`
482
+ padding: 6rem 0;
483
+ max-width: 1000px;
484
+ margin: 0 auto;
485
+ `
486
+
487
+ export const ComparisonTable = styled.table`
488
+ width: 100%;
489
+ border-collapse: collapse;
490
+ background: #ffffff;
491
+ border-radius: 12px;
492
+ overflow: hidden;
493
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
494
+ table-layout: fixed;
495
+ word-wrap: break-word;
496
+
497
+ @media (max-width: 768px) {
498
+ display: block;
499
+ overflow-x: hidden;
500
+ }
501
+ `
502
+
503
+ export const TableHeader = styled.thead`
504
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
505
+ color: #ffffff;
506
+ `
507
+
508
+ export const TableRow = styled.tr`
509
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06);
510
+ transition: background 0.2s ease;
511
+
512
+ &:last-child {
513
+ border-bottom: none;
514
+ }
515
+
516
+ &:hover {
517
+ background: rgba(102, 126, 234, 0.03);
518
+ }
519
+ `
520
+
521
+ export const TableCell = styled.td`
522
+ padding: 1rem 1.5rem;
523
+ font-size: 0.95rem;
524
+ color: #2d3748;
525
+ border-right: 1px solid rgba(0, 0, 0, 0.06);
526
+ word-wrap: break-word;
527
+
528
+ &:last-child {
529
+ border-right: none;
530
+ }
531
+
532
+ @media (max-width: 768px) {
533
+ padding: 0.75rem 1rem;
534
+ font-size: 0.85rem;
535
+ }
536
+ `
537
+
538
+ export const TableHeaderCell = styled.th`
539
+ padding: 1rem 1.5rem;
540
+ text-align: left;
541
+ font-weight: 600;
542
+ font-size: 0.9rem;
543
+ word-wrap: break-word;
544
+
545
+ @media (max-width: 768px) {
546
+ padding: 0.75rem 1rem;
547
+ }
548
+ `
549
+
550
+ export const NsbpJSBadge = styled.span`
551
+ background: #667eea;
552
+ color: #ffffff;
553
+ padding: 0.25rem 0.75rem;
554
+ border-radius: 4px;
555
+ font-size: 0.75rem;
556
+ font-weight: 600;
557
+ `
558
+
559
+ export const NextJSBadge = styled.span`
560
+ background: #6b7280;
561
+ color: #ffffff;
562
+ padding: 0.25rem 0.75rem;
563
+ border-radius: 4px;
564
+ font-size: 0.75rem;
565
+ font-weight: 600;
566
+ `
567
+
568
+ // ============================================
569
+ // Photo Menu
570
+ // ============================================
571
+
572
+ export const PhotoSection = styled.section`
573
+ padding: 6rem 0;
574
+ background: #ffffff;
575
+ display: flex;
576
+ flex-direction: column;
577
+ align-items: center;
578
+ width: 100%;
579
+ `
580
+
581
+ export const PhotoGrid = styled.ul`
582
+ display: grid;
583
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
584
+ gap: 2rem;
585
+ list-style: none;
586
+ padding: 0;
587
+ margin: 0 auto;
588
+ max-width: 1200px;
589
+
590
+ @media (max-width: 768px) {
591
+ grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
592
+ gap: 1.5rem;
593
+ }
594
+
595
+ @media (max-width: 480px) {
596
+ grid-template-columns: 1fr;
597
+ gap: 1rem;
598
+ }
599
+ `
600
+
601
+ export const PhotoCard = styled.li`
602
+ background: #ffffff;
603
+ border-radius: 12px;
604
+ overflow: hidden;
605
+ border: 1px solid rgba(0, 0, 0, 0.08);
606
+ transition: all 0.3s ease;
607
+ cursor: pointer;
608
+
609
+ &:hover {
610
+ transform: translateY(-8px);
611
+ box-shadow: 0 16px 32px rgba(0, 0, 0, 0.15);
612
+ }
613
+ `
614
+
615
+ export const PhotoImageWrapper = styled.div`
616
+ width: 100%;
617
+ height: 180px;
618
+ overflow: hidden;
619
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
620
+ position: relative;
621
+ `
622
+
623
+ export const PhotoImage = styled.img`
624
+ width: 100%;
625
+ height: 100%;
626
+ object-fit: cover;
627
+ transition: transform 0.5s ease;
628
+
629
+ ${PhotoCard}:hover & {
630
+ transform: scale(1.1);
631
+ }
632
+ `
633
+
634
+ export const PhotoName = styled.div`
635
+ padding: 1rem;
636
+ text-align: center;
637
+ background: #ffffff;
638
+ `
639
+
640
+ export const PhotoTitle = styled.h3`
641
+ font-size: 1.1rem;
642
+ font-weight: 600;
643
+ color: #2d3748;
644
+ margin-bottom: 0.5rem;
645
+ `
646
+
647
+ export const PhotoCount = styled.span`
648
+ display: inline-block;
649
+ background: #667eea;
650
+ color: #ffffff;
651
+ padding: 0.25rem 0.75rem;
652
+ border-radius: 12px;
653
+ font-size: 0.8rem;
654
+ font-weight: 500;
655
+ `
656
+
657
+ // ============================================
658
+ // 加载状态
659
+ // ============================================
660
+
661
+ const spin = keyframes`
662
+ 0% { transform: rotate(0deg); }
663
+ 100% { transform: rotate(360deg); }
664
+ `
665
+
666
+ export const LoadingContainer = styled.div`
667
+ display: flex;
668
+ justify-content: center;
669
+ align-items: center;
670
+ min-height: 200px;
671
+ `
672
+
673
+ export const LoadingSpinner = styled.div`
674
+ width: 48px;
675
+ height: 48px;
676
+ border: 4px solid rgba(102, 126, 234, 0.15);
677
+ border-top-color: #667eea;
678
+ border-radius: 50%;
679
+ animation: ${spin} 1s linear infinite;
680
+ `
681
+
682
+ export const LoadingText = styled.span`
683
+ margin-left: 1rem;
684
+ color: #6b7280;
685
+ font-size: 1rem;
686
+ `
687
+
688
+ export const ErrorContainer = styled.div`
689
+ text-align: center;
690
+ padding: 3rem;
691
+ background: rgba(239, 68, 68, 0.1);
692
+ border-radius: 12px;
693
+ max-width: 400px;
694
+ margin: 2rem auto;
695
+ `
696
+
697
+ export const ErrorTitle = styled.h3`
698
+ color: #ef4444;
699
+ font-size: 1.25rem;
700
+ margin-bottom: 0.5rem;
701
+ `
702
+
703
+ export const ErrorMessage = styled.p`
704
+ color: #6b7280;
705
+ font-size: 1rem;
706
+ line-height: 1.6;
707
+ `
708
+
709
+ // ============================================
710
+ // 快速开始
711
+ // ============================================
712
+
713
+ export const QuickStartSection = styled.section`
714
+ padding: 6rem 0;
715
+ background: #ffffff;
716
+ display: flex;
717
+ flex-direction: column;
718
+ align-items: center;
719
+ width: 100%;
720
+ `
721
+
722
+ export const QuickStartGrid = styled.div`
723
+ display: grid;
724
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
725
+ gap: 2rem;
726
+ max-width: 1200px;
727
+ margin: 0 auto;
728
+
729
+ @media (max-width: 768px) {
730
+ grid-template-columns: 1fr;
731
+ gap: 1.5rem;
732
+ }
733
+ `
734
+
735
+ export const QuickStartCard = styled.div`
736
+ background: #f8f9fa;
737
+ border: 1px solid rgba(0, 0, 0, 0.06);
738
+ border-radius: 12px;
739
+ padding: 2rem;
740
+ overflow: hidden;
741
+ max-width: 100%;
742
+ `
743
+
744
+ export const QuickStartTitle = styled.h3`
745
+ font-size: 1.25rem;
746
+ font-weight: 600;
747
+ color: #2d3748;
748
+ margin-bottom: 1rem;
749
+ display: flex;
750
+ align-items: center;
751
+ gap: 0.5rem;
752
+ `
753
+
754
+ export const QuickStartCode = styled.pre`
755
+ background: #1e293b;
756
+ color: #f8f8fa;
757
+ padding: 1rem;
758
+ border-radius: 8px;
759
+ overflow-x: auto;
760
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, monospace;
761
+ font-size: 0.85rem;
762
+ line-height: 1.6;
763
+ max-width: 100%;
764
+ word-wrap: break-word;
765
+ white-space: pre-wrap;
766
+
767
+ @media (max-width: 768px) {
768
+ overflow-x: hidden;
769
+ }
770
+ `
771
+
772
+ export const QuickStartDescription = styled.p`
773
+ font-size: 0.95rem;
774
+ color: #6b7280;
775
+ line-height: 1.6;
776
+ margin-top: 1rem;
777
+ `
778
+
779
+ // ============================================
780
+ // Footer
781
+ // ============================================
782
+
783
+ export const Footer = styled.footer`
784
+ text-align: center;
785
+ padding: 2rem 0;
786
+ background: rgba(255, 255, 255, 0.03);
787
+ color: #6b7280;
788
+ font-size: 0.9rem;
789
+ `