@veluai/velu 0.2.13 → 0.2.15
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.
- package/README.md +80 -80
- package/dist/cli.js +37 -37
- package/package.json +64 -64
- package/runtime/velu-ui/base.css +320 -320
- package/runtime/velu-ui/components/Accordion.jsx +64 -64
- package/runtime/velu-ui/components/ApiClient.jsx +207 -207
- package/runtime/velu-ui/components/ApiField.jsx +87 -87
- package/runtime/velu-ui/components/ApiPath.jsx +63 -63
- package/runtime/velu-ui/components/ApiReferencePage.jsx +384 -384
- package/runtime/velu-ui/components/ApiSamples.jsx +36 -36
- package/runtime/velu-ui/components/ApiSidebar.jsx +122 -122
- package/runtime/velu-ui/components/AskBar.jsx +71 -71
- package/runtime/velu-ui/components/Callout.jsx +114 -114
- package/runtime/velu-ui/components/Card.jsx +131 -131
- package/runtime/velu-ui/components/Chatbot.jsx +885 -885
- package/runtime/velu-ui/components/CodeBlock.jsx +375 -375
- package/runtime/velu-ui/components/Columns.jsx +56 -56
- package/runtime/velu-ui/components/ContextMenu.jsx +298 -273
- package/runtime/velu-ui/components/ErrorCard.jsx +138 -138
- package/runtime/velu-ui/components/Field.jsx +81 -81
- package/runtime/velu-ui/components/Image.jsx +163 -163
- package/runtime/velu-ui/components/Logo.jsx +31 -31
- package/runtime/velu-ui/components/MethodBadge.jsx +31 -31
- package/runtime/velu-ui/components/NavSelect.jsx +108 -108
- package/runtime/velu-ui/components/NotFound.jsx +63 -63
- package/runtime/velu-ui/components/PageFeedback.jsx +219 -219
- package/runtime/velu-ui/components/PageFooter.jsx +145 -145
- package/runtime/velu-ui/components/PageHeader.jsx +422 -422
- package/runtime/velu-ui/components/PageNav.jsx +77 -77
- package/runtime/velu-ui/components/PoweredBy.jsx +51 -51
- package/runtime/velu-ui/components/Prompt.jsx +115 -115
- package/runtime/velu-ui/components/Search.jsx +460 -460
- package/runtime/velu-ui/components/Sidebar.jsx +254 -254
- package/runtime/velu-ui/components/SocialLinks.jsx +90 -90
- package/runtime/velu-ui/components/Steps.jsx +65 -65
- package/runtime/velu-ui/components/ThemeToggle.jsx +48 -48
- package/runtime/velu-ui/components/Toc.jsx +537 -537
- package/runtime/velu-ui/components/TocBar.jsx +195 -195
- package/runtime/velu-ui/components/Tree.jsx +87 -87
- package/runtime/velu-ui/components/TryItBar.jsx +102 -102
- package/runtime/velu-ui/components/accordion.css +92 -92
- package/runtime/velu-ui/components/api-page.css +208 -208
- package/runtime/velu-ui/components/api.css +635 -635
- package/runtime/velu-ui/components/ask-bar.css +94 -94
- package/runtime/velu-ui/components/card.css +105 -105
- package/runtime/velu-ui/components/chatbot.css +622 -622
- package/runtime/velu-ui/components/code-block.css +263 -263
- package/runtime/velu-ui/components/context-menu.css +173 -173
- package/runtime/velu-ui/components/docs-layout.css +822 -822
- package/runtime/velu-ui/components/field.css +82 -82
- package/runtime/velu-ui/components/image.css +237 -237
- package/runtime/velu-ui/components/nav-select.css +157 -157
- package/runtime/velu-ui/components/not-found.css +94 -94
- package/runtime/velu-ui/components/page-feedback.css +241 -241
- package/runtime/velu-ui/components/page-footer.css +130 -130
- package/runtime/velu-ui/components/page-header.css +558 -558
- package/runtime/velu-ui/components/page-nav.css +50 -50
- package/runtime/velu-ui/components/powered-by.css +92 -92
- package/runtime/velu-ui/components/prompt.css +99 -99
- package/runtime/velu-ui/components/search.css +307 -307
- package/runtime/velu-ui/components/sidebar.css +205 -205
- package/runtime/velu-ui/components/steps.css +77 -77
- package/runtime/velu-ui/components/theme-toggle.css +102 -102
- package/runtime/velu-ui/components/toc-bar.css +234 -234
- package/runtime/velu-ui/components/tree.css +49 -49
- package/runtime/velu-ui/index.js +54 -54
- package/runtime/velu-ui/lib/api-send.js +92 -92
- package/runtime/velu-ui/lib/brand-icons.jsx +103 -103
- package/runtime/velu-ui/lib/component-schemas.js +100 -100
- package/runtime/velu-ui/lib/copyText.js +64 -64
- package/runtime/velu-ui/lib/docs-assistant.js +250 -250
- package/runtime/velu-ui/lib/lang-icons.jsx +147 -147
- package/runtime/velu-ui/lib/pagefind.js +113 -113
- package/runtime/velu-ui/lib/prism-langs.js +957 -957
- package/runtime/velu-ui/lib/prism-loader.js +74 -74
- package/runtime/velu-ui/lib/resolveIcon.jsx +29 -29
- package/runtime/velu-ui/lib/scrollIntoNearestView.js +66 -66
- package/runtime/velu-ui/mdx-components.jsx +105 -105
- package/runtime/velu-ui/primitives/Cluster.jsx +49 -49
- package/runtime/velu-ui/primitives/Stack.jsx +63 -63
- package/runtime/velu-ui/primitives/Switcher.jsx +57 -57
- package/runtime/velu-ui/primitives/stack.css +3 -3
- package/runtime/velu-ui/primitives/switcher.css +25 -25
- package/runtime/velu-ui/styles.css +46 -46
- package/runtime/velu-ui/tokens.css +4 -4
- package/schema/velu.schema.json +423 -423
- package/src/lib/extract-mdx-error.js +170 -170
- package/src/lib/issues.js +159 -159
- package/src/lib/known-components.js +34 -34
- package/src/navigation.js +443 -443
- package/src/runtime/App.jsx +1669 -1668
- package/src/runtime/ErrorBoundary.jsx +54 -54
- package/src/runtime/client-entry.jsx +22 -22
- package/src/runtime/server-entry.jsx +16 -16
- package/src/template.html +48 -48
- package/templates/starter/ai-tools/claude-code.mdx +26 -26
- package/templates/starter/ai-tools/cursor.mdx +17 -17
- package/templates/starter/api-reference/introduction.mdx +43 -43
- package/templates/starter/development.mdx +19 -19
- package/templates/starter/essentials/code.mdx +29 -29
- package/templates/starter/essentials/images.mdx +29 -29
- package/templates/starter/essentials/markdown.mdx +25 -25
- package/templates/starter/essentials/navigation.mdx +39 -39
- package/templates/starter/essentials/settings.mdx +30 -30
- package/templates/starter/favicon.svg +6 -6
- package/templates/starter/index.mdx +31 -31
- package/templates/starter/openapi.json +160 -160
- package/templates/starter/quickstart.mdx +31 -31
- package/templates/starter/velu.json +41 -41
|
@@ -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
|
+
}
|