reroute-js 0.6.0 → 0.7.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 (114) hide show
  1. package/README.md +1 -0
  2. package/_/basic/package.json +4 -4
  3. package/_/basic/src/client/index.html +1 -1
  4. package/_/basic/src/index.ts +1 -3
  5. package/_/blog/package.json +4 -4
  6. package/_/blog/src/client/App.tsx +1 -0
  7. package/_/blog/src/client/index.html +1 -1
  8. package/_/blog/src/client/routes/blog/content/1-hello-world.tsx +1 -1
  9. package/_/blog/src/client/routes/blog/content/3-markdown-with-syntax-highlighting.mdx +213 -0
  10. package/_/blog/src/client/routes/blog/content/4-content-in-markdown.md +143 -0
  11. package/_/blog/src/client/routes/blog/content/5-mdx-with-components.mdx +267 -0
  12. package/_/blog/src/client/routes/blog/index.tsx +59 -0
  13. package/_/blog/src/client/routes/client.tsx +564 -0
  14. package/_/blog/src/client/routes/docs.tsx +670 -0
  15. package/_/blog/src/client/routes/index.tsx +37 -0
  16. package/_/blog/src/client/routes/markdown-demo.md +169 -0
  17. package/_/blog/src/client/routes/markdown.tsx +160 -0
  18. package/_/blog/src/index.ts +5 -2
  19. package/_/store/package.json +4 -4
  20. package/_/store/src/client/index.html +1 -1
  21. package/_/store/src/index.ts +1 -1
  22. package/cli/bin.d.ts +1 -1
  23. package/cli/bin.js +806 -14
  24. package/cli/bin.js.map +11 -7
  25. package/cli/index.d.ts +1 -1
  26. package/cli/index.js +52 -3
  27. package/cli/index.js.map +4 -3
  28. package/cli/src/cli.d.ts +1 -1
  29. package/cli/src/commands/boot.d.ts +1 -1
  30. package/cli/src/commands/build.d.ts +19 -0
  31. package/cli/src/commands/build.d.ts.map +1 -0
  32. package/cli/src/commands/dev.d.ts +18 -0
  33. package/cli/src/commands/dev.d.ts.map +1 -0
  34. package/cli/src/commands/gen.d.ts +1 -1
  35. package/cli/src/commands/init.d.ts +1 -1
  36. package/cli/src/commands/start.d.ts +19 -0
  37. package/cli/src/commands/start.d.ts.map +1 -0
  38. package/cli/src/libs/index.d.ts +2 -1
  39. package/cli/src/libs/index.d.ts.map +1 -1
  40. package/cli/src/libs/log.d.ts +46 -0
  41. package/cli/src/libs/log.d.ts.map +1 -0
  42. package/cli/src/libs/markdown-processor.d.ts +1 -1
  43. package/cli/src/libs/markdown.d.ts +1 -1
  44. package/cli/src/libs/tailwind.d.ts +1 -1
  45. package/cli/src/libs/version.d.ts +1 -1
  46. package/core/index.d.ts +1 -1
  47. package/core/index.js +5 -5
  48. package/core/index.js.map +3 -3
  49. package/core/src/bundler/hash.d.ts +1 -1
  50. package/core/src/bundler/index.d.ts +1 -1
  51. package/core/src/bundler/transpile.d.ts +1 -1
  52. package/core/src/content/discovery.d.ts +1 -1
  53. package/core/src/content/index.d.ts +1 -1
  54. package/core/src/content/metadata.d.ts +1 -1
  55. package/core/src/content/registry.d.ts +1 -1
  56. package/core/src/index.d.ts +1 -1
  57. package/core/src/ssr/data.d.ts +1 -1
  58. package/core/src/ssr/index.d.ts +1 -1
  59. package/core/src/ssr/modules.d.ts +1 -1
  60. package/core/src/ssr/render.d.ts +1 -1
  61. package/core/src/ssr/seed.d.ts +1 -1
  62. package/core/src/template/html.d.ts +1 -1
  63. package/core/src/template/index.d.ts +1 -1
  64. package/core/src/types.d.ts +1 -1
  65. package/core/src/utils/cache.d.ts +1 -1
  66. package/core/src/utils/compression.d.ts +1 -1
  67. package/core/src/utils/index.d.ts +1 -1
  68. package/core/src/utils/mime.d.ts +1 -1
  69. package/core/src/utils/path.d.ts +1 -1
  70. package/elysia/index.d.ts +1 -1
  71. package/elysia/index.js +5 -5
  72. package/elysia/index.js.map +3 -3
  73. package/elysia/src/index.d.ts +1 -1
  74. package/elysia/src/libs/http.d.ts +1 -1
  75. package/elysia/src/libs/image.d.ts +1 -1
  76. package/elysia/src/plugin.d.ts +1 -1
  77. package/elysia/src/routes/artifacts.d.ts +1 -1
  78. package/elysia/src/routes/content.d.ts +1 -1
  79. package/elysia/src/routes/dev.d.ts +1 -1
  80. package/elysia/src/routes/image.d.ts +1 -1
  81. package/elysia/src/routes/ssr.d.ts +1 -1
  82. package/elysia/src/routes/static.d.ts +1 -1
  83. package/elysia/src/types.d.ts +1 -1
  84. package/package.json +6 -1
  85. package/react/index.d.ts +1 -1
  86. package/react/index.js +50 -24
  87. package/react/index.js.map +3 -3
  88. package/react/src/components/ClientOnly.d.ts +1 -1
  89. package/react/src/components/ContentRoute.d.ts +1 -1
  90. package/react/src/components/Image.d.ts +1 -1
  91. package/react/src/components/Link.d.ts +1 -1
  92. package/react/src/components/Markdown.d.ts +1 -1
  93. package/react/src/components/Outlet.d.ts +1 -1
  94. package/react/src/components/index.d.ts +1 -1
  95. package/react/src/hooks/index.d.ts +1 -1
  96. package/react/src/hooks/useContent.d.ts +1 -1
  97. package/react/src/hooks/useData.d.ts +1 -1
  98. package/react/src/hooks/useNavigate.d.ts +1 -1
  99. package/react/src/hooks/useParams.d.ts +1 -1
  100. package/react/src/hooks/useRouter.d.ts +1 -1
  101. package/react/src/hooks/useSearchParams.d.ts +1 -1
  102. package/react/src/index.d.ts +1 -1
  103. package/react/src/providers/ContentProvider.d.ts +1 -1
  104. package/react/src/providers/RerouteProvider.d.ts +1 -1
  105. package/react/src/providers/RouterProvider.d.ts +1 -1
  106. package/react/src/providers/RouterProvider.d.ts.map +1 -1
  107. package/react/src/providers/index.d.ts +1 -1
  108. package/react/src/types/any.d.ts +1 -1
  109. package/react/src/types/index.d.ts +1 -1
  110. package/react/src/types/router.d.ts +1 -1
  111. package/react/src/utils/content.d.ts +1 -1
  112. package/react/src/utils/head.d.ts +1 -1
  113. package/react/src/utils/index.d.ts +1 -1
  114. package/_/blog/src/client/components/Counter.tsx +0 -14
@@ -0,0 +1,564 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { ClientOnly, Link } from 'reroute-js/react';
3
+
4
+ const meta = {
5
+ title: 'ClientOnly Component Demo',
6
+ description: 'Examples of using ClientOnly to prevent hydration mismatches',
7
+ };
8
+
9
+ export default function ClientDemoPage() {
10
+ return (
11
+ <div style={{ minHeight: '100vh', background: '#f9f9f9' }}>
12
+ {/* Header */}
13
+ <header
14
+ style={{
15
+ background: '#fff',
16
+ borderBottom: '1px solid #e0e0e0',
17
+ padding: '1rem 2rem',
18
+ position: 'sticky',
19
+ top: 0,
20
+ zIndex: 100,
21
+ boxShadow: '0 2px 4px rgba(0,0,0,0.05)',
22
+ }}
23
+ >
24
+ <div style={{ maxWidth: '1200px', margin: '0 auto' }}>
25
+ <Link
26
+ to='/'
27
+ style={{
28
+ fontSize: '1.5rem',
29
+ fontWeight: 'bold',
30
+ color: '#333',
31
+ textDecoration: 'none',
32
+ }}
33
+ >
34
+ ← Back to Home
35
+ </Link>
36
+ </div>
37
+ </header>
38
+
39
+ {/* Main Content */}
40
+ <main style={{ maxWidth: '1200px', margin: '0 auto', padding: '2rem' }}>
41
+ <h1 style={{ fontSize: '2.5rem', marginBottom: '1rem' }}>
42
+ 🎯 ClientOnly Component Demo
43
+ </h1>
44
+ <p style={{ fontSize: '1.1rem', color: '#666', marginBottom: '3rem' }}>
45
+ This page demonstrates various use cases for the{' '}
46
+ <code>ClientOnly</code> component, which prevents hydration mismatches
47
+ by only rendering content on the client-side.
48
+ </p>
49
+
50
+ {/* Example 1: Current Time */}
51
+ <Section
52
+ title='Example 1: Dynamic Time Display'
53
+ description='Without ClientOnly, the server-rendered time would differ from the client, causing a hydration mismatch.'
54
+ >
55
+ <div style={{ display: 'flex', gap: '2rem', flexWrap: 'wrap' }}>
56
+ <ExampleCard title='❌ Wrong (Hydration Error)'>
57
+ <div
58
+ style={{
59
+ padding: '1rem',
60
+ background: '#fee',
61
+ border: '2px solid #fcc',
62
+ borderRadius: '4px',
63
+ }}
64
+ >
65
+ <p style={{ margin: 0 }}>
66
+ <strong>Time:</strong>
67
+ {/*comment the following line to stop the mismatch error */}
68
+ {new Date().toLocaleTimeString()}
69
+ </p>
70
+ <p
71
+ style={{
72
+ margin: '0.5rem 0 0 0',
73
+ fontSize: '0.875rem',
74
+ color: '#c33',
75
+ }}
76
+ >
77
+ ⚠️ This will cause hydration mismatch! throttle your network
78
+ connection to see.
79
+ </p>
80
+ </div>
81
+ </ExampleCard>
82
+
83
+ <ExampleCard title='✅ Correct (With ClientOnly)'>
84
+ <ClientOnly
85
+ fallback={
86
+ <div
87
+ style={{
88
+ padding: '1rem',
89
+ background: '#f0f0f0',
90
+ borderRadius: '4px',
91
+ }}
92
+ >
93
+ Loading time...
94
+ </div>
95
+ }
96
+ >
97
+ <div
98
+ style={{
99
+ padding: '1rem',
100
+ background: '#efe',
101
+ border: '2px solid #cfc',
102
+ borderRadius: '4px',
103
+ }}
104
+ >
105
+ <p style={{ margin: 0 }}>
106
+ <strong>Time:</strong> {new Date().toLocaleTimeString()}
107
+ </p>
108
+ <p
109
+ style={{
110
+ margin: '0.5rem 0 0 0',
111
+ fontSize: '0.875rem',
112
+ color: '#393',
113
+ }}
114
+ >
115
+ ✓ No hydration issues!
116
+ </p>
117
+ </div>
118
+ </ClientOnly>
119
+ </ExampleCard>
120
+ </div>
121
+ </Section>
122
+
123
+ {/* Example 2: Browser API */}
124
+ <Section
125
+ title='Example 2: Browser API Access'
126
+ description='Accessing window, navigator, or other browser APIs safely.'
127
+ >
128
+ <ClientOnly fallback={<LoadingCard message='Detecting browser...' />}>
129
+ <BrowserInfoCard />
130
+ </ClientOnly>
131
+ </Section>
132
+
133
+ {/* Example 3: LocalStorage */}
134
+ <Section
135
+ title='Example 3: LocalStorage Integration'
136
+ description='Reading from localStorage without causing SSR issues.'
137
+ >
138
+ <ClientOnly
139
+ fallback={<LoadingCard message='Loading preferences...' />}
140
+ >
141
+ <LocalStorageDemo />
142
+ </ClientOnly>
143
+ </Section>
144
+
145
+ {/* Example 4: Random Values */}
146
+ <Section
147
+ title='Example 4: Random Values'
148
+ description='Generating random values that would differ between server and client.'
149
+ >
150
+ <ClientOnly>
151
+ <RandomContentCard />
152
+ </ClientOnly>
153
+ </Section>
154
+
155
+ {/* Example 5: Live Counter */}
156
+ <Section
157
+ title='Example 5: Live Counter'
158
+ description='A component that updates in real-time and uses Date.now().'
159
+ >
160
+ <ClientOnly
161
+ fallback={
162
+ <div
163
+ style={{
164
+ padding: '2rem',
165
+ background: '#fff',
166
+ border: '1px solid #e0e0e0',
167
+ borderRadius: '8px',
168
+ textAlign: 'center',
169
+ }}
170
+ >
171
+ <p style={{ fontSize: '1.25rem', color: '#666' }}>
172
+ ⏳ Initializing counter...
173
+ </p>
174
+ </div>
175
+ }
176
+ >
177
+ <LiveCounter />
178
+ </ClientOnly>
179
+ </Section>
180
+
181
+ {/* Code Examples */}
182
+ <Section
183
+ title='📝 Code Examples'
184
+ description='How to use ClientOnly in your components.'
185
+ >
186
+ <div
187
+ style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}
188
+ >
189
+ <CodeExample
190
+ title='Basic Usage'
191
+ code={`import { ClientOnly } from 'reroute-js/react';
192
+
193
+ function MyComponent() {
194
+ return (
195
+ <ClientOnly>
196
+ <div>{new Date().toLocaleTimeString()}</div>
197
+ </ClientOnly>
198
+ );
199
+ }`}
200
+ />
201
+
202
+ <CodeExample
203
+ title='With Fallback'
204
+ code={`<ClientOnly fallback={<div>Loading...</div>}>
205
+ <ComponentUsingBrowserAPI />
206
+ </ClientOnly>`}
207
+ />
208
+
209
+ <CodeExample
210
+ title='LocalStorage Example'
211
+ code={`<ClientOnly fallback={<p>Loading preferences...</p>}>
212
+ <UserPreferences
213
+ theme={localStorage.getItem('theme')}
214
+ />
215
+ </ClientOnly>`}
216
+ />
217
+ </div>
218
+ </Section>
219
+
220
+ {/* Best Practices */}
221
+ <div
222
+ style={{
223
+ marginTop: '3rem',
224
+ padding: '2rem',
225
+ background: '#fff3cd',
226
+ border: '2px solid #ffc107',
227
+ borderRadius: '8px',
228
+ }}
229
+ >
230
+ <h3 style={{ marginTop: 0 }}>💡 Best Practices</h3>
231
+ <ul style={{ lineHeight: '1.8' }}>
232
+ <li>
233
+ Always provide a <code>fallback</code> for better user experience
234
+ </li>
235
+ <li>Keep the fallback similar in size to avoid layout shifts</li>
236
+ <li>
237
+ Use <code>ClientOnly</code> only when necessary to maximize SSR
238
+ benefits
239
+ </li>
240
+ <li>
241
+ Avoid wrapping your entire app - be specific about what needs
242
+ client-only rendering
243
+ </li>
244
+ </ul>
245
+ </div>
246
+
247
+ {/* Navigation */}
248
+ <div
249
+ style={{
250
+ marginTop: '3rem',
251
+ padding: '2rem',
252
+ background: '#fff',
253
+ border: '1px solid #e0e0e0',
254
+ borderRadius: '8px',
255
+ textAlign: 'center',
256
+ }}
257
+ >
258
+ <div
259
+ style={{
260
+ display: 'flex',
261
+ gap: '1rem',
262
+ justifyContent: 'center',
263
+ flexWrap: 'wrap',
264
+ }}
265
+ >
266
+ <Link
267
+ to='/'
268
+ style={{
269
+ padding: '0.75rem 1.5rem',
270
+ background: '#666',
271
+ color: '#fff',
272
+ textDecoration: 'none',
273
+ borderRadius: '4px',
274
+ }}
275
+ >
276
+ ← Back to Home
277
+ </Link>
278
+ <Link
279
+ to='/docs'
280
+ style={{
281
+ padding: '0.75rem 1.5rem',
282
+ background: '#0066cc',
283
+ color: '#fff',
284
+ textDecoration: 'none',
285
+ borderRadius: '4px',
286
+ }}
287
+ >
288
+ Fragment Demo →
289
+ </Link>
290
+ </div>
291
+ </div>
292
+ </main>
293
+ </div>
294
+ );
295
+ }
296
+
297
+ // Helper Components
298
+
299
+ function Section({
300
+ title,
301
+ description,
302
+ children,
303
+ }: {
304
+ title: string;
305
+ description: string;
306
+ children: React.ReactNode;
307
+ }) {
308
+ return (
309
+ <section
310
+ style={{
311
+ background: '#fff',
312
+ border: '1px solid #e0e0e0',
313
+ borderRadius: '8px',
314
+ padding: '2rem',
315
+ marginBottom: '2rem',
316
+ }}
317
+ >
318
+ <h2 style={{ marginTop: 0, marginBottom: '0.5rem' }}>{title}</h2>
319
+ <p style={{ color: '#666', marginBottom: '1.5rem' }}>{description}</p>
320
+ {children}
321
+ </section>
322
+ );
323
+ }
324
+
325
+ function ExampleCard({
326
+ title,
327
+ children,
328
+ }: {
329
+ title: string;
330
+ children: React.ReactNode;
331
+ }) {
332
+ return (
333
+ <div style={{ flex: '1 1 300px' }}>
334
+ <h4 style={{ marginTop: 0, marginBottom: '1rem' }}>{title}</h4>
335
+ {children}
336
+ </div>
337
+ );
338
+ }
339
+
340
+ function LoadingCard({ message }: { message: string }) {
341
+ return (
342
+ <div
343
+ style={{
344
+ padding: '2rem',
345
+ background: '#f8f9fa',
346
+ border: '1px solid #dee2e6',
347
+ borderRadius: '8px',
348
+ textAlign: 'center',
349
+ }}
350
+ >
351
+ <p style={{ margin: 0, color: '#6c757d' }}>⏳ {message}</p>
352
+ </div>
353
+ );
354
+ }
355
+
356
+ function BrowserInfoCard() {
357
+ const [info, setInfo] = useState({
358
+ userAgent: '',
359
+ language: '',
360
+ platform: '',
361
+ screenSize: '',
362
+ });
363
+
364
+ useEffect(() => {
365
+ setInfo({
366
+ userAgent: navigator.userAgent,
367
+ language: navigator.language,
368
+ platform: navigator.platform,
369
+ screenSize: `${window.innerWidth} x ${window.innerHeight}`,
370
+ });
371
+ }, []);
372
+
373
+ return (
374
+ <div
375
+ style={{
376
+ padding: '1.5rem',
377
+ background: '#fff',
378
+ border: '1px solid #e0e0e0',
379
+ borderRadius: '8px',
380
+ }}
381
+ >
382
+ <h4 style={{ marginTop: 0, marginBottom: '1rem' }}>🌐 Browser Info</h4>
383
+ <dl style={{ margin: 0 }}>
384
+ <dt style={{ fontWeight: 'bold', marginTop: '0.5rem' }}>Platform:</dt>
385
+ <dd style={{ margin: '0 0 0.5rem 1rem', color: '#666' }}>
386
+ {info.platform}
387
+ </dd>
388
+
389
+ <dt style={{ fontWeight: 'bold', marginTop: '0.5rem' }}>Language:</dt>
390
+ <dd style={{ margin: '0 0 0.5rem 1rem', color: '#666' }}>
391
+ {info.language}
392
+ </dd>
393
+
394
+ <dt style={{ fontWeight: 'bold', marginTop: '0.5rem' }}>
395
+ Screen Size:
396
+ </dt>
397
+ <dd style={{ margin: '0 0 0.5rem 1rem', color: '#666' }}>
398
+ {info.screenSize}
399
+ </dd>
400
+
401
+ <dt style={{ fontWeight: 'bold', marginTop: '0.5rem' }}>User Agent:</dt>
402
+ <dd
403
+ style={{
404
+ margin: '0 0 0.5rem 1rem',
405
+ color: '#666',
406
+ fontSize: '0.875rem',
407
+ wordBreak: 'break-word',
408
+ }}
409
+ >
410
+ {info.userAgent}
411
+ </dd>
412
+ </dl>
413
+ </div>
414
+ );
415
+ }
416
+
417
+ function LocalStorageDemo() {
418
+ const [visits, setVisits] = useState(0);
419
+
420
+ useEffect(() => {
421
+ const count = Number.parseInt(localStorage.getItem('visits') || '0', 10);
422
+ const newCount = count + 1;
423
+ localStorage.setItem('visits', String(newCount));
424
+ setVisits(newCount);
425
+ }, []);
426
+
427
+ const handleReset = () => {
428
+ localStorage.setItem('visits', '0');
429
+ setVisits(0);
430
+ };
431
+
432
+ return (
433
+ <div
434
+ style={{
435
+ padding: '1.5rem',
436
+ background: '#fff',
437
+ border: '1px solid #e0e0e0',
438
+ borderRadius: '8px',
439
+ }}
440
+ >
441
+ <h4 style={{ marginTop: 0, marginBottom: '1rem' }}>💾 Visit Counter</h4>
442
+ <p style={{ fontSize: '1.5rem', margin: '1rem 0' }}>
443
+ You've visited this page <strong>{visits}</strong> time
444
+ {visits !== 1 ? 's' : ''}
445
+ </p>
446
+ <button
447
+ type='button'
448
+ onClick={handleReset}
449
+ style={{
450
+ padding: '0.5rem 1rem',
451
+ background: '#dc3545',
452
+ color: '#fff',
453
+ border: 'none',
454
+ borderRadius: '4px',
455
+ cursor: 'pointer',
456
+ }}
457
+ >
458
+ Reset Counter
459
+ </button>
460
+ <p style={{ fontSize: '0.875rem', color: '#666', marginTop: '1rem' }}>
461
+ This data is stored in localStorage and persists across page reloads.
462
+ </p>
463
+ </div>
464
+ );
465
+ }
466
+
467
+ function RandomContentCard() {
468
+ const randomColor = `hsl(${Math.random() * 360}, 70%, 80%)`;
469
+ const randomNumber = Math.floor(Math.random() * 100);
470
+ const randomEmoji = ['🎲', '🎰', '🎯', '🎪', '🎨', '🎭', '🎪'][
471
+ Math.floor(Math.random() * 7)
472
+ ];
473
+
474
+ return (
475
+ <div
476
+ style={{
477
+ padding: '1.5rem',
478
+ background: randomColor,
479
+ border: '2px solid #333',
480
+ borderRadius: '8px',
481
+ }}
482
+ >
483
+ <h4 style={{ marginTop: 0, marginBottom: '1rem' }}>
484
+ {randomEmoji} Random Content
485
+ </h4>
486
+ <p style={{ fontSize: '1.25rem', margin: '1rem 0' }}>
487
+ Your lucky number: <strong>{randomNumber}</strong>
488
+ </p>
489
+ <p style={{ fontSize: '0.875rem', color: '#333', marginBottom: 0 }}>
490
+ This content is randomly generated on every client render. Without
491
+ ClientOnly, the server and client would generate different values,
492
+ causing a hydration error.
493
+ </p>
494
+ </div>
495
+ );
496
+ }
497
+
498
+ function LiveCounter() {
499
+ const [count, setCount] = useState(0);
500
+ const [startTime] = useState(() => Date.now());
501
+
502
+ useEffect(() => {
503
+ const interval = setInterval(() => {
504
+ setCount((c) => c + 1);
505
+ }, 1000);
506
+
507
+ return () => clearInterval(interval);
508
+ }, []);
509
+
510
+ const elapsed = Math.floor((Date.now() - startTime) / 1000);
511
+
512
+ return (
513
+ <div
514
+ style={{
515
+ padding: '2rem',
516
+ background: '#fff',
517
+ border: '1px solid #e0e0e0',
518
+ borderRadius: '8px',
519
+ textAlign: 'center',
520
+ }}
521
+ >
522
+ <h4 style={{ marginTop: 0, marginBottom: '1rem' }}>⏱️ Live Counter</h4>
523
+ <div
524
+ style={{
525
+ fontSize: '3rem',
526
+ fontWeight: 'bold',
527
+ color: '#0066cc',
528
+ margin: '1rem 0',
529
+ }}
530
+ >
531
+ {count}
532
+ </div>
533
+ <p style={{ color: '#666' }}>
534
+ Running for {elapsed} second{elapsed !== 1 ? 's' : ''}
535
+ </p>
536
+ <p style={{ fontSize: '0.875rem', color: '#999', marginTop: '1rem' }}>
537
+ This counter updates every second using setInterval and uses Date.now()
538
+ for timing.
539
+ </p>
540
+ </div>
541
+ );
542
+ }
543
+
544
+ function CodeExample({ title, code }: { title: string; code: string }) {
545
+ return (
546
+ <div>
547
+ <h4 style={{ marginBottom: '0.5rem' }}>{title}</h4>
548
+ <pre
549
+ style={{
550
+ background: '#f5f5f5',
551
+ padding: '1rem',
552
+ borderRadius: '4px',
553
+ overflow: 'auto',
554
+ fontSize: '0.875rem',
555
+ margin: 0,
556
+ }}
557
+ >
558
+ <code>{code}</code>
559
+ </pre>
560
+ </div>
561
+ );
562
+ }
563
+
564
+ export { meta };