@veluai/velu 0.1.16 → 0.2.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.
- package/dist/cli.js +45 -39
- package/package.json +8 -3
- package/runtime/velu-ui/components/ApiClient.jsx +13 -3
- package/runtime/velu-ui/components/ApiReferencePage.jsx +9 -13
- package/runtime/velu-ui/components/ContextMenu.jsx +272 -0
- package/runtime/velu-ui/components/NotFound.jsx +63 -0
- package/runtime/velu-ui/components/api-page.css +0 -7
- package/runtime/velu-ui/components/api.css +48 -8
- package/runtime/velu-ui/components/context-menu.css +170 -0
- package/runtime/velu-ui/components/docs-layout.css +18 -0
- package/runtime/velu-ui/components/not-found.css +94 -0
- package/runtime/velu-ui/components/powered-by.css +6 -0
- package/runtime/velu-ui/index.js +2 -0
- package/runtime/velu-ui/lib/brand-icons.jsx +102 -0
- package/runtime/velu-ui/styles.css +2 -0
- package/schema/velu.schema.json +112 -37
- package/src/runtime/App.jsx +62 -0
- package/templates/starter/velu.json +4 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/* not-found.css — the 404 page (Direction 1 · Classic).
|
|
2
|
+
Mapped onto Velu tokens, so light/dark follow [data-theme]. */
|
|
3
|
+
|
|
4
|
+
.velu-404 {
|
|
5
|
+
inline-size: 34rem;
|
|
6
|
+
max-inline-size: 100%;
|
|
7
|
+
margin-inline: auto;
|
|
8
|
+
text-align: center;
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
align-items: center;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.velu-404__eyebrow {
|
|
15
|
+
margin: 0 0 var(--s-1);
|
|
16
|
+
font-size: 0.75rem;
|
|
17
|
+
font-weight: var(--weight-medium);
|
|
18
|
+
letter-spacing: 0.06em;
|
|
19
|
+
text-transform: uppercase;
|
|
20
|
+
color: var(--accent-color);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Big crimson numeral — the brand font (Outfit), light weight. clamp keeps
|
|
24
|
+
it large on desktop but never overflows a narrow screen. */
|
|
25
|
+
.velu-404__num {
|
|
26
|
+
font-family: var(--font-brand);
|
|
27
|
+
font-weight: var(--weight-light);
|
|
28
|
+
font-size: clamp(7rem, 30vw, 9.375rem);
|
|
29
|
+
line-height: 0.9;
|
|
30
|
+
letter-spacing: -0.04em;
|
|
31
|
+
color: var(--accent-color);
|
|
32
|
+
margin-block-end: var(--s0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.velu-404__title {
|
|
36
|
+
margin: 0 0 var(--s-1);
|
|
37
|
+
font-size: var(--f-h2);
|
|
38
|
+
font-weight: var(--weight-semibold);
|
|
39
|
+
letter-spacing: -0.02em;
|
|
40
|
+
line-height: 1.1;
|
|
41
|
+
color: var(--text-color);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.velu-404__body {
|
|
45
|
+
margin: 0 0 var(--s2);
|
|
46
|
+
max-inline-size: 27rem;
|
|
47
|
+
font-size: var(--f-body);
|
|
48
|
+
font-weight: var(--weight-light);
|
|
49
|
+
line-height: 1.55;
|
|
50
|
+
color: var(--muted-color);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.velu-404__actions {
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
justify-content: center;
|
|
57
|
+
flex-wrap: wrap;
|
|
58
|
+
gap: var(--s-1);
|
|
59
|
+
margin-block-start: var(--s-2);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.velu-404__btn {
|
|
63
|
+
display: inline-flex;
|
|
64
|
+
align-items: center;
|
|
65
|
+
gap: var(--s-2);
|
|
66
|
+
white-space: nowrap;
|
|
67
|
+
padding: var(--s-2) var(--s1);
|
|
68
|
+
border: var(--border-width) solid var(--border-color);
|
|
69
|
+
border-radius: var(--radius-md);
|
|
70
|
+
background: var(--page-bg);
|
|
71
|
+
color: var(--text-color);
|
|
72
|
+
font: inherit;
|
|
73
|
+
font-size: var(--f-h6);
|
|
74
|
+
font-weight: var(--weight-medium);
|
|
75
|
+
text-decoration: none;
|
|
76
|
+
cursor: pointer;
|
|
77
|
+
transition: border-color 0.12s ease, background 0.12s ease;
|
|
78
|
+
}
|
|
79
|
+
.velu-404__btn:hover {
|
|
80
|
+
border-color: var(--accent-color);
|
|
81
|
+
}
|
|
82
|
+
.velu-404__btn--primary {
|
|
83
|
+
background: var(--accent-color);
|
|
84
|
+
border-color: var(--accent-color);
|
|
85
|
+
color: #fff;
|
|
86
|
+
font-weight: var(--weight-bold);
|
|
87
|
+
}
|
|
88
|
+
.velu-404__btn--primary:hover {
|
|
89
|
+
background: color-mix(in srgb, var(--accent-color) 88%, #000);
|
|
90
|
+
}
|
|
91
|
+
.velu-404__btn-ic {
|
|
92
|
+
display: inline-flex;
|
|
93
|
+
align-items: center;
|
|
94
|
+
}
|
|
@@ -16,6 +16,12 @@
|
|
|
16
16
|
.velu-content-foot .velu-powered-by {
|
|
17
17
|
margin: 0;
|
|
18
18
|
padding: 0;
|
|
19
|
+
/* Fill the row so the badge's own `justify-content: flex-end` right-aligns
|
|
20
|
+
it — with or without the social icons as a sibling. (Avoids a
|
|
21
|
+
`margin-inline-start: auto` reset, which the production CSS minifier folds
|
|
22
|
+
away when it follows `margin: 0`, leaving the lone badge stuck at the
|
|
23
|
+
start in the build but not in dev.) */
|
|
24
|
+
flex: 1;
|
|
19
25
|
}
|
|
20
26
|
.velu-content-foot .velu-social {
|
|
21
27
|
color: var(--muted-color);
|
package/runtime/velu-ui/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export { default as Cluster } from './primitives/Cluster.jsx';
|
|
|
4
4
|
export { default as ThemeToggle } from './components/ThemeToggle.jsx';
|
|
5
5
|
export { default as Sidebar } from './components/Sidebar.jsx';
|
|
6
6
|
export { default as NavSelect } from './components/NavSelect.jsx';
|
|
7
|
+
export { default as ContextMenu } from './components/ContextMenu.jsx';
|
|
7
8
|
export { default as Toc } from './components/Toc.jsx';
|
|
8
9
|
export { default as TocBar } from './components/TocBar.jsx';
|
|
9
10
|
export { default as Callout } from './components/Callout.jsx';
|
|
@@ -28,6 +29,7 @@ export { default as ApiClient } from './components/ApiClient.jsx';
|
|
|
28
29
|
export { default as ApiSidebar } from './components/ApiSidebar.jsx';
|
|
29
30
|
export { default as ApiReferencePage } from './components/ApiReferencePage.jsx';
|
|
30
31
|
export { default as ApiSamples } from './components/ApiSamples.jsx';
|
|
32
|
+
export { default as NotFound } from './components/NotFound.jsx';
|
|
31
33
|
export { default as AskBar } from './components/AskBar.jsx';
|
|
32
34
|
export { default as Chatbot } from './components/Chatbot.jsx';
|
|
33
35
|
export { default as PageFeedback } from './components/PageFeedback.jsx';
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Brand glyphs for the contextual menu's "Open in <tool>" / "Connect to <IDE>"
|
|
5
|
+
* actions. Sourced from svgl.app and inlined (the project inlines icons as JSX,
|
|
6
|
+
* like lucide — no SVG loader). Each takes a `size` prop, mirroring lucide's
|
|
7
|
+
* icon API so they're drop-in in the menu.
|
|
8
|
+
*
|
|
9
|
+
* Monochrome marks (OpenAI, Cursor) use `currentColor` so they follow the menu
|
|
10
|
+
* text/hover color across light + dark. Multi-color marks (Claude, Perplexity,
|
|
11
|
+
* VS Code) keep their brand colors — legible on both themes.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export function MarkdownIcon({ size = '1em', ...rest }) {
|
|
15
|
+
return (
|
|
16
|
+
<svg width={size} height={size} viewBox="0 0 208 128" fill="none" aria-hidden="true" {...rest}>
|
|
17
|
+
<path
|
|
18
|
+
fill="none"
|
|
19
|
+
stroke="currentColor"
|
|
20
|
+
strokeWidth="10"
|
|
21
|
+
d="M15 5h178a10 10 0 0 1 10 10v98a10 10 0 0 1-10 10H15a10 10 0 0 1-10-10V15A10 10 0 0 1 15 5z"
|
|
22
|
+
/>
|
|
23
|
+
<path
|
|
24
|
+
fill="currentColor"
|
|
25
|
+
d="M30 98V30h20l20 25 20-25h20v68H90V59L70 84 50 59v39H30zm125 0-30-33h20V30h20v35h20l-30 33z"
|
|
26
|
+
/>
|
|
27
|
+
</svg>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function OpenAIIcon({ size = '1em', ...rest }) {
|
|
32
|
+
return (
|
|
33
|
+
<svg width={size} height={size} viewBox="0 0 256 260" fill="currentColor" aria-hidden="true" {...rest}>
|
|
34
|
+
<path d="M239.184 106.203a64.716 64.716 0 0 0-5.576-53.103C219.452 28.459 191 15.784 163.213 21.74A65.586 65.586 0 0 0 52.096 45.22a64.716 64.716 0 0 0-43.23 31.36c-14.31 24.602-11.061 55.634 8.033 76.74a64.665 64.665 0 0 0 5.525 53.102c14.174 24.65 42.644 37.324 70.446 31.36a64.72 64.72 0 0 0 48.754 21.744c28.481.025 53.714-18.361 62.414-45.481a64.767 64.767 0 0 0 43.229-31.36c14.137-24.558 10.875-55.423-8.083-76.483Zm-97.56 136.338a48.397 48.397 0 0 1-31.105-11.255l1.535-.87 51.67-29.825a8.595 8.595 0 0 0 4.247-7.367v-72.85l21.845 12.636c.218.111.37.32.409.563v60.367c-.056 26.818-21.783 48.545-48.601 48.601Zm-104.466-44.61a48.345 48.345 0 0 1-5.781-32.589l1.534.921 51.722 29.826a8.339 8.339 0 0 0 8.441 0l63.181-36.425v25.221a.87.87 0 0 1-.358.665l-52.335 30.184c-23.257 13.398-52.97 5.431-66.404-17.803ZM23.549 85.38a48.499 48.499 0 0 1 25.58-21.333v61.39a8.288 8.288 0 0 0 4.195 7.316l62.874 36.272-21.845 12.636a.819.819 0 0 1-.767 0L41.353 151.53c-23.211-13.454-31.171-43.144-17.804-66.405v.256Zm179.466 41.695-63.08-36.63L161.73 77.86a.819.819 0 0 1 .768 0l52.233 30.184a48.6 48.6 0 0 1-7.316 87.635v-61.391a8.544 8.544 0 0 0-4.4-7.213Zm21.742-32.69-1.535-.922-51.619-30.081a8.39 8.39 0 0 0-8.492 0L99.98 99.808V74.587a.716.716 0 0 1 .307-.665l52.233-30.133a48.652 48.652 0 0 1 72.236 50.391v.205ZM88.061 139.097l-21.845-12.585a.87.87 0 0 1-.41-.614V65.685a48.652 48.652 0 0 1 79.757-37.346l-1.535.87-51.67 29.825a8.595 8.595 0 0 0-4.246 7.367l-.051 72.697Zm11.868-25.58 28.138-16.217 28.188 16.218v32.434l-28.086 16.218-28.188-16.218-.052-32.434Z" />
|
|
35
|
+
</svg>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function ClaudeIcon({ size = '1em', ...rest }) {
|
|
40
|
+
return (
|
|
41
|
+
<svg width={size} height={size} viewBox="0 0 256 257" aria-hidden="true" {...rest}>
|
|
42
|
+
<path fill="#D97757" d="m50.228 170.321 50.357-28.257.843-2.463-.843-1.361h-2.462l-8.426-.518-28.775-.778-24.952-1.037-24.175-1.296-6.092-1.297L0 125.796l.583-3.759 5.12-3.434 7.324.648 16.202 1.101 24.304 1.685 17.629 1.037 26.118 2.722h4.148l.583-1.685-1.426-1.037-1.101-1.037-25.147-17.045-27.22-18.017-14.258-10.37-7.713-5.25-3.888-4.925-1.685-10.758 7-7.713 9.397.649 2.398.648 9.527 7.323 20.35 15.75L94.817 91.9l3.889 3.24 1.555-1.102.195-.777-1.75-2.917-14.453-26.118-15.425-26.572-6.87-11.018-1.814-6.61c-.648-2.723-1.102-4.991-1.102-7.778l7.972-10.823L71.42 0 82.05 1.426l4.472 3.888 6.61 15.101 10.694 23.786 16.591 32.34 4.861 9.592 2.592 8.879.973 2.722h1.685v-1.556l1.36-18.211 2.528-22.36 2.463-28.776.843-8.1 4.018-9.722 7.971-5.25 6.222 2.981 5.12 7.324-.713 4.73-3.046 19.768-5.962 30.98-3.889 20.739h2.268l2.593-2.593 10.499-13.934 17.628-22.036 7.778-8.749 9.073-9.657 5.833-4.601h11.018l8.1 12.055-3.628 12.443-11.342 14.388-9.398 12.184-13.48 18.147-8.426 14.518.778 1.166 2.01-.194 30.46-6.481 16.462-2.982 19.637-3.37 8.88 4.148.971 4.213-3.5 8.62-20.998 5.184-24.628 4.926-36.682 8.685-.454.324.519.648 16.526 1.555 7.065.389h17.304l32.21 2.398 8.426 5.574 5.055 6.805-.843 5.184-12.962 6.611-17.498-4.148-40.83-9.721-14-3.5h-1.944v1.167l11.666 11.406 21.387 19.314 26.767 24.887 1.36 6.157-3.434 4.86-3.63-.518-23.526-17.693-9.073-7.972-20.545-17.304h-1.36v1.814l4.73 6.935 25.017 37.59 1.296 11.536-1.814 3.76-6.481 2.268-7.13-1.297-14.647-20.544-15.1-23.138-12.185-20.739-1.49.843-7.194 77.448-3.37 3.953-7.778 2.981-6.48-4.925-3.436-7.972 3.435-15.749 4.148-20.544 3.37-16.333 3.046-20.285 1.815-6.74-.13-.454-1.49.194-15.295 20.999-23.267 31.433-18.406 19.702-4.407 1.75-7.648-3.954.713-7.064 4.277-6.286 25.47-32.405 15.36-20.092 9.917-11.6-.065-1.686h-.583L44.07 198.125l-12.055 1.555-5.185-4.86.648-7.972 2.463-2.593 20.35-13.999-.064.065Z" />
|
|
43
|
+
</svg>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function PerplexityIcon({ size = '1em', ...rest }) {
|
|
48
|
+
return (
|
|
49
|
+
<svg width={size} height={size} viewBox="0 0 48 48" fill="none" aria-hidden="true" {...rest}>
|
|
50
|
+
<g stroke="#20808d" strokeLinecap="round" strokeLinejoin="round">
|
|
51
|
+
<path d="M24 4.5v39M13.73 16.573v-9.99L24 16.573m0 14.5L13.73 41.417V27.01L24 16.573m0 0l10.27-9.99v9.99" />
|
|
52
|
+
<path d="M13.73 31.396H9.44V16.573h29.12v14.823h-4.29" />
|
|
53
|
+
<path d="M24 16.573L34.27 27.01v14.407L24 31.073" />
|
|
54
|
+
</g>
|
|
55
|
+
</svg>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function CursorIcon({ size = '1em', ...rest }) {
|
|
60
|
+
return (
|
|
61
|
+
<svg width={size} height={size} viewBox="0 0 466.73 532.09" fill="currentColor" aria-hidden="true" {...rest}>
|
|
62
|
+
<path d="M457.43,125.94L244.42,2.96c-6.84-3.95-15.28-3.95-22.12,0L9.3,125.94c-5.75,3.32-9.3,9.46-9.3,16.11v247.99c0,6.65,3.55,12.79,9.3,16.11l213.01,122.98c6.84,3.95,15.28,3.95,22.12,0l213.01-122.98c5.75-3.32,9.3-9.46,9.3-16.11v-247.99c0-6.65-3.55-12.79-9.3-16.11h-.01ZM444.05,151.99l-205.63,356.16c-1.39,2.4-5.06,1.42-5.06-1.36v-233.21c0-4.66-2.49-8.97-6.53-11.31L24.87,145.67c-2.4-1.39-1.42-5.06,1.36-5.06h411.26c5.84,0,9.49,6.33,6.57,11.39h-.01Z" />
|
|
63
|
+
</svg>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function VscodeIcon({ size = '1em', ...rest }) {
|
|
68
|
+
return (
|
|
69
|
+
<svg width={size} height={size} viewBox="0 0 100 100" fill="none" aria-hidden="true" {...rest}>
|
|
70
|
+
<mask
|
|
71
|
+
id="velu-vsc-mask"
|
|
72
|
+
width="100"
|
|
73
|
+
height="100"
|
|
74
|
+
x="0"
|
|
75
|
+
y="0"
|
|
76
|
+
maskUnits="userSpaceOnUse"
|
|
77
|
+
style={{ maskType: 'alpha' }}
|
|
78
|
+
>
|
|
79
|
+
<path
|
|
80
|
+
fill="#fff"
|
|
81
|
+
fillRule="evenodd"
|
|
82
|
+
clipRule="evenodd"
|
|
83
|
+
d="M70.912 99.317a6.223 6.223 0 0 0 4.96-.19l20.589-9.907A6.25 6.25 0 0 0 100 83.587V16.413a6.25 6.25 0 0 0-3.54-5.632L75.874.874a6.226 6.226 0 0 0-7.104 1.21L29.355 38.04 12.187 25.01a4.162 4.162 0 0 0-5.318.236l-5.506 5.009a4.168 4.168 0 0 0-.004 6.162L16.247 50 1.36 63.583a4.168 4.168 0 0 0 .004 6.162l5.506 5.01a4.162 4.162 0 0 0 5.318.236l17.168-13.032L68.77 97.917a6.217 6.217 0 0 0 2.143 1.4ZM75.015 27.3 45.11 50l29.906 22.701V27.3Z"
|
|
84
|
+
/>
|
|
85
|
+
</mask>
|
|
86
|
+
<g mask="url(#velu-vsc-mask)">
|
|
87
|
+
<path
|
|
88
|
+
fill="#0065A9"
|
|
89
|
+
d="M96.461 10.796 75.857.876a6.23 6.23 0 0 0-7.107 1.207l-67.451 61.5a4.167 4.167 0 0 0 .004 6.162l5.51 5.009a4.167 4.167 0 0 0 5.32.236l81.228-61.62c2.725-2.067 6.639-.124 6.639 3.297v-.24a6.25 6.25 0 0 0-3.539-5.63Z"
|
|
90
|
+
/>
|
|
91
|
+
<path
|
|
92
|
+
fill="#007ACC"
|
|
93
|
+
d="m96.461 89.204-20.604 9.92a6.229 6.229 0 0 1-7.107-1.207l-67.451-61.5a4.167 4.167 0 0 1 .004-6.162l5.51-5.009a4.167 4.167 0 0 1 5.32-.236l81.228 61.62c2.725 2.067 6.639.124 6.639-3.297v.24a6.25 6.25 0 0 1-3.539 5.63Z"
|
|
94
|
+
/>
|
|
95
|
+
<path
|
|
96
|
+
fill="#1F9CF0"
|
|
97
|
+
d="M75.858 99.126a6.232 6.232 0 0 1-7.108-1.21c2.306 2.307 6.25.674 6.25-2.588V4.672c0-3.262-3.944-4.895-6.25-2.589a6.232 6.232 0 0 1 7.108-1.21l20.6 9.908A6.25 6.25 0 0 1 100 16.413v67.174a6.25 6.25 0 0 1-3.541 5.633l-20.601 9.906Z"
|
|
98
|
+
/>
|
|
99
|
+
</g>
|
|
100
|
+
</svg>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
@import './primitives/switcher.css';
|
|
22
22
|
@import './components/sidebar.css';
|
|
23
23
|
@import './components/nav-select.css';
|
|
24
|
+
@import './components/context-menu.css';
|
|
24
25
|
@import './components/accordion.css';
|
|
25
26
|
@import './components/card.css';
|
|
26
27
|
@import './components/image.css';
|
|
@@ -31,6 +32,7 @@
|
|
|
31
32
|
@import './components/tree.css';
|
|
32
33
|
@import './components/api.css';
|
|
33
34
|
@import './components/api-page.css';
|
|
35
|
+
@import './components/not-found.css';
|
|
34
36
|
@import './components/ask-bar.css';
|
|
35
37
|
@import './components/chatbot.css';
|
|
36
38
|
@import './components/page-feedback.css';
|
package/schema/velu.schema.json
CHANGED
|
@@ -40,43 +40,7 @@
|
|
|
40
40
|
"type": "string",
|
|
41
41
|
"description": "Path to the favicon, relative to the project root."
|
|
42
42
|
},
|
|
43
|
-
"api": {
|
|
44
|
-
"type": "object",
|
|
45
|
-
"additionalProperties": false,
|
|
46
|
-
"description": "API reference behaviour for OpenAPI-generated pages.",
|
|
47
|
-
"properties": {
|
|
48
|
-
"server": {
|
|
49
|
-
"type": "string",
|
|
50
|
-
"description": "Base URL override for the playground + code samples (otherwise the spec's first server is used)."
|
|
51
|
-
},
|
|
52
|
-
"playground": {
|
|
53
|
-
"type": "object",
|
|
54
|
-
"additionalProperties": false,
|
|
55
|
-
"properties": {
|
|
56
|
-
"display": {
|
|
57
|
-
"type": "string",
|
|
58
|
-
"enum": ["interactive", "simple", "none"],
|
|
59
|
-
"description": "Playground mode. \"none\" disables the proxy / live send."
|
|
60
|
-
},
|
|
61
|
-
"proxy": {
|
|
62
|
-
"type": "boolean",
|
|
63
|
-
"description": "Send Try-It requests through the dev proxy to avoid CORS (default true)."
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
"examples": {
|
|
68
|
-
"type": "object",
|
|
69
|
-
"additionalProperties": false,
|
|
70
|
-
"properties": {
|
|
71
|
-
"languages": {
|
|
72
|
-
"type": "array",
|
|
73
|
-
"items": { "type": "string", "enum": ["curl", "javascript", "python", "ruby"] },
|
|
74
|
-
"description": "Which request-snippet languages to generate."
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
},
|
|
43
|
+
"api": { "$ref": "#/$defs/api" },
|
|
80
44
|
"logo": {
|
|
81
45
|
"description": "Site logo shown in the header, replacing the name wordmark. A single path used in both themes, or per-theme light/dark images with an optional click-through href. Paths are relative to the project root.",
|
|
82
46
|
"oneOf": [
|
|
@@ -206,6 +170,78 @@
|
|
|
206
170
|
"navigation": {
|
|
207
171
|
"$ref": "#/$defs/navContainer",
|
|
208
172
|
"description": "Site navigation. Mintlify-compatible: products > versions > languages > tabs > groups > pages. Switchable axes (product/version/language) become URL path prefixes; the default value of each is unprefixed. Dropdowns are intentionally unsupported."
|
|
173
|
+
},
|
|
174
|
+
"url": {
|
|
175
|
+
"type": "string",
|
|
176
|
+
"description": "Production site origin (e.g. \"https://docs.example.com\"), no trailing slash. Used by the static build for canonical URLs, og:url, and sitemap.xml, and by llms.txt for absolute links."
|
|
177
|
+
},
|
|
178
|
+
"thumbnails": {
|
|
179
|
+
"type": "object",
|
|
180
|
+
"additionalProperties": false,
|
|
181
|
+
"description": "Auto-generated OG (social share) image options. Build-only.",
|
|
182
|
+
"properties": {
|
|
183
|
+
"background": {
|
|
184
|
+
"type": "string",
|
|
185
|
+
"description": "Project-relative path to a background image for the OG cards (e.g. \"/images/og-background.png\"). Defaults to a brand-color glow."
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
"seo": {
|
|
190
|
+
"type": "object",
|
|
191
|
+
"additionalProperties": false,
|
|
192
|
+
"description": "Search-engine optimization. Build-only: meta tags and sitemap behaviour applied when the site is built (not in the dev preview).",
|
|
193
|
+
"properties": {
|
|
194
|
+
"metatags": {
|
|
195
|
+
"type": "object",
|
|
196
|
+
"description": "Extra meta tags injected on every page, as a flat key→value map (e.g. \"google-site-verification\", \"og:image\").",
|
|
197
|
+
"additionalProperties": { "type": "string" }
|
|
198
|
+
},
|
|
199
|
+
"indexing": {
|
|
200
|
+
"type": "string",
|
|
201
|
+
"enum": ["navigable", "all"],
|
|
202
|
+
"description": "\"navigable\" (default) indexes only pages in navigation; \"all\" also includes hidden pages."
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
"contextual": {
|
|
207
|
+
"type": "object",
|
|
208
|
+
"additionalProperties": false,
|
|
209
|
+
"description": "Per-page context menu — the \"Copy Page\" split-button + dropdown of agent/IDE actions shown at the top of each page. Mintlify-compatible. Unset → copy, view, chatgpt, claude.",
|
|
210
|
+
"required": ["options"],
|
|
211
|
+
"properties": {
|
|
212
|
+
"options": {
|
|
213
|
+
"type": "array",
|
|
214
|
+
"description": "Menu items, in order. Known string options, or custom { title, description, href } entries.",
|
|
215
|
+
"items": {
|
|
216
|
+
"oneOf": [
|
|
217
|
+
{
|
|
218
|
+
"type": "string",
|
|
219
|
+
"enum": [
|
|
220
|
+
"copy", "assistant", "view", "download-pdf", "download-spec",
|
|
221
|
+
"chatgpt", "claude", "perplexity", "grok", "aistudio", "devin", "windsurf",
|
|
222
|
+
"mcp", "add-mcp", "cursor", "vscode", "devin-mcp"
|
|
223
|
+
]
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"type": "object",
|
|
227
|
+
"additionalProperties": false,
|
|
228
|
+
"required": ["title", "href"],
|
|
229
|
+
"properties": {
|
|
230
|
+
"title": { "type": "string", "description": "Menu item label." },
|
|
231
|
+
"description": { "type": "string", "description": "Secondary line under the label." },
|
|
232
|
+
"href": { "type": "string", "description": "URL to open." },
|
|
233
|
+
"icon": { "type": "string", "description": "Lucide icon id." }
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
]
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
"display": {
|
|
240
|
+
"type": "string",
|
|
241
|
+
"enum": ["header", "toc"],
|
|
242
|
+
"description": "Where the menu renders. Default \"header\"."
|
|
243
|
+
}
|
|
244
|
+
}
|
|
209
245
|
}
|
|
210
246
|
},
|
|
211
247
|
"$defs": {
|
|
@@ -243,6 +279,7 @@
|
|
|
243
279
|
"expanded": { "type": "boolean", "description": "Start expanded." },
|
|
244
280
|
"root": { "type": "string", "description": "Landing page path for the group." },
|
|
245
281
|
"openapi": { "$ref": "#/$defs/openapiRef" },
|
|
282
|
+
"api": { "$ref": "#/$defs/api" },
|
|
246
283
|
"pages": { "$ref": "#/$defs/pages" }
|
|
247
284
|
}
|
|
248
285
|
},
|
|
@@ -255,11 +292,49 @@
|
|
|
255
292
|
"icon": { "type": "string" },
|
|
256
293
|
"href": { "type": "string", "description": "External/override link instead of in-site pages." },
|
|
257
294
|
"openapi": { "$ref": "#/$defs/openapiRef" },
|
|
295
|
+
"api": { "$ref": "#/$defs/api" },
|
|
258
296
|
"anchors": { "type": "array", "items": { "$ref": "#/$defs/anchor" } },
|
|
259
297
|
"groups": { "type": "array", "items": { "$ref": "#/$defs/group" } },
|
|
260
298
|
"pages": { "$ref": "#/$defs/pages" }
|
|
261
299
|
}
|
|
262
300
|
},
|
|
301
|
+
"api": {
|
|
302
|
+
"type": "object",
|
|
303
|
+
"additionalProperties": false,
|
|
304
|
+
"description": "API reference behaviour for OpenAPI-generated pages. Set at the root, or on a tab/group with `openapi` to override it for just that section.",
|
|
305
|
+
"properties": {
|
|
306
|
+
"server": {
|
|
307
|
+
"type": "string",
|
|
308
|
+
"description": "Base URL override for the playground + code samples (otherwise the spec's first server is used)."
|
|
309
|
+
},
|
|
310
|
+
"playground": {
|
|
311
|
+
"type": "object",
|
|
312
|
+
"additionalProperties": false,
|
|
313
|
+
"properties": {
|
|
314
|
+
"display": {
|
|
315
|
+
"type": "string",
|
|
316
|
+
"enum": ["interactive", "simple", "none"],
|
|
317
|
+
"description": "Playground mode. \"none\" disables the proxy / live send."
|
|
318
|
+
},
|
|
319
|
+
"proxy": {
|
|
320
|
+
"type": "boolean",
|
|
321
|
+
"description": "Send Try-It requests through the dev proxy to avoid CORS (default true)."
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
"examples": {
|
|
326
|
+
"type": "object",
|
|
327
|
+
"additionalProperties": false,
|
|
328
|
+
"properties": {
|
|
329
|
+
"languages": {
|
|
330
|
+
"type": "array",
|
|
331
|
+
"items": { "type": "string", "enum": ["curl", "javascript", "python", "ruby"] },
|
|
332
|
+
"description": "Which request-snippet languages to generate."
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
},
|
|
263
338
|
"openapiRef": {
|
|
264
339
|
"description": "Path or URL to an OpenAPI spec (JSON/YAML), or an array of them. Auto-generates a page per operation, grouped by tag.",
|
|
265
340
|
"oneOf": [
|
package/src/runtime/App.jsx
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
NavSelect,
|
|
15
15
|
Toc,
|
|
16
16
|
TocBar,
|
|
17
|
+
ContextMenu,
|
|
17
18
|
Callout,
|
|
18
19
|
Accordion,
|
|
19
20
|
AccordionGroup,
|
|
@@ -48,6 +49,7 @@ import {
|
|
|
48
49
|
ApiField,
|
|
49
50
|
ApiSidebar,
|
|
50
51
|
ApiSamples,
|
|
52
|
+
NotFound,
|
|
51
53
|
VeluMark,
|
|
52
54
|
} from 'velu-ui';
|
|
53
55
|
import { X, ChevronDown, ChevronUp } from 'lucide-react';
|
|
@@ -727,6 +729,9 @@ function DocsPage() {
|
|
|
727
729
|
const frontmatter = entry?.frontmatter ?? {};
|
|
728
730
|
const PageComponent = entry?.Component ?? null;
|
|
729
731
|
const pageToc = entry?.toc ?? [];
|
|
732
|
+
// No renderable page for this route → show the 404 page (a clean centered
|
|
733
|
+
// takeover: header + footer stay, the docs sidebar/TOC are hidden).
|
|
734
|
+
const isNotFound = !PageComponent;
|
|
730
735
|
|
|
731
736
|
// Switcher option sets (only render a switcher when an axis has >1
|
|
732
737
|
// option). Anchors are pinned sidebar links shown in the context zone.
|
|
@@ -763,6 +768,27 @@ function DocsPage() {
|
|
|
763
768
|
/>
|
|
764
769
|
);
|
|
765
770
|
|
|
771
|
+
// Keep document.title / meta description / <html lang> in sync on client-side
|
|
772
|
+
// navigation. The HTML is SSR'd with a per-page (build) or generic (dev) title;
|
|
773
|
+
// without this the tab title would stay frozen on the first page as you
|
|
774
|
+
// navigate the SPA, and JS-executing crawlers would read the stale value.
|
|
775
|
+
React.useEffect(() => {
|
|
776
|
+
const t = frontmatter.title;
|
|
777
|
+
document.title = isNotFound
|
|
778
|
+
? `Page not found - ${site.name}`
|
|
779
|
+
: t && t !== site.name
|
|
780
|
+
? `${t} - ${site.name}`
|
|
781
|
+
: site.name;
|
|
782
|
+
let descTag = document.querySelector('meta[name="description"]');
|
|
783
|
+
if (!descTag) {
|
|
784
|
+
descTag = document.createElement('meta');
|
|
785
|
+
descTag.setAttribute('name', 'description');
|
|
786
|
+
document.head.appendChild(descTag);
|
|
787
|
+
}
|
|
788
|
+
descTag.setAttribute('content', frontmatter.description || '');
|
|
789
|
+
document.documentElement.lang = nav?.activeLanguageCode || 'en';
|
|
790
|
+
}, [pathname, isNotFound, frontmatter.title, frontmatter.description, nav?.activeLanguageCode]);
|
|
791
|
+
|
|
766
792
|
// Frontmatter title needs an id so scroll-spy + click-to-scroll work
|
|
767
793
|
// against it like any other heading.
|
|
768
794
|
const pageId = React.useMemo(() => {
|
|
@@ -1059,6 +1085,7 @@ function DocsPage() {
|
|
|
1059
1085
|
data-sidebar-open={sidebarOpen ? 'true' : 'false'}
|
|
1060
1086
|
data-drawer-open={drawerOpen ? 'true' : 'false'}
|
|
1061
1087
|
data-api={entry?.api ? 'true' : 'false'}
|
|
1088
|
+
data-not-found={isNotFound ? 'true' : undefined}
|
|
1062
1089
|
>
|
|
1063
1090
|
{/* Scrim — visible at mobile while the drawer OR the chatbot
|
|
1064
1091
|
sheet is open. Sits between the article (z-0) and the
|
|
@@ -1353,6 +1380,21 @@ function DocsPage() {
|
|
|
1353
1380
|
and the right margin collapses to 0 while the chatbot is
|
|
1354
1381
|
open via [data-chat-open="true"]). */}
|
|
1355
1382
|
<div className="velu-docs-layout__center">
|
|
1383
|
+
{isNotFound ? (
|
|
1384
|
+
<main className="velu-404-main">
|
|
1385
|
+
<NotFound
|
|
1386
|
+
homeHref="/"
|
|
1387
|
+
linkComponent={RouterLink}
|
|
1388
|
+
onSearch={() =>
|
|
1389
|
+
window.dispatchEvent(
|
|
1390
|
+
new KeyboardEvent('keydown', { key: 'k', ctrlKey: true, metaKey: true }),
|
|
1391
|
+
)
|
|
1392
|
+
}
|
|
1393
|
+
onAskAI={IS_DEV_PREVIEW ? undefined : () => askAI('')}
|
|
1394
|
+
/>
|
|
1395
|
+
</main>
|
|
1396
|
+
) : (
|
|
1397
|
+
<>
|
|
1356
1398
|
{/* Narrow-layout TOC bar — always in DOM, only visible at
|
|
1357
1399
|
< 1024px (toggled by @container in toc-bar.css). Shares
|
|
1358
1400
|
its `items` + `activeId` + `onSelect` API with the
|
|
@@ -1396,6 +1438,24 @@ function DocsPage() {
|
|
|
1396
1438
|
</span>
|
|
1397
1439
|
</button>
|
|
1398
1440
|
<div className="velu-docs-layout__article">
|
|
1441
|
+
{/* Per-page agent/IDE action bar: the section eyebrow + a
|
|
1442
|
+
"Copy Page" split-button whose dropdown is driven by the
|
|
1443
|
+
Mintlify-compatible `contextual` config. Renders nothing
|
|
1444
|
+
when there's neither an eyebrow nor any enabled options. */}
|
|
1445
|
+
<ContextMenu
|
|
1446
|
+
eyebrow={
|
|
1447
|
+
nav?.breadcrumb && nav.breadcrumb.length > 1
|
|
1448
|
+
? nav.breadcrumb[nav.breadcrumb.length - 2].label
|
|
1449
|
+
: undefined
|
|
1450
|
+
}
|
|
1451
|
+
pageUrl={pathname}
|
|
1452
|
+
title={frontmatter.title}
|
|
1453
|
+
isApi={entry?.api === true}
|
|
1454
|
+
siteName={site.name}
|
|
1455
|
+
options={site.contextual?.options ?? []}
|
|
1456
|
+
onAssistant={IS_DEV_PREVIEW ? undefined : () => askAI('')}
|
|
1457
|
+
/>
|
|
1458
|
+
|
|
1399
1459
|
{/* Page hero from MDX frontmatter. `.velu-hero` rules
|
|
1400
1460
|
(in base.css) keep the title and description tightly
|
|
1401
1461
|
grouped and create a clear break before the prose body. */}
|
|
@@ -1494,6 +1554,8 @@ function DocsPage() {
|
|
|
1494
1554
|
)}
|
|
1495
1555
|
</div>
|
|
1496
1556
|
</main>
|
|
1557
|
+
</>
|
|
1558
|
+
)}
|
|
1497
1559
|
</div>
|
|
1498
1560
|
|
|
1499
1561
|
{/* Full site footer — only when the config provides link columns. Spans
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://veludocs.com/velu.schema.json",
|
|
3
3
|
"name": "Starter",
|
|
4
|
+
"url": "https://veludocs.com",
|
|
4
5
|
"colors": {
|
|
5
6
|
"primary": "#dc143c"
|
|
6
7
|
},
|
|
7
8
|
"favicon": "/favicon.svg",
|
|
9
|
+
"contextual": {
|
|
10
|
+
"options": ["copy", "view", "chatgpt", "claude", "mcp", "cursor", "vscode"]
|
|
11
|
+
},
|
|
8
12
|
"navigation": {
|
|
9
13
|
"anchors": [
|
|
10
14
|
{ "anchor": "Documentation", "icon": "book-open", "href": "https://veludocs.com" },
|