@veluai/velu 0.2.25 → 0.2.28

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 +80 -80
  2. package/dist/cli.js +44 -44
  3. package/package.json +64 -64
  4. package/runtime/velu-ui/base.css +320 -320
  5. package/runtime/velu-ui/components/Accordion.jsx +64 -64
  6. package/runtime/velu-ui/components/ApiClient.jsx +207 -207
  7. package/runtime/velu-ui/components/ApiField.jsx +87 -87
  8. package/runtime/velu-ui/components/ApiPath.jsx +63 -63
  9. package/runtime/velu-ui/components/ApiReferencePage.jsx +384 -384
  10. package/runtime/velu-ui/components/ApiSamples.jsx +36 -36
  11. package/runtime/velu-ui/components/ApiSidebar.jsx +125 -125
  12. package/runtime/velu-ui/components/AskBar.jsx +71 -71
  13. package/runtime/velu-ui/components/Callout.jsx +114 -114
  14. package/runtime/velu-ui/components/Card.jsx +131 -131
  15. package/runtime/velu-ui/components/ChangelogFilters.jsx +75 -75
  16. package/runtime/velu-ui/components/Chatbot.jsx +972 -972
  17. package/runtime/velu-ui/components/CodeBlock.jsx +375 -375
  18. package/runtime/velu-ui/components/Columns.jsx +56 -56
  19. package/runtime/velu-ui/components/ContextMenu.jsx +327 -327
  20. package/runtime/velu-ui/components/ErrorCard.jsx +138 -138
  21. package/runtime/velu-ui/components/Field.jsx +81 -81
  22. package/runtime/velu-ui/components/Image.jsx +163 -163
  23. package/runtime/velu-ui/components/Logo.jsx +31 -31
  24. package/runtime/velu-ui/components/MethodBadge.jsx +31 -31
  25. package/runtime/velu-ui/components/NavSelect.jsx +108 -108
  26. package/runtime/velu-ui/components/NotFound.jsx +63 -63
  27. package/runtime/velu-ui/components/PageFeedback.jsx +219 -219
  28. package/runtime/velu-ui/components/PageFooter.jsx +145 -145
  29. package/runtime/velu-ui/components/PageHeader.jsx +422 -422
  30. package/runtime/velu-ui/components/PageNav.jsx +77 -77
  31. package/runtime/velu-ui/components/PoweredBy.jsx +51 -51
  32. package/runtime/velu-ui/components/Prompt.jsx +115 -115
  33. package/runtime/velu-ui/components/Search.jsx +460 -460
  34. package/runtime/velu-ui/components/Sidebar.jsx +246 -246
  35. package/runtime/velu-ui/components/SocialLinks.jsx +90 -90
  36. package/runtime/velu-ui/components/Steps.jsx +65 -65
  37. package/runtime/velu-ui/components/ThemeToggle.jsx +48 -48
  38. package/runtime/velu-ui/components/Toc.jsx +537 -537
  39. package/runtime/velu-ui/components/TocBar.jsx +195 -195
  40. package/runtime/velu-ui/components/Tree.jsx +87 -87
  41. package/runtime/velu-ui/components/TryItBar.jsx +102 -102
  42. package/runtime/velu-ui/components/Update.jsx +92 -92
  43. package/runtime/velu-ui/components/accordion.css +92 -92
  44. package/runtime/velu-ui/components/api-page.css +208 -208
  45. package/runtime/velu-ui/components/api.css +635 -635
  46. package/runtime/velu-ui/components/ask-bar.css +103 -103
  47. package/runtime/velu-ui/components/card.css +105 -105
  48. package/runtime/velu-ui/components/changelog-filters.css +43 -43
  49. package/runtime/velu-ui/components/chatbot.css +687 -687
  50. package/runtime/velu-ui/components/code-block.css +263 -263
  51. package/runtime/velu-ui/components/context-menu.css +199 -199
  52. package/runtime/velu-ui/components/docs-layout.css +822 -822
  53. package/runtime/velu-ui/components/field.css +82 -82
  54. package/runtime/velu-ui/components/image.css +237 -237
  55. package/runtime/velu-ui/components/nav-select.css +157 -157
  56. package/runtime/velu-ui/components/not-found.css +94 -94
  57. package/runtime/velu-ui/components/page-feedback.css +261 -261
  58. package/runtime/velu-ui/components/page-footer.css +130 -130
  59. package/runtime/velu-ui/components/page-header.css +558 -558
  60. package/runtime/velu-ui/components/page-nav.css +50 -50
  61. package/runtime/velu-ui/components/powered-by.css +92 -92
  62. package/runtime/velu-ui/components/prompt.css +99 -99
  63. package/runtime/velu-ui/components/search.css +307 -307
  64. package/runtime/velu-ui/components/sidebar.css +205 -205
  65. package/runtime/velu-ui/components/steps.css +77 -77
  66. package/runtime/velu-ui/components/theme-toggle.css +102 -102
  67. package/runtime/velu-ui/components/toc-bar.css +234 -234
  68. package/runtime/velu-ui/components/tree.css +49 -49
  69. package/runtime/velu-ui/components/update.css +111 -111
  70. package/runtime/velu-ui/index.js +58 -58
  71. package/runtime/velu-ui/lib/api-send.js +118 -118
  72. package/runtime/velu-ui/lib/brand-icons.jsx +103 -103
  73. package/runtime/velu-ui/lib/component-schemas.js +100 -100
  74. package/runtime/velu-ui/lib/copyText.js +64 -64
  75. package/runtime/velu-ui/lib/docs-assistant.js +273 -273
  76. package/runtime/velu-ui/lib/lang-icons.jsx +147 -147
  77. package/runtime/velu-ui/lib/page-feedback.js +72 -72
  78. package/runtime/velu-ui/lib/pagefind.js +116 -116
  79. package/runtime/velu-ui/lib/prism-langs.js +957 -957
  80. package/runtime/velu-ui/lib/prism-loader.js +74 -74
  81. package/runtime/velu-ui/lib/resolveIcon.jsx +29 -29
  82. package/runtime/velu-ui/lib/scrollIntoNearestView.js +66 -66
  83. package/runtime/velu-ui/mdx-components.jsx +107 -107
  84. package/runtime/velu-ui/primitives/Cluster.jsx +49 -49
  85. package/runtime/velu-ui/primitives/Stack.jsx +63 -63
  86. package/runtime/velu-ui/primitives/Switcher.jsx +57 -57
  87. package/runtime/velu-ui/primitives/stack.css +3 -3
  88. package/runtime/velu-ui/primitives/switcher.css +25 -25
  89. package/runtime/velu-ui/styles.css +48 -48
  90. package/runtime/velu-ui/tokens.css +4 -4
  91. package/schema/velu.schema.json +419 -419
  92. package/src/lib/extract-mdx-error.js +170 -170
  93. package/src/lib/issues.js +159 -159
  94. package/src/lib/known-components.js +35 -35
  95. package/src/navigation.js +459 -459
  96. package/src/runtime/App.jsx +1754 -1754
  97. package/src/runtime/ErrorBoundary.jsx +54 -54
  98. package/src/runtime/client-entry.jsx +27 -27
  99. package/src/runtime/server-entry.jsx +24 -24
  100. package/src/template.html +48 -48
  101. package/templates/starter/ai-tools/claude-code.mdx +26 -26
  102. package/templates/starter/ai-tools/cursor.mdx +17 -17
  103. package/templates/starter/api-reference/introduction.mdx +43 -43
  104. package/templates/starter/development.mdx +19 -19
  105. package/templates/starter/essentials/code.mdx +29 -29
  106. package/templates/starter/essentials/images.mdx +29 -29
  107. package/templates/starter/essentials/markdown.mdx +25 -25
  108. package/templates/starter/essentials/navigation.mdx +39 -39
  109. package/templates/starter/essentials/settings.mdx +30 -30
  110. package/templates/starter/favicon.svg +6 -6
  111. package/templates/starter/index.mdx +31 -31
  112. package/templates/starter/openapi.json +160 -160
  113. package/templates/starter/quickstart.mdx +31 -31
  114. package/templates/starter/velu.json +40 -40
@@ -1,90 +1,90 @@
1
- import React from 'react';
2
- import Cluster from '../primitives/Cluster.jsx';
3
-
4
- /**
5
- * SocialLinks — a row of social-platform icon links. Shared by the full
6
- * PageFooter (centered at its base) and the compact content-foot row (next to
7
- * "Powered by Velu" when there are no footer link columns).
8
- *
9
- * `socials` is `[{ kind, href }]`. Unknown kinds (no icon) are skipped.
10
- *
11
- * @param {{ socials?: {kind: string, href: string}[],
12
- * justify?: string, className?: string }} props
13
- */
14
-
15
- /* Inline SVGs, fill: currentColor so they inherit the themed text color. */
16
- function GithubIcon() {
17
- return (
18
- <svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
19
- <path
20
- fillRule="evenodd"
21
- clipRule="evenodd"
22
- d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z"
23
- />
24
- </svg>
25
- );
26
- }
27
- function XIcon() {
28
- return (
29
- <svg viewBox="0 0 1200 1227" fill="currentColor" aria-hidden="true">
30
- <path d="M714.163 519.284 1160.89 0h-105.86L667.137 450.887 357.328 0H0l468.492 681.821L0 1226.37h105.866l409.625-476.152 327.181 476.152H1200L714.137 519.284h.026ZM569.165 687.828l-47.468-67.894-377.686-540.24h162.604l304.797 435.991 47.468 67.894 396.2 566.721H892.476L569.165 687.854v-.026Z" />
31
- </svg>
32
- );
33
- }
34
- function YoutubeIcon() {
35
- return (
36
- <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
37
- <path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" />
38
- </svg>
39
- );
40
- }
41
- function LinkedinIcon() {
42
- return (
43
- <svg viewBox="0 0 256 256" fill="currentColor" aria-hidden="true">
44
- <path d="M218.123 218.127h-37.931v-59.403c0-14.165-.253-32.4-19.728-32.4-19.756 0-22.779 15.434-22.779 31.369v60.43h-37.93V95.967h36.413v16.694h.51a39.907 39.907 0 0 1 35.928-19.733c38.445 0 45.533 25.288 45.533 58.186l-.016 67.013ZM56.955 79.27c-12.157.002-22.014-9.852-22.016-22.009-.002-12.157 9.851-22.014 22.008-22.016 12.157-.003 22.014 9.851 22.016 22.008A22.013 22.013 0 0 1 56.955 79.27m18.966 138.858H37.95V95.967h37.97v122.16ZM237.033.018H18.89C8.58-.098.125 8.161-.001 18.471v219.053c.122 10.315 8.576 18.582 18.89 18.474h218.144c10.336.128 18.823-8.139 18.966-18.474V18.454c-.147-10.33-8.635-18.588-18.966-18.453" />
45
- </svg>
46
- );
47
- }
48
-
49
- export const SOCIAL_ICONS = {
50
- github: GithubIcon,
51
- x: XIcon,
52
- youtube: YoutubeIcon,
53
- linkedin: LinkedinIcon,
54
- };
55
- const SOCIAL_LABELS = {
56
- github: 'GitHub',
57
- x: 'X',
58
- youtube: 'YouTube',
59
- linkedin: 'LinkedIn',
60
- };
61
-
62
- export default function SocialLinks({ socials = [], justify, className = '' }) {
63
- const known = socials.filter((s) => s && SOCIAL_ICONS[s.kind]);
64
- if (!known.length) return null;
65
- return (
66
- <Cluster
67
- space="var(--s-2)"
68
- align="center"
69
- justify={justify}
70
- className={`velu-socials ${className}`.trim()}
71
- >
72
- {known.map((s, i) => {
73
- const Icon = SOCIAL_ICONS[s.kind];
74
- return (
75
- <a
76
- key={i}
77
- href={s.href}
78
- target="_blank"
79
- rel="noreferrer"
80
- aria-label={SOCIAL_LABELS[s.kind] ?? s.kind}
81
- className="velu-social"
82
- data-kind={s.kind}
83
- >
84
- <Icon />
85
- </a>
86
- );
87
- })}
88
- </Cluster>
89
- );
90
- }
1
+ import React from 'react';
2
+ import Cluster from '../primitives/Cluster.jsx';
3
+
4
+ /**
5
+ * SocialLinks — a row of social-platform icon links. Shared by the full
6
+ * PageFooter (centered at its base) and the compact content-foot row (next to
7
+ * "Powered by Velu" when there are no footer link columns).
8
+ *
9
+ * `socials` is `[{ kind, href }]`. Unknown kinds (no icon) are skipped.
10
+ *
11
+ * @param {{ socials?: {kind: string, href: string}[],
12
+ * justify?: string, className?: string }} props
13
+ */
14
+
15
+ /* Inline SVGs, fill: currentColor so they inherit the themed text color. */
16
+ function GithubIcon() {
17
+ return (
18
+ <svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
19
+ <path
20
+ fillRule="evenodd"
21
+ clipRule="evenodd"
22
+ d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z"
23
+ />
24
+ </svg>
25
+ );
26
+ }
27
+ function XIcon() {
28
+ return (
29
+ <svg viewBox="0 0 1200 1227" fill="currentColor" aria-hidden="true">
30
+ <path d="M714.163 519.284 1160.89 0h-105.86L667.137 450.887 357.328 0H0l468.492 681.821L0 1226.37h105.866l409.625-476.152 327.181 476.152H1200L714.137 519.284h.026ZM569.165 687.828l-47.468-67.894-377.686-540.24h162.604l304.797 435.991 47.468 67.894 396.2 566.721H892.476L569.165 687.854v-.026Z" />
31
+ </svg>
32
+ );
33
+ }
34
+ function YoutubeIcon() {
35
+ return (
36
+ <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
37
+ <path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" />
38
+ </svg>
39
+ );
40
+ }
41
+ function LinkedinIcon() {
42
+ return (
43
+ <svg viewBox="0 0 256 256" fill="currentColor" aria-hidden="true">
44
+ <path d="M218.123 218.127h-37.931v-59.403c0-14.165-.253-32.4-19.728-32.4-19.756 0-22.779 15.434-22.779 31.369v60.43h-37.93V95.967h36.413v16.694h.51a39.907 39.907 0 0 1 35.928-19.733c38.445 0 45.533 25.288 45.533 58.186l-.016 67.013ZM56.955 79.27c-12.157.002-22.014-9.852-22.016-22.009-.002-12.157 9.851-22.014 22.008-22.016 12.157-.003 22.014 9.851 22.016 22.008A22.013 22.013 0 0 1 56.955 79.27m18.966 138.858H37.95V95.967h37.97v122.16ZM237.033.018H18.89C8.58-.098.125 8.161-.001 18.471v219.053c.122 10.315 8.576 18.582 18.89 18.474h218.144c10.336.128 18.823-8.139 18.966-18.474V18.454c-.147-10.33-8.635-18.588-18.966-18.453" />
45
+ </svg>
46
+ );
47
+ }
48
+
49
+ export const SOCIAL_ICONS = {
50
+ github: GithubIcon,
51
+ x: XIcon,
52
+ youtube: YoutubeIcon,
53
+ linkedin: LinkedinIcon,
54
+ };
55
+ const SOCIAL_LABELS = {
56
+ github: 'GitHub',
57
+ x: 'X',
58
+ youtube: 'YouTube',
59
+ linkedin: 'LinkedIn',
60
+ };
61
+
62
+ export default function SocialLinks({ socials = [], justify, className = '' }) {
63
+ const known = socials.filter((s) => s && SOCIAL_ICONS[s.kind]);
64
+ if (!known.length) return null;
65
+ return (
66
+ <Cluster
67
+ space="var(--s-2)"
68
+ align="center"
69
+ justify={justify}
70
+ className={`velu-socials ${className}`.trim()}
71
+ >
72
+ {known.map((s, i) => {
73
+ const Icon = SOCIAL_ICONS[s.kind];
74
+ return (
75
+ <a
76
+ key={i}
77
+ href={s.href}
78
+ target="_blank"
79
+ rel="noreferrer"
80
+ aria-label={SOCIAL_LABELS[s.kind] ?? s.kind}
81
+ className="velu-social"
82
+ data-kind={s.kind}
83
+ >
84
+ <Icon />
85
+ </a>
86
+ );
87
+ })}
88
+ </Cluster>
89
+ );
90
+ }
@@ -1,65 +1,65 @@
1
- import React, { Children, cloneElement, isValidElement } from 'react';
2
-
3
- /**
4
- * Steps + Step — numbered procedural steps with a continuous accent rail
5
- * down the left edge.
6
- *
7
- * <Steps>
8
- * <Step title="First Step">These are instructions…</Step>
9
- * <Step title="Second Step">…</Step>
10
- * <Step title="Third Step">…</Step>
11
- * </Steps>
12
- *
13
- * Steps assigns 1-based `index` to each <Step> child via cloneElement —
14
- * callers don't have to number them by hand. A <Step> rendered outside
15
- * <Steps> falls back to whatever `index` it was given (or 1).
16
- *
17
- * Each Step is a 2-column grid:
18
- *
19
- * [circle + line] title
20
- * body
21
- *
22
- * The marker column spans both rows. The vertical rail (line below the
23
- * circle) is `flex: 1`, so it stretches to fill the Step's full height —
24
- * meaning adjacent steps' rails meet end-to-end with no manual height
25
- * math. The last step keeps its rail too (matches the design).
26
- */
27
-
28
- export function Step({
29
- title,
30
- index = 1,
31
- children,
32
- className = '',
33
- ...rest
34
- }) {
35
- return (
36
- <div className={`velu-step ${className}`.trim()} {...rest}>
37
- <div className="velu-step__circle" aria-hidden="true">
38
- {index}
39
- </div>
40
- <div className="velu-step__line" aria-hidden="true" />
41
- {title != null && (
42
- <div className="velu-step__title">{title}</div>
43
- )}
44
- {children != null && children !== false && (
45
- <div className="velu-step__body">{children}</div>
46
- )}
47
- </div>
48
- );
49
- }
50
- Step.displayName = 'Step';
51
-
52
- export default function Steps({ children, className = '', ...rest }) {
53
- let i = 0;
54
- const numbered = Children.map(children, (child) => {
55
- if (!isValidElement(child)) return child;
56
- if (child.type !== Step && child.type?.displayName !== 'Step') return child;
57
- i += 1;
58
- return cloneElement(child, { index: child.props.index ?? i });
59
- });
60
- return (
61
- <div className={`velu-steps ${className}`.trim()} {...rest}>
62
- {numbered}
63
- </div>
64
- );
65
- }
1
+ import React, { Children, cloneElement, isValidElement } from 'react';
2
+
3
+ /**
4
+ * Steps + Step — numbered procedural steps with a continuous accent rail
5
+ * down the left edge.
6
+ *
7
+ * <Steps>
8
+ * <Step title="First Step">These are instructions…</Step>
9
+ * <Step title="Second Step">…</Step>
10
+ * <Step title="Third Step">…</Step>
11
+ * </Steps>
12
+ *
13
+ * Steps assigns 1-based `index` to each <Step> child via cloneElement —
14
+ * callers don't have to number them by hand. A <Step> rendered outside
15
+ * <Steps> falls back to whatever `index` it was given (or 1).
16
+ *
17
+ * Each Step is a 2-column grid:
18
+ *
19
+ * [circle + line] title
20
+ * body
21
+ *
22
+ * The marker column spans both rows. The vertical rail (line below the
23
+ * circle) is `flex: 1`, so it stretches to fill the Step's full height —
24
+ * meaning adjacent steps' rails meet end-to-end with no manual height
25
+ * math. The last step keeps its rail too (matches the design).
26
+ */
27
+
28
+ export function Step({
29
+ title,
30
+ index = 1,
31
+ children,
32
+ className = '',
33
+ ...rest
34
+ }) {
35
+ return (
36
+ <div className={`velu-step ${className}`.trim()} {...rest}>
37
+ <div className="velu-step__circle" aria-hidden="true">
38
+ {index}
39
+ </div>
40
+ <div className="velu-step__line" aria-hidden="true" />
41
+ {title != null && (
42
+ <div className="velu-step__title">{title}</div>
43
+ )}
44
+ {children != null && children !== false && (
45
+ <div className="velu-step__body">{children}</div>
46
+ )}
47
+ </div>
48
+ );
49
+ }
50
+ Step.displayName = 'Step';
51
+
52
+ export default function Steps({ children, className = '', ...rest }) {
53
+ let i = 0;
54
+ const numbered = Children.map(children, (child) => {
55
+ if (!isValidElement(child)) return child;
56
+ if (child.type !== Step && child.type?.displayName !== 'Step') return child;
57
+ i += 1;
58
+ return cloneElement(child, { index: child.props.index ?? i });
59
+ });
60
+ return (
61
+ <div className={`velu-steps ${className}`.trim()} {...rest}>
62
+ {numbered}
63
+ </div>
64
+ );
65
+ }
@@ -1,48 +1,48 @@
1
- import React from 'react';
2
- import { Sun, Moon } from 'lucide-react';
3
-
4
- /**
5
- * ThemeToggle — pill-shaped two-state light/dark switch. The thumb
6
- * sits at the inline-start in light mode and slides to the inline-end
7
- * in dark mode. A sun glyph rides the thumb in light, a moon in dark.
8
- *
9
- * SSR-safe by design: markup is identical regardless of theme (the
10
- * server doesn't know it), and the thumb position + which glyph shows
11
- * are decided purely by CSS via the `[data-theme]` attribute — NOT by
12
- * React state. So there is no hydration mismatch and no icon flash. JS
13
- * only handles the click: it flips `data-theme` and persists the
14
- * explicit choice (which then overrides the OS preference on future
15
- * visits, per the anti-flash script in the template).
16
- */
17
- export default function ThemeToggle({ className = '', ...rest }) {
18
- function toggle() {
19
- const root = document.documentElement;
20
- const next = root.dataset.theme === 'dark' ? 'light' : 'dark';
21
- root.dataset.theme = next;
22
- try {
23
- localStorage.setItem('velu-theme', next);
24
- } catch {
25
- /* private mode / storage disabled — toggle still works for the session */
26
- }
27
- }
28
-
29
- return (
30
- <button
31
- type="button"
32
- onClick={toggle}
33
- aria-label="Toggle color theme"
34
- title="Toggle color theme"
35
- className={`velu-theme-toggle ${className}`.trim()}
36
- {...rest}
37
- >
38
- <Sun
39
- className="velu-theme-toggle__icon velu-theme-toggle__icon--sun"
40
- aria-hidden="true"
41
- />
42
- <Moon
43
- className="velu-theme-toggle__icon velu-theme-toggle__icon--moon"
44
- aria-hidden="true"
45
- />
46
- </button>
47
- );
48
- }
1
+ import React from 'react';
2
+ import { Sun, Moon } from 'lucide-react';
3
+
4
+ /**
5
+ * ThemeToggle — pill-shaped two-state light/dark switch. The thumb
6
+ * sits at the inline-start in light mode and slides to the inline-end
7
+ * in dark mode. A sun glyph rides the thumb in light, a moon in dark.
8
+ *
9
+ * SSR-safe by design: markup is identical regardless of theme (the
10
+ * server doesn't know it), and the thumb position + which glyph shows
11
+ * are decided purely by CSS via the `[data-theme]` attribute — NOT by
12
+ * React state. So there is no hydration mismatch and no icon flash. JS
13
+ * only handles the click: it flips `data-theme` and persists the
14
+ * explicit choice (which then overrides the OS preference on future
15
+ * visits, per the anti-flash script in the template).
16
+ */
17
+ export default function ThemeToggle({ className = '', ...rest }) {
18
+ function toggle() {
19
+ const root = document.documentElement;
20
+ const next = root.dataset.theme === 'dark' ? 'light' : 'dark';
21
+ root.dataset.theme = next;
22
+ try {
23
+ localStorage.setItem('velu-theme', next);
24
+ } catch {
25
+ /* private mode / storage disabled — toggle still works for the session */
26
+ }
27
+ }
28
+
29
+ return (
30
+ <button
31
+ type="button"
32
+ onClick={toggle}
33
+ aria-label="Toggle color theme"
34
+ title="Toggle color theme"
35
+ className={`velu-theme-toggle ${className}`.trim()}
36
+ {...rest}
37
+ >
38
+ <Sun
39
+ className="velu-theme-toggle__icon velu-theme-toggle__icon--sun"
40
+ aria-hidden="true"
41
+ />
42
+ <Moon
43
+ className="velu-theme-toggle__icon velu-theme-toggle__icon--moon"
44
+ aria-hidden="true"
45
+ />
46
+ </button>
47
+ );
48
+ }