@veluai/velu 0.2.38 → 0.2.40
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/dist/cli.js +42 -42
- package/docs/aalam.md +44 -0
- package/docs/mintlify-migration.md +9 -3
- package/docs/nila.md +13 -0
- package/docs/thulir.md +23 -0
- package/package.json +5 -2
- package/runtime/velu-ui/base.css +9 -0
- package/runtime/velu-ui/components/ApiReferencePage.jsx +4 -3
- package/runtime/velu-ui/components/ApiSamples.jsx +9 -2
- package/runtime/velu-ui/components/Callout.jsx +4 -1
- package/runtime/velu-ui/components/Chatbot.jsx +0 -16
- package/runtime/velu-ui/components/NavSelect.jsx +2 -1
- package/runtime/velu-ui/components/PageFooter.jsx +3 -22
- package/runtime/velu-ui/components/PageHeader.jsx +9 -21
- package/runtime/velu-ui/components/PageNav.jsx +98 -22
- package/runtime/velu-ui/components/PoweredBy.jsx +0 -20
- package/runtime/velu-ui/components/Sidebar.jsx +21 -9
- package/runtime/velu-ui/components/ThemeToggle.jsx +1 -0
- package/runtime/velu-ui/components/TocBar.jsx +21 -2
- package/runtime/velu-ui/components/VepaFooter.jsx +19 -17
- package/runtime/velu-ui/components/api-page.css +6 -3
- package/runtime/velu-ui/components/chatbot.css +5 -4
- package/runtime/velu-ui/components/docs-layout.css +99 -114
- package/runtime/velu-ui/components/page-footer.css +2 -3
- package/runtime/velu-ui/components/page-header.css +6 -0
- package/runtime/velu-ui/components/page-nav.css +192 -0
- package/runtime/velu-ui/components/sidebar.css +5 -13
- package/runtime/velu-ui/primitives/switcher.css +4 -0
- package/runtime/velu-ui/styles.css +3 -0
- package/runtime/velu-ui/themes/aalam.css +266 -0
- package/runtime/velu-ui/themes/nila.css +62 -0
- package/runtime/velu-ui/themes/thulir.css +428 -0
- package/schema/velu.schema.json +24 -3
- package/src/navigation.js +25 -4
- package/src/runtime/App.jsx +174 -108
- package/templates/starter/ai-tools/claude-code.mdx +1 -0
- package/templates/starter/ai-tools/cursor.mdx +1 -0
- package/templates/starter/api-reference/introduction.mdx +1 -0
- package/templates/starter/development.mdx +1 -0
- package/templates/starter/essentials/code.mdx +1 -0
- package/templates/starter/essentials/images.mdx +1 -0
- package/templates/starter/essentials/markdown.mdx +1 -0
- package/templates/starter/essentials/navigation.mdx +1 -0
- package/templates/starter/essentials/settings.mdx +1 -0
- package/templates/starter/index.mdx +1 -0
- package/templates/starter/quickstart.mdx +1 -0
package/docs/aalam.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Aalam theme
|
|
2
|
+
|
|
3
|
+
Aalam (ஆலம், banyan) is Velu's Maple equivalent. Use `"theme": "aalam"` in `velu.json`. Running `velu migrate mintlify SOURCE --out DESTINATION` maps Mintlify's `"theme": "maple"` to Aalam automatically.
|
|
4
|
+
|
|
5
|
+
```json
|
|
6
|
+
{
|
|
7
|
+
"name": "My docs",
|
|
8
|
+
"theme": "aalam",
|
|
9
|
+
"colors": {
|
|
10
|
+
"primary": "#C2410C",
|
|
11
|
+
"light": "#EA580C",
|
|
12
|
+
"dark": "#FB923C"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The desktop layout places branding, search, and navigation in the left sidebar, with tabs above the article. Below 1024px, navigation becomes a left-opening drawer with a labeled section picker, focus trapping, Escape dismissal, and background scroll locking. Below 1280px, a sticky On this page dropdown replaces the right-hand TOC. Drawer and TOC hierarchy use indentation without vertical lines.
|
|
18
|
+
|
|
19
|
+
Aalam shares component styles and responsive behavior with Thulir. Its desktop layout was compared with the cached npm renderer `@mintlify/client@0.0.3566` using the same showcase content. This is not a guarantee of pixel-perfect parity: theme controls, icons, compatibility adapters, generated OpenAPI examples, and custom scripts still require review. The left-opening drawer and line-free navigation intentionally follow Velu's requested behavior.
|
|
20
|
+
|
|
21
|
+
Color semantics match the Mintlify presets: `colors.primary` for the light-mode accent, `colors.light` for the dark-mode accent, and `colors.dark` for CTA colors. The default font is Inter.
|
|
22
|
+
|
|
23
|
+
## Page icons in the sidebar
|
|
24
|
+
|
|
25
|
+
Pages can show a Lucide icon next to their sidebar label:
|
|
26
|
+
|
|
27
|
+
1. Frontmatter on the MDX page:
|
|
28
|
+
|
|
29
|
+
```mdx
|
|
30
|
+
---
|
|
31
|
+
title: Quickstart
|
|
32
|
+
icon: rocket
|
|
33
|
+
---
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
2. Or a page object in `velu.json` navigation:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{ "page": "quickstart", "icon": "zap" }
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Frontmatter is used when the nav entry is a plain path string. A nav `icon` wins when both are set.
|
|
43
|
+
|
|
44
|
+
For the migrated component showcase, run `node packages/velu-cli/test/aalam-browser.mjs` with Playwright installed and the preview running. `VELU_TEST_URL` overrides the default `http://localhost:8473`. The checks cover phone/tablet drawer focus, dismissal, route changes, horizontal overflow, desktop resizing, section selection, content tabs, and sticky TOC heading clearance.
|
|
@@ -52,7 +52,7 @@ filenames, endpoint paths, and diagnostic text; review before sharing.
|
|
|
52
52
|
|
|
53
53
|
| Area | Migration behavior |
|
|
54
54
|
| --- | --- |
|
|
55
|
-
| Config | Projects supported values against the shipped Velu schema; reports unsupported nested fields individually and preserves the original source config |
|
|
55
|
+
| Config | Projects supported values against the shipped Velu schema; maps Mintlify `mint` to `thulir`, `sequoia` to `vepa`, and `maple` to `aalam`; reports unsupported nested fields individually and preserves the original source config |
|
|
56
56
|
| Legacy config | Converts navigation arrays, prefix-based tabs, anchors, topbar links/CTA, footer socials, and API base URL/example languages |
|
|
57
57
|
| Navigation | Retains groups, tabs, products, versions, languages, root pages, and supported OpenAPI references; maps simple dropdown containers to tabs and merges global anchors/tabs with a review finding |
|
|
58
58
|
| Branding | Copies logos/favicons, colors, navbar/footer, contextual menu and SEO; maps `description` to SEO metadata, body font family to `font`, and a dual favicon to one local variant |
|
|
@@ -86,6 +86,8 @@ Verify behavior after client-side navigation as well as a full refresh.
|
|
|
86
86
|
|
|
87
87
|
The `css-dom` finding flags styles aimed at shared chrome IDs such as `#content`,
|
|
88
88
|
`#sidebar-content`, and `#footer`. Vepa aligns these common hooks with Sequoia,
|
|
89
|
+
while Thulir preserves Mint's `#sidebar`, `#navigation-items`, `#page-title`,
|
|
90
|
+
`#content`, and `#footer` structure,
|
|
89
91
|
including a content-body-only `#content` and route-specific body remounting, but
|
|
90
92
|
a shared ID does not guarantee identical nesting or dimensions for every component.
|
|
91
93
|
Selectors are preserved for review,
|
|
@@ -112,9 +114,12 @@ replaced with empty placeholders. Font Awesome/Tabler icon names may need Lucide
|
|
|
112
114
|
equivalents. Original references in code examples and prose remain unchanged.
|
|
113
115
|
|
|
114
116
|
Mintlify's `theme: "sequoia"` is converted to `theme: "vepa"` and activates the [Vepa preset](vepa.md).
|
|
115
|
-
|
|
117
|
+
Mintlify's `theme: "mint"` is converted to `theme: "thulir"` and activates the [Thulir preset](thulir.md).
|
|
118
|
+
Mintlify's `theme: "maple"` is converted to `theme: "aalam"` and activates the [Aalam preset](aalam.md).
|
|
119
|
+
Mintlify's `theme: "aspen"` is converted to `theme: "nila"` and activates the [Nila preset](nila.md).
|
|
120
|
+
The presets are not a certification of pixel equality for every component or custom script.
|
|
116
121
|
|
|
117
|
-
Features without full Velu equivalents require review:
|
|
122
|
+
Features without full Velu equivalents require review: remaining Mintlify themes, advanced
|
|
118
123
|
fonts and styling, redirects, analytics/support integrations, hosting settings,
|
|
119
124
|
authentication, personalization, hidden-page routing, legacy version filtering,
|
|
120
125
|
complex dropdown/global navigation, AsyncAPI, manually authored `api:` pages,
|
|
@@ -137,6 +142,7 @@ Inspected on 2026-09-08:
|
|
|
137
142
|
- Published [`@mintlify/cli@4.0.1479`](https://www.npmjs.com/package/@mintlify/cli/v/4.0.1479): command dispatch/options in `bin/cli.js`; starter source in `bin/init.js`.
|
|
138
143
|
- Published [`@mintlify/common@1.0.1132`](https://www.npmjs.com/package/@mintlify/common/v/1.0.1132): content/snippet categories and `.mintignore` semantics in `dist/getFileCategory.js` and `dist/mintIgnore.js`.
|
|
139
144
|
- Published [`@mintlify/prebuild@1.0.1286`](https://www.npmjs.com/package/@mintlify/prebuild/v/1.0.1286): split config references and config loading.
|
|
145
|
+
- The CLI-downloaded `@mintlify/client@0.0.3566` renderer: Mint theme dispatch, computed layout geometry, typography, navigation, API samples, responsive behavior, and footer. The renderer was inspected for interoperability research and is not redistributed.
|
|
140
146
|
- [`docs.json` schema](https://www.mintlify.com/docs.json), [custom scripts documentation](https://www.mintlify.com/docs/customize/custom-scripts), and [official starter](https://github.com/mintlify/starter).
|
|
141
147
|
|
|
142
148
|
The current `@mintlify/validation@0.1.849` tarball was also retrieved, but it did
|
package/docs/nila.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Nila theme
|
|
2
|
+
|
|
3
|
+
Nila is Velu's independently implemented Aspen equivalent. Set `"theme": "nila"` in `velu.json`, or run `velu migrate mintlify SOURCE --out DESTINATION` to map Mintlify's `"theme": "aspen"` automatically. The existing `maple` to `aalam` mapping remains supported.
|
|
4
|
+
|
|
5
|
+
The layout uses a compact 96px desktop header, sidebar group separators, a 632px reading column at a 1440px viewport, page icons, and light/dark/system preferences. Nila shares the responsive navigation foundation with Thulir: a left-opening drawer, labeled section picker, focus trapping, Escape dismissal, scroll locking, and an on-page TOC dropdown when the right rail is hidden. Vertical hierarchy lines remain disabled in the drawer and mobile TOC.
|
|
6
|
+
|
|
7
|
+
The reference is the cached npm renderer `@mintlify/client@0.0.3566`, displaying the same source showcase. Theme matching is not a guarantee of identical component behavior: icon artwork, custom CSS selectors, compatibility adapters, generated API examples and interactive playgrounds still require review.
|
|
8
|
+
|
|
9
|
+
For the migrated showcase, set `VELU_TEST_URL=http://localhost:8474` and run `node packages/velu-cli/test/aalam-browser.mjs`. This shared browser regression covers drawer behavior, section selection, content tabs, responsive widths, and TOC heading clearance.
|
|
10
|
+
|
|
11
|
+
Mintlify migration enables `navigation.preservePagePaths` when the source uses product, version, or language axes. These axes select the navigation context while retaining source page URLs, rather than adding another path prefix. Native Velu configurations retain their existing prefix behavior unless they opt into this setting.
|
|
12
|
+
|
|
13
|
+
The navigation stress fixture adds three products (Platform, Analytics, Automation), two versions each, and 32 guides per product/version pair. With that migrated fixture running on port 8474, `node packages/velu-cli/test/nila-navigation-browser.mjs` checks five viewport widths, sidebar scrolling, product/version switching, direct reloads, horizontal overflow, and nested Escape behavior. This fixture has 220 pages including the existing component/API showcase; its additional Guides tab differs from the original four-tab showcase used by the shared browser test above.
|
package/docs/thulir.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Thulir theme
|
|
2
|
+
|
|
3
|
+
Thulir is Velu's equivalent of Mintlify's `mint` theme. The name means a fresh sprout in Tamil.
|
|
4
|
+
|
|
5
|
+
```json
|
|
6
|
+
{
|
|
7
|
+
"name": "My docs",
|
|
8
|
+
"theme": "thulir",
|
|
9
|
+
"colors": {
|
|
10
|
+
"primary": "#0D9373",
|
|
11
|
+
"light": "#12A481",
|
|
12
|
+
"dark": "#46D6AD"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`velu migrate mintlify` converts `"theme": "mint"` to `"theme": "thulir"`. Thulir preserves Mint's main renderer hooks, including `#navbar`, `#sidebar`, `#navigation-items`, `#page-title`, `#content`, and `#footer`, so portable custom CSS and browser scripts retain their targets.
|
|
18
|
+
|
|
19
|
+
The preset includes Mint's desktop grid, Inter typography, navigation states, prose rhythm, compact previous/next links, API document and code columns, responsive drawer behavior, light/dark palette, and advanced footer.
|
|
20
|
+
|
|
21
|
+
Mint color semantics are preserved: `colors.primary` is the light-mode accent, `colors.light` is the dark-mode accent, and `colors.dark` supplies the header button background. Missing values fall back to `colors.primary`. Header and footer theme controls share the same light/dark/system preference.
|
|
22
|
+
|
|
23
|
+
The reference is the renderer downloaded by `@mintlify/cli@4.0.1479` (`@mintlify/client@0.0.3566`). Compare identical content at identical viewport sizes when checking parity. The reference fixture covers documentation, GET/POST API pages, desktop/tablet/mobile layouts, and dark mode. Generated OpenAPI example values, icon artwork, and playground interactions can still differ; matching the theme does not imply that every Mintlify component or script has identical behavior.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veluai/velu",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.40",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": "./dist/cli.js",
|
|
6
6
|
"publishConfig": {
|
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
"schema/**",
|
|
19
19
|
"templates/**",
|
|
20
20
|
"docs/mintlify-migration.md",
|
|
21
|
-
"docs/vepa.md"
|
|
21
|
+
"docs/vepa.md",
|
|
22
|
+
"docs/thulir.md",
|
|
23
|
+
"docs/aalam.md",
|
|
24
|
+
"docs/nila.md"
|
|
22
25
|
],
|
|
23
26
|
"scripts": {
|
|
24
27
|
"build": "node scripts/build.mjs",
|
package/runtime/velu-ui/base.css
CHANGED
|
@@ -389,6 +389,14 @@
|
|
|
389
389
|
background: var(--muted-color);
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
+
/* Kill document-level horizontal scroll. Sticky header + fixed rails
|
|
393
|
+
share the viewport scrollport — if anything widens the page, the
|
|
394
|
+
whole chrome (top bar included) slides sideways. Clip here so wide
|
|
395
|
+
children must scroll inside their own containers instead. */
|
|
396
|
+
html {
|
|
397
|
+
overflow-x: clip;
|
|
398
|
+
}
|
|
399
|
+
|
|
392
400
|
/* Unitless line-height ratios (height ÷ font-size from Figma) */
|
|
393
401
|
body {
|
|
394
402
|
font-size: var(--f-body);
|
|
@@ -396,6 +404,7 @@ body {
|
|
|
396
404
|
font-weight: var(--weight-normal);
|
|
397
405
|
background: var(--page-bg);
|
|
398
406
|
color: var(--text-color);
|
|
407
|
+
overflow-x: clip;
|
|
399
408
|
}
|
|
400
409
|
h1 { font-size: var(--f-h1); line-height: var(--lh-h1); font-weight: var(--weight-semibold); }
|
|
401
410
|
h2 { font-size: var(--f-h2); line-height: var(--lh-h2); font-weight: var(--weight-medium); }
|
|
@@ -78,7 +78,7 @@ export default function ApiReferencePage({ operation, samples = [], apiOperation
|
|
|
78
78
|
className="velu-api-page__tryit"
|
|
79
79
|
/>
|
|
80
80
|
|
|
81
|
-
{preset === 'vepa' && <div className="velu-api-page__inline-samples"><ApiSamples idPrefix="inline-" samples={samples} responses={operation.responses} preset={preset} title={operation.title} /></div>}
|
|
81
|
+
{(preset === 'vepa' || ['thulir', 'aalam', 'nila'].includes(preset)) && <div className="velu-api-page__inline-samples"><ApiSamples idPrefix="inline-" samples={samples} responses={operation.responses} preset={preset} title={operation.title} /></div>}
|
|
82
82
|
{children && <div className="velu-api-page__custom-content">{children}</div>}
|
|
83
83
|
|
|
84
84
|
{/* Authorizations */}
|
|
@@ -114,6 +114,7 @@ export default function ApiReferencePage({ operation, samples = [], apiOperation
|
|
|
114
114
|
{operation.body?.fields?.length > 0 && (
|
|
115
115
|
<api-section class="velu-api-page__section">
|
|
116
116
|
<h2 data-component="api-section-heading">Body</h2>
|
|
117
|
+
{['thulir', 'aalam', 'nila'].includes(preset) && operation.body.contentType && <span className="velu-api-body-type">{operation.body.contentType}</span>}
|
|
117
118
|
{operation.body.fields.map((f, i) => (
|
|
118
119
|
<Field key={i} name={f.name} type={f.type} required={f.required}>
|
|
119
120
|
{f.description}
|
|
@@ -179,10 +180,10 @@ function EnumHint({ values }) {
|
|
|
179
180
|
}
|
|
180
181
|
|
|
181
182
|
function ResponseSections({ responses, preset }) {
|
|
182
|
-
if (preset === 'vepa') return <div className="velu-vepa-responses">{responses.map((r, i) => <section key={i}>
|
|
183
|
+
if (preset === 'vepa' || ['thulir', 'aalam', 'nila'].includes(preset)) return <div className="velu-vepa-responses">{responses.map((r, i) => <section key={i}>
|
|
183
184
|
<div className="velu-vepa-responses__status">{r.status}{r.contentType && ` · ${r.contentType}`}</div>
|
|
184
185
|
{r.description && <p>{r.description}</p>}
|
|
185
|
-
{r.fields?.map((f, j) => <Field key={j} name={f.name} type={f.type} required={f.required}>{f.description}</Field>)}
|
|
186
|
+
{(['thulir', 'aalam', 'nila'].includes(preset) ? [...(r.fields || [])].sort((a, b) => Number(Boolean(b.required)) - Number(Boolean(a.required))) : r.fields)?.map((f, j) => <Field key={f.name || j} name={f.name} type={f.type} required={f.required}>{f.description}</Field>)}
|
|
186
187
|
</section>)}</div>;
|
|
187
188
|
return (
|
|
188
189
|
<AccordionGroup className="velu-api-resp">
|
|
@@ -12,12 +12,19 @@ import CodeBlock, { CodeGroup } from './CodeBlock.jsx';
|
|
|
12
12
|
export default function ApiSamples({ samples = [], responses = [], preset, title, idPrefix = '' }) {
|
|
13
13
|
const [selected, setSelected] = React.useState('');
|
|
14
14
|
const sample = samples.find((s) => s.key === selected) || samples[0];
|
|
15
|
+
// Mint displays JSON request bodies across lines. Keep custom/non-JSON
|
|
16
|
+
// snippets intact, and change whitespace only in a valid JSON body.
|
|
17
|
+
const sampleCode = ['thulir', 'aalam', 'nila'].includes(preset) && sample?.key === 'curl'
|
|
18
|
+
? sample.code.replace(/(--data )'([\s\S]*)'$/, (original, prefix, body) => {
|
|
19
|
+
try { return `${prefix}'\n${JSON.stringify(JSON.parse(body), null, 2)}\n'`; }
|
|
20
|
+
catch { return original; }
|
|
21
|
+
}) : sample?.code;
|
|
15
22
|
const withExample = responses.filter((r) => r.example != null);
|
|
16
23
|
return (
|
|
17
24
|
<div className="velu-api-samples">
|
|
18
|
-
{sample && preset === 'vepa' ? <section id={idPrefix + 'request-example'} className="velu-vepa-request">
|
|
25
|
+
{sample && (preset === 'vepa' || ['thulir', 'aalam', 'nila'].includes(preset)) ? <section id={idPrefix + 'request-example'} className="velu-vepa-request">
|
|
19
26
|
<div className="velu-vepa-request__header"><span>{title}</span><select aria-label="Request example language" value={sample.key} onChange={(e) => setSelected(e.target.value)}>{samples.map((s) => <option key={s.key} value={s.key}>{s.label}</option>)}</select></div>
|
|
20
|
-
<CodeBlock language={sample.language}>{
|
|
27
|
+
<CodeBlock language={sample.language}>{sampleCode}</CodeBlock>
|
|
21
28
|
</section> : samples.length > 0 && (
|
|
22
29
|
<CodeGroup id={idPrefix + 'request-example'} className="velu-api-samples__req">
|
|
23
30
|
{samples.map((s) => (
|
|
@@ -90,7 +90,10 @@ export default function Callout({
|
|
|
90
90
|
>
|
|
91
91
|
{iconNode && (
|
|
92
92
|
<span className="velu-callout__icon" aria-hidden="true">
|
|
93
|
-
{
|
|
93
|
+
{type === 'note' && icon == null ? <>
|
|
94
|
+
<span className="velu-callout__default-note">{iconNode}</span>
|
|
95
|
+
<span className="velu-callout__mint-note" style={{ display: 'none' }}>{resolveIcon('circle-alert', { size: '1em' })}</span>
|
|
96
|
+
</> : iconNode}
|
|
94
97
|
</span>
|
|
95
98
|
)}
|
|
96
99
|
{children}
|
|
@@ -33,21 +33,6 @@ import Cluster from '../primitives/Cluster.jsx';
|
|
|
33
33
|
* demo answer (so dev preview / offline still shows the experience).
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
|
-
/* ── Brand mark — the Velu double-hook logo ─────────────────────────── */
|
|
37
|
-
// `size` is the glyph height; width derives from the 32:24 viewBox.
|
|
38
|
-
function VeluMark({ size = 'var(--icon-size-lg)' }) {
|
|
39
|
-
return (
|
|
40
|
-
<svg
|
|
41
|
-
className="velu-chatbot__mark"
|
|
42
|
-
viewBox="0 0 32 24"
|
|
43
|
-
fill="currentColor"
|
|
44
|
-
aria-hidden="true"
|
|
45
|
-
style={{ width: `calc(${size} * (32 / 24))`, height: size }}
|
|
46
|
-
>
|
|
47
|
-
<path d="M 29.656 14.153 C 29.574 14.562 29.351 14.929 29.025 15.193 C 28.699 15.457 28.291 15.601 27.869 15.601 L 21.37 15.601 C 20.534 15.601 19.805 16.163 19.603 16.964 L 18.167 22.637 C 18.068 23.026 17.841 23.372 17.521 23.619 C 17.2 23.866 16.805 24 16.399 24 L 10.438 24 C 10.161 24 9.888 23.938 9.639 23.818 C 9.39 23.698 9.172 23.524 9.001 23.308 C 8.831 23.092 8.712 22.841 8.655 22.574 C 8.598 22.306 8.603 22.029 8.67 21.764 L 10.189 15.764 C 10.287 15.374 10.515 15.029 10.835 14.782 C 11.155 14.535 11.55 14.401 11.956 14.4 L 18.393 14.4 C 19.262 14.4 20.01 13.795 20.18 12.953 L 22.388 2.048 C 22.471 1.639 22.694 1.272 23.019 1.008 C 23.345 0.744 23.754 0.6 24.175 0.6 L 30.177 0.6 C 30.447 0.6 30.713 0.659 30.957 0.773 C 31.201 0.886 31.416 1.052 31.587 1.258 C 31.758 1.464 31.881 1.705 31.946 1.964 C 32.011 2.222 32.017 2.492 31.964 2.753 L 29.656 14.153 Z M 9.611 13.554 C 9.528 13.962 9.305 14.329 8.979 14.593 C 8.653 14.857 8.245 15.001 7.824 15.001 L 1.823 15.001 C 1.553 15.001 1.287 14.942 1.043 14.828 C 0.799 14.714 0.584 14.548 0.413 14.342 C 0.242 14.136 0.119 13.895 0.054 13.637 C -0.011 13.378 -0.017 13.109 0.036 12.848 L 2.344 1.447 C 2.426 1.039 2.649 0.672 2.975 0.408 C 3.301 0.144 3.709 0 4.131 0 L 10.132 0 C 10.402 0 10.668 0.059 10.912 0.173 C 11.155 0.287 11.371 0.453 11.541 0.659 C 11.712 0.865 11.835 1.106 11.9 1.364 C 11.965 1.623 11.972 1.892 11.919 2.153 L 9.611 13.553 L 9.611 13.554 Z" />
|
|
48
|
-
</svg>
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
36
|
|
|
52
37
|
/* ── "Thinking" loader — 3x3 grid of squares, opacity wave sweeps the
|
|
53
38
|
anti-diagonal so it ripples top-left → bottom-right. ─────────────── */
|
|
@@ -346,7 +331,6 @@ function ChatHeader({ onClose, onNew, onHistory, historyOpen, showClose = true }
|
|
|
346
331
|
className="velu-chatbot__header"
|
|
347
332
|
>
|
|
348
333
|
<Cluster space="var(--s-2)" align="center">
|
|
349
|
-
<VeluMark />
|
|
350
334
|
<span className="velu-chatbot__brand">
|
|
351
335
|
<span className="velu-chatbot__brand-name">Velu</span>
|
|
352
336
|
<span className="velu-chatbot__brand-sep">/</span>
|
|
@@ -49,7 +49,7 @@ export default function NavSelect({
|
|
|
49
49
|
if (!rootRef.current?.contains(e.target)) setOpen(false);
|
|
50
50
|
};
|
|
51
51
|
const onKey = (e) => {
|
|
52
|
-
if (e.key === 'Escape') setOpen(false);
|
|
52
|
+
if (e.key === 'Escape') { setOpen(false); rootRef.current?.querySelector('button')?.focus(); }
|
|
53
53
|
};
|
|
54
54
|
document.addEventListener('mousedown', onDoc);
|
|
55
55
|
document.addEventListener('keydown', onKey);
|
|
@@ -124,3 +124,4 @@ export default function NavSelect({
|
|
|
124
124
|
</div>
|
|
125
125
|
);
|
|
126
126
|
}
|
|
127
|
+
|
|
@@ -47,26 +47,6 @@ import SocialLinks from './SocialLinks.jsx';
|
|
|
47
47
|
* linkComponent?: React.ElementType, className?: string }} props
|
|
48
48
|
*/
|
|
49
49
|
|
|
50
|
-
/* ── Brand mark — the Velu double-hook logo. Color: currentColor for
|
|
51
|
-
the surrounding wordmark; the mark itself uses --accent-color via
|
|
52
|
-
the wrapping span. ───────────────────────────────────────────────── */
|
|
53
|
-
function VeluMark() {
|
|
54
|
-
return (
|
|
55
|
-
<svg
|
|
56
|
-
className="velu-footer__mark"
|
|
57
|
-
viewBox="0 0 32 24"
|
|
58
|
-
fill="currentColor"
|
|
59
|
-
aria-hidden="true"
|
|
60
|
-
>
|
|
61
|
-
<path
|
|
62
|
-
fillRule="evenodd"
|
|
63
|
-
clipRule="evenodd"
|
|
64
|
-
d="M29.6564 14.1534C29.5739 14.5616 29.3507 14.9288 29.0248 15.1926C28.699 15.4565 28.2907 15.6007 27.8694 15.6006H21.3702C20.5341 15.6006 19.8051 16.1627 19.6031 16.9638L18.1671 22.6368C18.0685 23.0263 17.8409 23.372 17.5205 23.619C17.2002 23.866 16.8054 24.0001 16.399 24H10.4378C10.1609 24.0001 9.88759 23.9378 9.63868 23.8179C9.38976 23.6979 9.1718 23.5235 9.00135 23.3079C8.8309 23.0923 8.71245 22.8412 8.65499 22.5736C8.59754 22.306 8.6026 22.029 8.66978 21.7636L10.1888 15.7636C10.2874 15.3743 10.5148 15.0287 10.835 14.7817C11.1551 14.5347 11.5496 14.4005 11.9559 14.4004H18.3931C19.2621 14.4004 20.0101 13.7949 20.1801 12.9532L22.3882 2.04774C22.4705 1.63945 22.6936 1.27198 23.0195 1.00793C23.3453 0.74388 23.7538 0.599578 24.1752 0.599605H30.1774C30.4471 0.599529 30.7134 0.658587 30.9572 0.772515C31.201 0.886444 31.4161 1.0524 31.5871 1.25842C31.758 1.46443 31.8806 1.70537 31.9458 1.96383C32.0111 2.2223 32.0175 2.49185 31.9645 2.75305L29.6564 14.1534ZM9.61081 13.5538C9.52825 13.962 9.30507 14.3292 8.97924 14.593C8.65341 14.857 8.24508 15.0011 7.82375 15.001H1.82257C1.55289 15.0011 1.28656 14.942 1.04279 14.8281C0.79902 14.7141 0.583892 14.5482 0.412928 14.3422C0.241965 14.1362 0.11943 13.8952 0.0541639 13.6368C-0.0111017 13.3783 -0.0174699 13.1087 0.0355186 12.8475L2.34359 1.44715C2.42614 1.03904 2.64933 0.671798 2.97516 0.407944C3.30099 0.144089 3.70932 -7.24082e-05 4.13064 7.25899e-08H10.1318C10.4015 -7.55792e-05 10.6678 0.0589818 10.9116 0.172911C11.1554 0.286839 11.3705 0.453799 11.5415 0.659813C11.7124 0.864828 11.835 1.10576 11.9002 1.36423C11.9655 1.62269 11.9719 1.89225 11.9189 2.15344L9.61081 13.553L9.61081 13.5538Z"
|
|
65
|
-
/>
|
|
66
|
-
</svg>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
50
|
export default function PageFooter({
|
|
71
51
|
preset,
|
|
72
52
|
brand,
|
|
@@ -77,13 +57,14 @@ export default function PageFooter({
|
|
|
77
57
|
...rest
|
|
78
58
|
}) {
|
|
79
59
|
const Link = linkComponent;
|
|
80
|
-
if (preset === 'vepa'
|
|
60
|
+
if (preset === 'vepa' || ['thulir', 'aalam', 'nila'].includes(preset)) {
|
|
61
|
+
return <VepaFooter preset={preset} brand={brand} columns={columns} socials={socials} linkComponent={Link} />;
|
|
62
|
+
}
|
|
81
63
|
const brandLabel = brand?.label ?? 'Velu';
|
|
82
64
|
const brandHref = brand?.href;
|
|
83
65
|
|
|
84
66
|
const brandNode = (
|
|
85
67
|
<Cluster space="var(--s-3)" align="center" className="velu-footer__brand">
|
|
86
|
-
<VeluMark />
|
|
87
68
|
<span className="velu-footer__wordmark">{brandLabel}</span>
|
|
88
69
|
</Cluster>
|
|
89
70
|
);
|
|
@@ -48,25 +48,8 @@ import Logo from './Logo.jsx';
|
|
|
48
48
|
* linkComponent?: React.ElementType, className?: string }} props
|
|
49
49
|
*/
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
mobile nav drawer) can render the mark alongside its own chrome. */
|
|
54
|
-
export function VeluMark() {
|
|
55
|
-
return (
|
|
56
|
-
<svg
|
|
57
|
-
className="velu-header__mark"
|
|
58
|
-
viewBox="0 0 32 24"
|
|
59
|
-
fill="currentColor"
|
|
60
|
-
aria-hidden="true"
|
|
61
|
-
>
|
|
62
|
-
<path
|
|
63
|
-
fillRule="evenodd"
|
|
64
|
-
clipRule="evenodd"
|
|
65
|
-
d="M29.6564 14.1534C29.5739 14.5616 29.3507 14.9288 29.0248 15.1926C28.699 15.4565 28.2907 15.6007 27.8694 15.6006H21.3702C20.5341 15.6006 19.8051 16.1627 19.6031 16.9638L18.1671 22.6368C18.0685 23.0263 17.8409 23.372 17.5205 23.619C17.2002 23.866 16.8054 24.0001 16.399 24H10.4378C10.1609 24.0001 9.88759 23.9378 9.63868 23.8179C9.38976 23.6979 9.1718 23.5235 9.00135 23.3079C8.8309 23.0923 8.71245 22.8412 8.65499 22.5736C8.59754 22.306 8.6026 22.029 8.66978 21.7636L10.1888 15.7636C10.2874 15.3743 10.5148 15.0287 10.835 14.7817C11.1551 14.5347 11.5496 14.4005 11.9559 14.4004H18.3931C19.2621 14.4004 20.0101 13.7949 20.1801 12.9532L22.3882 2.04774C22.4705 1.63945 22.6936 1.27198 23.0195 1.00793C23.3453 0.74388 23.7538 0.599578 24.1752 0.599605H30.1774C30.4471 0.599529 30.7134 0.658587 30.9572 0.772515C31.201 0.886444 31.4161 1.0524 31.5871 1.25842C31.758 1.46443 31.8806 1.70537 31.9458 1.96383C32.0111 2.2223 32.0175 2.49185 31.9645 2.75305L29.6564 14.1534ZM9.61081 13.5538C9.52825 13.962 9.30507 14.3292 8.97924 14.593C8.65341 14.857 8.24508 15.0011 7.82375 15.001H1.82257C1.55289 15.0011 1.28656 14.942 1.04279 14.8281C0.79902 14.7141 0.583892 14.5482 0.412928 14.3422C0.241965 14.1362 0.11943 13.8952 0.0541639 13.6368C-0.0111017 13.3783 -0.0174699 13.1087 0.0355186 12.8475L2.34359 1.44715C2.42614 1.03904 2.64933 0.671798 2.97516 0.407944C3.30099 0.144089 3.70932 -7.24082e-05 4.13064 7.25899e-08H10.1318C10.4015 -7.55792e-05 10.6678 0.0589818 10.9116 0.172911C11.1554 0.286839 11.3705 0.453799 11.5415 0.659813C11.7124 0.864828 11.835 1.10576 11.9002 1.36423C11.9655 1.62269 11.9719 1.89225 11.9189 2.15344L9.61081 13.5538Z"
|
|
66
|
-
/>
|
|
67
|
-
</svg>
|
|
68
|
-
);
|
|
69
|
-
}
|
|
51
|
+
// Kept for callers importing the former mark; built-in branding is text-only.
|
|
52
|
+
export function VeluMark() { return null; }
|
|
70
53
|
|
|
71
54
|
function ActionButton({ action, Link, id }) {
|
|
72
55
|
const { label, icon, kind = 'outlined', href, onClick, external, component } = action;
|
|
@@ -243,6 +226,8 @@ export default function PageHeader({
|
|
|
243
226
|
activeTab,
|
|
244
227
|
breadcrumb,
|
|
245
228
|
onMenuClick,
|
|
229
|
+
menuOpen,
|
|
230
|
+
menuId,
|
|
246
231
|
linkComponent = 'a',
|
|
247
232
|
className = '',
|
|
248
233
|
...rest
|
|
@@ -280,7 +265,6 @@ export default function PageHeader({
|
|
|
280
265
|
<Logo logo={brand.logo} alt={brandLabel} />
|
|
281
266
|
) : (
|
|
282
267
|
<>
|
|
283
|
-
<VeluMark />
|
|
284
268
|
<span className="velu-header__wordmark">{brandLabel}</span>
|
|
285
269
|
</>
|
|
286
270
|
)}
|
|
@@ -290,7 +274,7 @@ export default function PageHeader({
|
|
|
290
274
|
return (
|
|
291
275
|
<Stack
|
|
292
276
|
as="header"
|
|
293
|
-
space={preset === 'vepa' ? '0px' : 'var(--s0)'}
|
|
277
|
+
space={preset === 'vepa' || ['thulir', 'aalam', 'nila'].includes(preset) ? '0px' : 'var(--s0)'}
|
|
294
278
|
data-has-tabs={tabs.length > 0 ? 'true' : 'false'}
|
|
295
279
|
className={`velu-header${scrolled ? ' velu-header--scrolled' : ''}${
|
|
296
280
|
showTabsRow ? '' : ' velu-header--no-tabs'
|
|
@@ -391,6 +375,8 @@ export default function PageHeader({
|
|
|
391
375
|
data-component="breadcrumb-list"
|
|
392
376
|
onClick={onMenuClick}
|
|
393
377
|
aria-label="Open navigation menu"
|
|
378
|
+
aria-expanded={menuOpen}
|
|
379
|
+
aria-controls={menuId}
|
|
394
380
|
>
|
|
395
381
|
{breadcrumb.map((c, i) => {
|
|
396
382
|
const isLast = i === breadcrumb.length - 1;
|
|
@@ -432,6 +418,8 @@ export default function PageHeader({
|
|
|
432
418
|
type="button"
|
|
433
419
|
className="velu-header__menu"
|
|
434
420
|
aria-label="Open navigation menu"
|
|
421
|
+
aria-expanded={menuOpen}
|
|
422
|
+
aria-controls={menuId}
|
|
435
423
|
onClick={onMenuClick}
|
|
436
424
|
>
|
|
437
425
|
<Menu aria-hidden="true" focusable="false" />
|
|
@@ -5,29 +5,23 @@ import Switcher from '../primitives/Switcher.jsx';
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* PageNav — previous / next page navigation for the foot of a docs
|
|
8
|
-
* page.
|
|
9
|
-
* start, a "‹ Previous" label) and the next page (flush to the inline
|
|
10
|
-
* end, "Next ›").
|
|
8
|
+
* page.
|
|
11
9
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* />
|
|
17
|
-
*
|
|
18
|
-
* Either side may be omitted (first / last page) — the present card
|
|
19
|
-
* keeps its half-width slot. Cards sit side by side and stack on narrow
|
|
20
|
-
* widths via <Switcher>. Router-agnostic through `linkComponent`.
|
|
10
|
+
* Default / Thulir: two text cards ("‹ Previous" / "Next ›").
|
|
11
|
+
* Aalam (Maple): padded tray with a featured destination card
|
|
12
|
+
* (title + description + label) and a compact Previous when both
|
|
13
|
+
* sides exist — matching Mintlify Maple's pagination.
|
|
21
14
|
*
|
|
22
15
|
* @typedef {Object} PageRef
|
|
23
|
-
* @property {string} label
|
|
16
|
+
* @property {string} label
|
|
24
17
|
* @property {string} [href]
|
|
18
|
+
* @property {string} [description]
|
|
25
19
|
*
|
|
26
|
-
* @param {{ prev?: PageRef, next?: PageRef,
|
|
20
|
+
* @param {{ prev?: PageRef, next?: PageRef, variant?: 'default'|'aalam',
|
|
27
21
|
* linkComponent?: React.ElementType, className?: string }} props
|
|
28
22
|
*/
|
|
29
23
|
|
|
30
|
-
function
|
|
24
|
+
function DefaultCard({ page, dir, Link }) {
|
|
31
25
|
const isPrev = dir === 'prev';
|
|
32
26
|
return (
|
|
33
27
|
<Link
|
|
@@ -36,11 +30,7 @@ function NavCard({ page, dir, Link }) {
|
|
|
36
30
|
href={page.href ?? '#'}
|
|
37
31
|
>
|
|
38
32
|
<pagination-title class="velu-pagenav__title">{page.label}</pagination-title>
|
|
39
|
-
<Cluster
|
|
40
|
-
space="var(--s-4)"
|
|
41
|
-
align="center"
|
|
42
|
-
className="velu-pagenav__dir"
|
|
43
|
-
>
|
|
33
|
+
<Cluster space="var(--s-4)" align="center" className="velu-pagenav__dir">
|
|
44
34
|
{isPrev && resolveIcon('chevron-left', { size: '1em' })}
|
|
45
35
|
<span>{isPrev ? 'Previous' : 'Next'}</span>
|
|
46
36
|
{!isPrev && resolveIcon('chevron-right', { size: '1em' })}
|
|
@@ -49,13 +39,99 @@ function NavCard({ page, dir, Link }) {
|
|
|
49
39
|
);
|
|
50
40
|
}
|
|
51
41
|
|
|
42
|
+
/** Maple featured destination card: title + description | Next/Previous. */
|
|
43
|
+
function MapleFeatured({ page, dir, Link }) {
|
|
44
|
+
const isPrev = dir === 'prev';
|
|
45
|
+
const meta = (
|
|
46
|
+
<div className="velu-pagenav__meta">
|
|
47
|
+
<pagination-title class="velu-pagenav__title">{page.label}</pagination-title>
|
|
48
|
+
{page.description ? (
|
|
49
|
+
<span className="velu-pagenav__desc">{page.description}</span>
|
|
50
|
+
) : null}
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
const rule = <div className="velu-pagenav__rule" aria-hidden="true" />;
|
|
54
|
+
const label = (
|
|
55
|
+
<div className="velu-pagenav__dir">
|
|
56
|
+
{isPrev ? resolveIcon('chevron-left', { size: 12, strokeWidth: 3 }) : null}
|
|
57
|
+
<span>{isPrev ? 'Previous' : 'Next'}</span>
|
|
58
|
+
{!isPrev ? resolveIcon('chevron-right', { size: 12, strokeWidth: 3 }) : null}
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
return (
|
|
62
|
+
<Link
|
|
63
|
+
className={`velu-pagenav__featured velu-pagenav__featured--${dir}`}
|
|
64
|
+
data-component={`pagination-${dir}`}
|
|
65
|
+
href={page.href ?? '#'}
|
|
66
|
+
>
|
|
67
|
+
<div className="velu-pagenav__featured-inner">
|
|
68
|
+
{isPrev ? (
|
|
69
|
+
<>
|
|
70
|
+
{label}
|
|
71
|
+
{rule}
|
|
72
|
+
{meta}
|
|
73
|
+
</>
|
|
74
|
+
) : (
|
|
75
|
+
<>
|
|
76
|
+
{meta}
|
|
77
|
+
{rule}
|
|
78
|
+
{label}
|
|
79
|
+
</>
|
|
80
|
+
)}
|
|
81
|
+
</div>
|
|
82
|
+
</Link>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Compact Previous control when Next is the featured card. */
|
|
87
|
+
function MapleCompactPrev({ page, Link }) {
|
|
88
|
+
return (
|
|
89
|
+
<Link
|
|
90
|
+
className="velu-pagenav__compact velu-pagenav__compact--prev"
|
|
91
|
+
data-component="pagination-prev"
|
|
92
|
+
href={page.href ?? '#'}
|
|
93
|
+
>
|
|
94
|
+
{resolveIcon('chevron-left', { size: 12, strokeWidth: 3 })}
|
|
95
|
+
<span>Previous</span>
|
|
96
|
+
</Link>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function AalamNav({ prev, next, Link }) {
|
|
101
|
+
// Maple: when both exist, Previous is compact and Next is featured.
|
|
102
|
+
// Only-prev / only-next: the single side is the featured card.
|
|
103
|
+
if (prev && next) {
|
|
104
|
+
return (
|
|
105
|
+
<>
|
|
106
|
+
<MapleCompactPrev page={prev} Link={Link} />
|
|
107
|
+
<MapleFeatured page={next} dir="next" Link={Link} />
|
|
108
|
+
</>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
if (next) return <MapleFeatured page={next} dir="next" Link={Link} />;
|
|
112
|
+
if (prev) return <MapleFeatured page={prev} dir="prev" Link={Link} />;
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
52
116
|
export default function PageNav({
|
|
53
117
|
prev,
|
|
54
118
|
next,
|
|
119
|
+
variant = 'default',
|
|
55
120
|
linkComponent = 'a',
|
|
56
121
|
className = '',
|
|
57
122
|
...rest
|
|
58
123
|
}) {
|
|
124
|
+
if (variant === 'aalam') {
|
|
125
|
+
return (
|
|
126
|
+
<nav
|
|
127
|
+
className={`velu-pagenav velu-pagenav--aalam ${className}`.trim()}
|
|
128
|
+
{...rest}
|
|
129
|
+
>
|
|
130
|
+
<AalamNav prev={prev} next={next} Link={linkComponent} />
|
|
131
|
+
</nav>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
59
135
|
return (
|
|
60
136
|
<Switcher
|
|
61
137
|
space="var(--s0)"
|
|
@@ -64,12 +140,12 @@ export default function PageNav({
|
|
|
64
140
|
{...rest}
|
|
65
141
|
>
|
|
66
142
|
{prev ? (
|
|
67
|
-
<
|
|
143
|
+
<DefaultCard page={prev} dir="prev" Link={linkComponent} />
|
|
68
144
|
) : (
|
|
69
145
|
<div aria-hidden="true" />
|
|
70
146
|
)}
|
|
71
147
|
{next ? (
|
|
72
|
-
<
|
|
148
|
+
<DefaultCard page={next} dir="next" Link={linkComponent} />
|
|
73
149
|
) : (
|
|
74
150
|
<div aria-hidden="true" />
|
|
75
151
|
)}
|
|
@@ -8,25 +8,6 @@ import React from 'react';
|
|
|
8
8
|
* @param {{ href?: string, className?: string }} props
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
/* Velu brand mark — same path as PageHeader's mark, sized for the
|
|
12
|
-
badge via .velu-powered-by__mark CSS. */
|
|
13
|
-
function VeluMark() {
|
|
14
|
-
return (
|
|
15
|
-
<svg
|
|
16
|
-
className="velu-powered-by__mark"
|
|
17
|
-
viewBox="0 0 32 24"
|
|
18
|
-
fill="currentColor"
|
|
19
|
-
aria-hidden="true"
|
|
20
|
-
>
|
|
21
|
-
<path
|
|
22
|
-
fillRule="evenodd"
|
|
23
|
-
clipRule="evenodd"
|
|
24
|
-
d="M29.6564 14.1534C29.5739 14.5616 29.3507 14.9288 29.0248 15.1926C28.699 15.4565 28.2907 15.6007 27.8694 15.6006H21.3702C20.5341 15.6006 19.8051 16.1627 19.6031 16.9638L18.1671 22.6368C18.0685 23.0263 17.8409 23.372 17.5205 23.619C17.2002 23.866 16.8054 24.0001 16.399 24H10.4378C10.1609 24.0001 9.88759 23.9378 9.63868 23.8179C9.38976 23.6979 9.1718 23.5235 9.00135 23.3079C8.8309 23.0923 8.71245 22.8412 8.65499 22.5736C8.59754 22.306 8.6026 22.029 8.66978 21.7636L10.1888 15.7636C10.2874 15.3743 10.5148 15.0287 10.835 14.7817C11.1551 14.5347 11.5496 14.4005 11.9559 14.4004H18.3931C19.2621 14.4004 20.0101 13.7949 20.1801 12.9532L22.3882 2.04774C22.4705 1.63945 22.6936 1.27198 23.0195 1.00793C23.3453 0.74388 23.7538 0.599578 24.1752 0.599605H30.1774C30.4471 0.599529 30.7134 0.658587 30.9572 0.772515C31.201 0.886444 31.4161 1.0524 31.5871 1.25842C31.758 1.46443 31.8806 1.70537 31.9458 1.96383C32.0111 2.2223 32.0175 2.49185 31.9645 2.75305L29.6564 14.1534ZM9.61081 13.5538C9.52825 13.962 9.30507 14.3292 8.97924 14.593C8.65341 14.857 8.24508 15.0011 7.82375 15.001H1.82257C1.55289 15.0011 1.28656 14.942 1.04279 14.8281C0.79902 14.7141 0.583892 14.5482 0.412928 14.3422C0.241965 14.1362 0.11943 13.8952 0.0541639 13.6368C-0.0111017 13.3783 -0.0174699 13.1087 0.0355186 12.8475L2.34359 1.44715C2.42614 1.03904 2.64933 0.671798 2.97516 0.407944C3.30099 0.144089 3.70932 -7.24082e-05 4.13064 7.25899e-08H10.1318C10.4015 -7.55792e-05 10.6678 0.0589818 10.9116 0.172911C11.1554 0.286839 11.3705 0.453799 11.5415 0.659813C11.7124 0.864828 11.835 1.10576 11.9002 1.36423C11.9655 1.62269 11.9719 1.89225 11.9189 2.15344L9.61081 13.5538Z"
|
|
25
|
-
/>
|
|
26
|
-
</svg>
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
11
|
export default function PoweredBy({
|
|
31
12
|
href = 'https://veludocs.com',
|
|
32
13
|
className = '',
|
|
@@ -42,7 +23,6 @@ export default function PoweredBy({
|
|
|
42
23
|
>
|
|
43
24
|
<span className="velu-powered-by__label">Powered by</span>
|
|
44
25
|
<span className="velu-powered-by__brand">
|
|
45
|
-
<VeluMark />
|
|
46
26
|
<span className="velu-powered-by__wordmark">Velu</span>
|
|
47
27
|
</span>
|
|
48
28
|
</a>
|
|
@@ -152,16 +152,28 @@ export default function Sidebar({
|
|
|
152
152
|
);
|
|
153
153
|
|
|
154
154
|
// Bring the active item into view inside the parent scroll container
|
|
155
|
-
// whenever the active route changes
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
// scrollIntoView) so `scroll-padding-*` on the parent — which the
|
|
159
|
-
// docs layout uses to mark the footer-eclipsed band as off-limits —
|
|
160
|
-
// is reliably honoured.
|
|
155
|
+
// whenever the active route changes. useLayoutEffect + a frame wait
|
|
156
|
+
// so nested <details> have opened and sticky headings have laid out
|
|
157
|
+
// before we measure — otherwise a deep page can stay clipped.
|
|
161
158
|
const rootRef = React.useRef(null);
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
159
|
+
useIsoLayoutEffect(() => {
|
|
160
|
+
const nav = rootRef.current;
|
|
161
|
+
if (!nav) return undefined;
|
|
162
|
+
let cancelled = false;
|
|
163
|
+
const reveal = () => {
|
|
164
|
+
if (cancelled) return;
|
|
165
|
+
const el = nav.querySelector('[aria-current="page"]');
|
|
166
|
+
if (!el) return;
|
|
167
|
+
// Clear sticky section headings so the active row isn't tucked
|
|
168
|
+
// under a pinned title (honours scroll-padding on the scroller).
|
|
169
|
+
scrollIntoNearestView(el);
|
|
170
|
+
};
|
|
171
|
+
reveal();
|
|
172
|
+
const id = requestAnimationFrame(reveal);
|
|
173
|
+
return () => {
|
|
174
|
+
cancelled = true;
|
|
175
|
+
cancelAnimationFrame(id);
|
|
176
|
+
};
|
|
165
177
|
}, [activeHref]);
|
|
166
178
|
|
|
167
179
|
// The single gliding active indicator (crimson tint + flush-left bar). One
|