@veluai/velu 0.2.41 → 0.2.43
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 +43 -43
- package/docs/kadhir.md +31 -0
- package/docs/mintlify-migration.md +2 -0
- package/package.json +3 -2
- package/runtime/velu-ui/base.css +4 -0
- package/runtime/velu-ui/components/ApiReferencePage.jsx +4 -4
- package/runtime/velu-ui/components/ApiSamples.jsx +2 -2
- package/runtime/velu-ui/components/Card.jsx +6 -1
- package/runtime/velu-ui/components/PageFooter.jsx +1 -1
- package/runtime/velu-ui/components/PageHeader.jsx +8 -19
- package/runtime/velu-ui/components/PoweredBy.jsx +2 -0
- package/runtime/velu-ui/components/VepaFooter.jsx +5 -4
- package/runtime/velu-ui/components/api-page.css +6 -6
- package/runtime/velu-ui/components/api.css +2 -2
- package/runtime/velu-ui/components/ask-bar.css +3 -3
- package/runtime/velu-ui/components/changelog-filters.css +5 -5
- package/runtime/velu-ui/components/chatbot.css +5 -5
- package/runtime/velu-ui/components/code-block.css +2 -2
- package/runtime/velu-ui/components/context-menu.css +1 -1
- package/runtime/velu-ui/components/docs-layout.css +3 -3
- package/runtime/velu-ui/components/field.css +1 -1
- package/runtime/velu-ui/components/image.css +1 -1
- package/runtime/velu-ui/components/not-found.css +1 -1
- package/runtime/velu-ui/components/page-feedback.css +5 -5
- package/runtime/velu-ui/components/page-footer.css +1 -1
- package/runtime/velu-ui/components/page-header.css +11 -3
- package/runtime/velu-ui/components/page-nav.css +1 -1
- package/runtime/velu-ui/components/powered-by.css +1 -1
- package/runtime/velu-ui/components/prompt.css +2 -2
- package/runtime/velu-ui/components/search.css +1 -1
- package/runtime/velu-ui/components/sidebar.css +2 -2
- package/runtime/velu-ui/components/toc-bar.css +2 -2
- package/runtime/velu-ui/components/update.css +1 -1
- package/runtime/velu-ui/lib/velu-mark.jsx +20 -0
- package/runtime/velu-ui/styles.css +13 -1
- package/runtime/velu-ui/themes/aalam.css +19 -8
- package/runtime/velu-ui/themes/kadhir.css +77 -0
- package/runtime/velu-ui/themes/nila.css +6 -1
- package/runtime/velu-ui/themes/thulir.css +240 -231
- package/runtime/velu-ui/themes/vepa.css +21 -10
- package/schema/velu.schema.json +1 -1
- package/src/runtime/App.jsx +49 -37
package/docs/kadhir.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Kadhir
|
|
2
|
+
|
|
3
|
+
Kadhir is Velu's independently implemented Luma equivalent. Mintlify migration maps `theme: "luma"` to `theme: "kadhir"`; native Velu sites can select Kadhir directly.
|
|
4
|
+
|
|
5
|
+
The implementation uses the existing Velu component and navigation infrastructure, with a new single-row 48px desktop header, pill tabs, compact search, a 248px sidebar, a 648px reading column, and a separate API sample rail. Mobile uses the accessible left drawer and on-page TOC. Pagination has 48px separation from content.
|
|
6
|
+
|
|
7
|
+
Reference: the cached npm renderer `@mintlify/client@0.0.3566`. Luma-specific classes were inspected in `.next/server/chunks/ssr/_6753cfba._.js` and `_450d9e92._.js`. These are reference inputs, not distributed code. Reference API data was generated using `@mintlify/validation@0.1.849` and its `openApiToSchemaGraph` export.
|
|
8
|
+
|
|
9
|
+
## Local comparison
|
|
10
|
+
|
|
11
|
+
The fresh fixture is `.incoming/kadhir/source`. It contains 220 pages, three products, two versions, API operations, component examples, and a custom stylesheet/script. The migrated output is `.incoming/kadhir/final`.
|
|
12
|
+
|
|
13
|
+
```powershell
|
|
14
|
+
node packages/velu-cli/scripts/build.mjs
|
|
15
|
+
node packages/velu-cli/dist/cli.js migrate mintlify .incoming/kadhir/source --out .incoming/kadhir/fresh-output
|
|
16
|
+
$env:PORT='8476'
|
|
17
|
+
node packages/velu-cli/dist/cli.js dev .incoming/kadhir/fresh-output
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Browser tests expect that fixture on port 8476:
|
|
21
|
+
|
|
22
|
+
```powershell
|
|
23
|
+
node --test packages/velu-cli/test/kadhir.test.mjs
|
|
24
|
+
node packages/velu-cli/test/kadhir-tabs-browser.mjs
|
|
25
|
+
node packages/velu-cli/test/kadhir-navigation-browser.mjs
|
|
26
|
+
node packages/velu-cli/test/kadhir-mobile-browser.mjs
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Tests cover tab clicks, API content, products/versions, reloads, long sidebars, drawer keyboard behavior, TOC clearance, and overflow. Screenshots and reference preparation scripts are under `.incoming/kadhir/`.
|
|
30
|
+
|
|
31
|
+
This is not a pixel-perfect guarantee. Icon artwork, syntax highlighting, generated response examples, component spacing, and arbitrary custom DOM selectors can differ. The fixture migration reports compatibility warnings for endpoint snapshots, component adapters, and CSS selectors; review those before deploying a migrated site.
|
|
@@ -163,3 +163,5 @@ Tests cover clean and review-required outputs, source immutability, JSONC/YAML
|
|
|
163
163
|
references, ignore negation, collisions/junctions, script preservation, AST
|
|
164
164
|
rewrites, assets, endpoint snapshots, generated-project validation, and CLI exit
|
|
165
165
|
codes. No network is needed for the migration tests.
|
|
166
|
+
|
|
167
|
+
Luma sites migrate to the Kadhir theme. See [Kadhir](kadhir.md) for layout details and verification.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veluai/velu",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.43",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": "./dist/cli.js",
|
|
6
6
|
"publishConfig": {
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"docs/vepa.md",
|
|
22
22
|
"docs/thulir.md",
|
|
23
23
|
"docs/aalam.md",
|
|
24
|
-
"docs/nila.md"
|
|
24
|
+
"docs/nila.md",
|
|
25
|
+
"docs/kadhir.md"
|
|
25
26
|
],
|
|
26
27
|
"scripts": {
|
|
27
28
|
"build": "node scripts/build.mjs",
|
package/runtime/velu-ui/base.css
CHANGED
|
@@ -107,6 +107,10 @@
|
|
|
107
107
|
--font-sans: "Google Sans Flex", sans-serif;
|
|
108
108
|
--font-mono: "Google Sans Code", ui-monospace, SFMono-Regular, monospace;
|
|
109
109
|
--font-brand: "Outfit", var(--font-sans);
|
|
110
|
+
/* Brand wordmark (header site name, footer credits) — always
|
|
111
|
+
Google Sans Flex on every theme for a consistent Velu identity,
|
|
112
|
+
even when a project overrides --font-sans. */
|
|
113
|
+
--font-wordmark: "Google Sans Flex", var(--font-sans);
|
|
110
114
|
font-family: var(--font-sans);
|
|
111
115
|
|
|
112
116
|
/* Approx height of the docs PageHeader (used as the offset for sticky
|
|
@@ -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' || ['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>}
|
|
81
|
+
{(preset === 'vepa' || ['thulir', 'aalam', 'nila', 'kadhir'].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,7 +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
|
+
{['thulir', 'aalam', 'nila', 'kadhir'].includes(preset) && operation.body.contentType && <span className="velu-api-body-type">{operation.body.contentType}</span>}
|
|
118
118
|
{operation.body.fields.map((f, i) => (
|
|
119
119
|
<Field key={i} name={f.name} type={f.type} required={f.required}>
|
|
120
120
|
{f.description}
|
|
@@ -180,10 +180,10 @@ function EnumHint({ values }) {
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
function ResponseSections({ responses, preset }) {
|
|
183
|
-
if (preset === 'vepa' || ['thulir', 'aalam', 'nila'].includes(preset)) return <div className="velu-vepa-responses">{responses.map((r, i) => <section key={i}>
|
|
183
|
+
if (preset === 'vepa' || ['thulir', 'aalam', 'nila', 'kadhir'].includes(preset)) return <div className="velu-vepa-responses">{responses.map((r, i) => <section key={i}>
|
|
184
184
|
<div className="velu-vepa-responses__status">{r.status}{r.contentType && ` · ${r.contentType}`}</div>
|
|
185
185
|
{r.description && <p>{r.description}</p>}
|
|
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
|
+
{(['thulir', 'aalam', 'nila', 'kadhir'].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>)}
|
|
187
187
|
</section>)}</div>;
|
|
188
188
|
return (
|
|
189
189
|
<AccordionGroup className="velu-api-resp">
|
|
@@ -14,7 +14,7 @@ export default function ApiSamples({ samples = [], responses = [], preset, title
|
|
|
14
14
|
const sample = samples.find((s) => s.key === selected) || samples[0];
|
|
15
15
|
// Mint displays JSON request bodies across lines. Keep custom/non-JSON
|
|
16
16
|
// snippets intact, and change whitespace only in a valid JSON body.
|
|
17
|
-
const sampleCode = ['thulir', 'aalam', 'nila'].includes(preset) && sample?.key === 'curl'
|
|
17
|
+
const sampleCode = ['thulir', 'aalam', 'nila', 'kadhir'].includes(preset) && sample?.key === 'curl'
|
|
18
18
|
? sample.code.replace(/(--data )'([\s\S]*)'$/, (original, prefix, body) => {
|
|
19
19
|
try { return `${prefix}'\n${JSON.stringify(JSON.parse(body), null, 2)}\n'`; }
|
|
20
20
|
catch { return original; }
|
|
@@ -22,7 +22,7 @@ export default function ApiSamples({ samples = [], responses = [], preset, title
|
|
|
22
22
|
const withExample = responses.filter((r) => r.example != null);
|
|
23
23
|
return (
|
|
24
24
|
<div className="velu-api-samples">
|
|
25
|
-
{sample && (preset === 'vepa' || ['thulir', 'aalam', 'nila'].includes(preset)) ? <section id={idPrefix + 'request-example'} className="velu-vepa-request">
|
|
25
|
+
{sample && (preset === 'vepa' || ['thulir', 'aalam', 'nila', 'kadhir'].includes(preset)) ? <section id={idPrefix + 'request-example'} className="velu-vepa-request">
|
|
26
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>
|
|
27
27
|
<CodeBlock language={sample.language}>{sampleCode}</CodeBlock>
|
|
28
28
|
</section> : samples.length > 0 && (
|
|
@@ -117,6 +117,11 @@ export function CardGroup({
|
|
|
117
117
|
className = '',
|
|
118
118
|
...rest
|
|
119
119
|
}) {
|
|
120
|
+
// Grid column count follows the child count (capped by limit) so a group
|
|
121
|
+
// never renders emptier than it is: 2 cards fill 2 columns instead of
|
|
122
|
+
// leaving a third one blank. Mintlify's CardGroup defaults to 2 columns.
|
|
123
|
+
const count = React.Children.count(children);
|
|
124
|
+
const cols = Math.max(1, Math.min(4, Math.min(Number(limit) || 2, count || 1)));
|
|
120
125
|
return (
|
|
121
126
|
<Switcher
|
|
122
127
|
as="card-group"
|
|
@@ -124,7 +129,7 @@ export function CardGroup({
|
|
|
124
129
|
space={space}
|
|
125
130
|
min={min}
|
|
126
131
|
limit={limit}
|
|
127
|
-
style={{ '--vepa-columns':
|
|
132
|
+
style={{ '--vepa-columns': cols }}
|
|
128
133
|
{...rest}
|
|
129
134
|
>
|
|
130
135
|
{children}
|
|
@@ -57,7 +57,7 @@ export default function PageFooter({
|
|
|
57
57
|
...rest
|
|
58
58
|
}) {
|
|
59
59
|
const Link = linkComponent;
|
|
60
|
-
if (preset === 'vepa' || ['thulir', 'aalam', 'nila'].includes(preset)) {
|
|
60
|
+
if (preset === 'vepa' || ['thulir', 'aalam', 'nila', 'kadhir'].includes(preset)) {
|
|
61
61
|
return <VepaFooter preset={preset} brand={brand} columns={columns} socials={socials} linkComponent={Link} />;
|
|
62
62
|
}
|
|
63
63
|
const brandLabel = brand?.label ?? 'Velu';
|
|
@@ -53,22 +53,7 @@ import Logo from './Logo.jsx';
|
|
|
53
53
|
mobile nav drawer) can render the mark alongside its own chrome.
|
|
54
54
|
Only the default theme shows it — the vepa / thulir / aalam / nila
|
|
55
55
|
presets hide it via CSS (text-only branding). */
|
|
56
|
-
export function VeluMark() {
|
|
57
|
-
return (
|
|
58
|
-
<svg
|
|
59
|
-
className="velu-header__mark"
|
|
60
|
-
viewBox="0 0 32 24"
|
|
61
|
-
fill="currentColor"
|
|
62
|
-
aria-hidden="true"
|
|
63
|
-
>
|
|
64
|
-
<path
|
|
65
|
-
fillRule="evenodd"
|
|
66
|
-
clipRule="evenodd"
|
|
67
|
-
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.70535 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"
|
|
68
|
-
/>
|
|
69
|
-
</svg>
|
|
70
|
-
);
|
|
71
|
-
}
|
|
56
|
+
export function VeluMark() { return null; }
|
|
72
57
|
|
|
73
58
|
function ActionButton({ action, Link, id }) {
|
|
74
59
|
const { label, icon, kind = 'outlined', href, onClick, external, component } = action;
|
|
@@ -277,11 +262,15 @@ export default function PageHeader({
|
|
|
277
262
|
Boolean(tabsTrailing) ||
|
|
278
263
|
Boolean(onMenuClick);
|
|
279
264
|
|
|
280
|
-
// A configured logo
|
|
265
|
+
// A configured logo renders alongside the wordmark (Mintlify-style
|
|
266
|
+
// headers show mark + name together).
|
|
281
267
|
const brandBlock = (
|
|
282
268
|
<Cluster space="var(--s-3)" align="center" className="velu-header__brand" style={{ flexWrap: 'nowrap' }}>
|
|
283
269
|
{brand?.logo ? (
|
|
284
|
-
|
|
270
|
+
<>
|
|
271
|
+
<Logo logo={brand.logo} alt="" />
|
|
272
|
+
<span className="velu-header__wordmark">{brandLabel}</span>
|
|
273
|
+
</>
|
|
285
274
|
) : (
|
|
286
275
|
<>
|
|
287
276
|
<VeluMark />
|
|
@@ -294,7 +283,7 @@ export default function PageHeader({
|
|
|
294
283
|
return (
|
|
295
284
|
<Stack
|
|
296
285
|
as="header"
|
|
297
|
-
space={preset === 'vepa' || ['thulir', 'aalam', 'nila'].includes(preset) ? '0px' : 'var(--s0)'}
|
|
286
|
+
space={preset === 'vepa' || ['thulir', 'aalam', 'nila', 'kadhir'].includes(preset) ? '0px' : 'var(--s0)'}
|
|
298
287
|
data-has-tabs={tabs.length > 0 ? 'true' : 'false'}
|
|
299
288
|
className={`velu-header${scrolled ? ' velu-header--scrolled' : ''}${
|
|
300
289
|
showTabsRow ? '' : ' velu-header--no-tabs'
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import VeluMark from '../lib/velu-mark.jsx';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* PoweredBy — "Powered by Velu" badge. Right-aligned, sits above the
|
|
@@ -23,6 +24,7 @@ export default function PoweredBy({
|
|
|
23
24
|
>
|
|
24
25
|
<span className="velu-powered-by__label">Powered by</span>
|
|
25
26
|
<span className="velu-powered-by__brand">
|
|
27
|
+
<VeluMark className="velu-powered-by__mark" />
|
|
26
28
|
<span className="velu-powered-by__wordmark">Velu</span>
|
|
27
29
|
</span>
|
|
28
30
|
</a>
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import Logo from './Logo.jsx';
|
|
3
|
+
import VeluMark from '../lib/velu-mark.jsx';
|
|
3
4
|
import SocialLinks from './SocialLinks.jsx';
|
|
4
5
|
import ThemePreferenceMenu from './ThemePreferenceMenu.jsx';
|
|
5
6
|
|
|
6
7
|
export default function VepaFooter({ preset = 'vepa', brand, columns = [], socials = [], linkComponent: Link = 'a' }) {
|
|
7
8
|
const single = columns.length === 1;
|
|
8
9
|
const completeHeadings = columns.every((c) => c.title);
|
|
9
|
-
const root = ['thulir', 'aalam', 'nila'].includes(preset) ? 'thulir-footer' : 'vepa-footer';
|
|
10
|
+
const root = ['thulir', 'aalam', 'nila', 'kadhir'].includes(preset) ? 'thulir-footer' : 'vepa-footer';
|
|
10
11
|
return <footer id="footer" className={root} data-layout={columns.length <= 3 ? 'compact' : 'expanded'} data-single={single} data-headings={completeHeadings}>
|
|
11
12
|
<div className={`${root}__inner`}>
|
|
12
13
|
<div className={`${root}__top`}>
|
|
13
14
|
<div className={`${root}__brand-column`}>
|
|
14
15
|
<Link href={brand?.href || '/'} className={`${root}__brand`}>
|
|
15
|
-
{brand?.logo ?
|
|
16
|
+
{brand?.logo ? <><Logo logo={brand.logo} alt="" /><span>{brand?.label || 'Velu'}</span></> : <span>{brand?.label || 'Velu'}</span>}
|
|
16
17
|
</Link>
|
|
17
|
-
{['thulir', 'aalam', 'nila'].includes(preset) && <SocialLinks socials={socials} className="thulir-footer__brand-mobile-socials" />}
|
|
18
|
+
{['thulir', 'aalam', 'nila', 'kadhir'].includes(preset) && <SocialLinks socials={socials} className="thulir-footer__brand-mobile-socials" />}
|
|
18
19
|
<SocialLinks socials={socials} className={`${root}__brand-socials`} />
|
|
19
20
|
</div>
|
|
20
21
|
<div className={`${root}__columns`} style={{ '--footer-columns': columns.length }}>
|
|
@@ -34,7 +35,7 @@ export default function VepaFooter({ preset = 'vepa', brand, columns = [], socia
|
|
|
34
35
|
{!completeHeadings && <SocialLinks socials={socials} className={`${root}__mobile-socials`} />}
|
|
35
36
|
<div className={`${root}__divider`} />
|
|
36
37
|
<div className={`${root}__bottom`}>
|
|
37
|
-
<a href="https://veludocs.com" target="_blank" rel="noreferrer" className={`${root}__powered`}>Powered by <strong>Velu</strong></a>
|
|
38
|
+
<a href="https://veludocs.com" target="_blank" rel="noreferrer" className={`${root}__powered`}>Powered by <VeluMark className={`${root}__powered-mark`} /><strong>Velu</strong></a>
|
|
38
39
|
<ThemePreferenceMenu inline />
|
|
39
40
|
</div>
|
|
40
41
|
</div>
|
|
@@ -164,7 +164,7 @@
|
|
|
164
164
|
justify-content: center;
|
|
165
165
|
padding: clamp(var(--s1), 6vh, var(--s5)) var(--s2);
|
|
166
166
|
overflow-y: auto;
|
|
167
|
-
background:
|
|
167
|
+
background: rgb(from var(--page-bg) r g b / 40%), rgba(0, 0, 0, 0.55);
|
|
168
168
|
backdrop-filter: blur(6px) saturate(1.1);
|
|
169
169
|
-webkit-backdrop-filter: blur(6px) saturate(1.1);
|
|
170
170
|
animation: velu-api-modal-in 140ms ease-out;
|
|
@@ -204,8 +204,8 @@
|
|
|
204
204
|
border-radius: var(--radius-xs, 4px);
|
|
205
205
|
text-transform: uppercase;
|
|
206
206
|
}
|
|
207
|
-
.velu-sidebar__method.velu-method-badge--get { color: var(--get-pill-color); background:
|
|
208
|
-
.velu-sidebar__method.velu-method-badge--post { color: var(--post-pill-color); background:
|
|
209
|
-
.velu-sidebar__method.velu-method-badge--put { color: var(--put-pill-color); background:
|
|
210
|
-
.velu-sidebar__method.velu-method-badge--patch { color: var(--patch-pill-color); background:
|
|
211
|
-
.velu-sidebar__method.velu-method-badge--delete { color: var(--delete-pill-color); background:
|
|
207
|
+
.velu-sidebar__method.velu-method-badge--get { color: var(--get-pill-color); background: rgb(from var(--get-pill-color) r g b / 14%); }
|
|
208
|
+
.velu-sidebar__method.velu-method-badge--post { color: var(--post-pill-color); background: rgb(from var(--post-pill-color) r g b / 14%); }
|
|
209
|
+
.velu-sidebar__method.velu-method-badge--put { color: var(--put-pill-color); background: rgb(from var(--put-pill-color) r g b / 14%); }
|
|
210
|
+
.velu-sidebar__method.velu-method-badge--patch { color: var(--patch-pill-color); background: rgb(from var(--patch-pill-color) r g b / 14%); }
|
|
211
|
+
.velu-sidebar__method.velu-method-badge--delete { color: var(--delete-pill-color); background: rgb(from var(--delete-pill-color) r g b / 14%); }
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
cursor: pointer;
|
|
195
195
|
}
|
|
196
196
|
.velu-try-it__cta:hover {
|
|
197
|
-
background:
|
|
197
|
+
background: rgb(from var(--method-cta, var(--accent-color)) calc(r * 0.88) calc(g * 0.88) calc(b * 0.88));
|
|
198
198
|
}
|
|
199
199
|
.velu-try-it__cta:disabled {
|
|
200
200
|
cursor: default;
|
|
@@ -528,7 +528,7 @@
|
|
|
528
528
|
align-items: center;
|
|
529
529
|
padding-block: 0;
|
|
530
530
|
padding-inline: var(--s-3);
|
|
531
|
-
background:
|
|
531
|
+
background: rgb(from var(--accent-color) r g b / 18%);
|
|
532
532
|
border-radius: var(--radius-sm);
|
|
533
533
|
font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
|
|
534
534
|
font-size: var(--f-h6);
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
border-color: var(--accent-color);
|
|
44
44
|
box-shadow:
|
|
45
45
|
0 0 0 var(--border-width) var(--accent-color),
|
|
46
|
-
0 0 var(--s1)
|
|
46
|
+
0 0 var(--s1) rgb(from var(--accent-color) r g b / 40%);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/* Multi-line input. padding-inline-end clears the send button so typed
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
cursor: pointer;
|
|
88
88
|
}
|
|
89
89
|
.velu-ask-bar__send:hover {
|
|
90
|
-
background:
|
|
90
|
+
background: rgb(from var(--accent-color) calc(r * 0.88) calc(g * 0.88) calc(b * 0.88));
|
|
91
91
|
}
|
|
92
92
|
.velu-ask-bar__send:focus-visible {
|
|
93
93
|
outline: var(--border-width) solid var(--text-color);
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
palette) so the button reads as "not yet ready" rather than a
|
|
98
98
|
different control entirely. No pointer events / hover affordance. */
|
|
99
99
|
.velu-ask-bar__send:disabled {
|
|
100
|
-
background:
|
|
100
|
+
background: rgb(from var(--accent-color) r g b / 35%);
|
|
101
101
|
cursor: not-allowed;
|
|
102
102
|
pointer-events: none;
|
|
103
103
|
}
|
|
@@ -26,18 +26,18 @@
|
|
|
26
26
|
font-size: var(--f-h6);
|
|
27
27
|
font-weight: var(--weight-medium);
|
|
28
28
|
color: var(--text-color);
|
|
29
|
-
background:
|
|
29
|
+
background: rgb(from var(--muted-color) r g b / 14%);
|
|
30
30
|
border: var(--border-width) solid transparent;
|
|
31
31
|
transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
.velu-changelog-filter:hover {
|
|
35
|
-
border-color:
|
|
36
|
-
background:
|
|
35
|
+
border-color: rgb(from var(--accent-color) r g b / 50%);
|
|
36
|
+
background: rgb(from var(--accent-color) r g b / 8%);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
.velu-changelog-filter--active {
|
|
40
40
|
color: var(--accent-color);
|
|
41
|
-
border-color:
|
|
42
|
-
background:
|
|
41
|
+
border-color: rgb(from var(--accent-color) r g b / 55%);
|
|
42
|
+
background: rgb(from var(--accent-color) r g b / 12%);
|
|
43
43
|
}
|
|
@@ -215,7 +215,7 @@
|
|
|
215
215
|
color: var(--text-color);
|
|
216
216
|
}
|
|
217
217
|
.velu-chatbot__iconbtn.is-active {
|
|
218
|
-
background:
|
|
218
|
+
background: rgb(from var(--accent-color) r g b / 12%);
|
|
219
219
|
color: var(--accent-color);
|
|
220
220
|
}
|
|
221
221
|
|
|
@@ -322,7 +322,7 @@
|
|
|
322
322
|
}
|
|
323
323
|
.velu-chatbot__suggest:hover {
|
|
324
324
|
border-color: var(--accent-color);
|
|
325
|
-
background:
|
|
325
|
+
background: rgb(from var(--accent-color) r g b / 6%);
|
|
326
326
|
}
|
|
327
327
|
.velu-chatbot__suggest svg {
|
|
328
328
|
flex: none;
|
|
@@ -497,7 +497,7 @@
|
|
|
497
497
|
font-size: var(--f-h7);
|
|
498
498
|
font-weight: var(--weight-medium);
|
|
499
499
|
color: var(--accent-color);
|
|
500
|
-
background:
|
|
500
|
+
background: rgb(from var(--accent-color) r g b / 14%);
|
|
501
501
|
border: var(--border-width) solid transparent;
|
|
502
502
|
border-radius: var(--radius-sm);
|
|
503
503
|
text-decoration: none;
|
|
@@ -552,7 +552,7 @@
|
|
|
552
552
|
font-size: var(--f-h7);
|
|
553
553
|
font-weight: var(--weight-medium);
|
|
554
554
|
color: var(--accent-color);
|
|
555
|
-
background:
|
|
555
|
+
background: rgb(from var(--accent-color) r g b / 14%);
|
|
556
556
|
border-radius: var(--radius-sm);
|
|
557
557
|
}
|
|
558
558
|
.velu-chatbot__source-meta {
|
|
@@ -635,7 +635,7 @@
|
|
|
635
635
|
}
|
|
636
636
|
/* Chosen thumb (up/down) — accent tint so the click is clearly registered. */
|
|
637
637
|
.velu-chatbot__msg-action.is-active {
|
|
638
|
-
background:
|
|
638
|
+
background: rgb(from var(--accent-color) r g b / 12%);
|
|
639
639
|
color: var(--accent-color);
|
|
640
640
|
}
|
|
641
641
|
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
inline-size: 1.75em;
|
|
129
129
|
block-size: 1.75em;
|
|
130
130
|
padding: 0;
|
|
131
|
-
background:
|
|
131
|
+
background: rgb(from var(--surface-color) r g b / 75%);
|
|
132
132
|
-webkit-backdrop-filter: blur(6px);
|
|
133
133
|
backdrop-filter: blur(6px);
|
|
134
134
|
border: var(--border-width) solid var(--border-color);
|
|
@@ -160,7 +160,7 @@
|
|
|
160
160
|
the code text. The bg is a subtle accent tint — works in both
|
|
161
161
|
light + dark via [data-theme] color-mix. */
|
|
162
162
|
.velu-code-block__line--highlight {
|
|
163
|
-
background:
|
|
163
|
+
background: rgb(from var(--accent-color) r g b / 12%);
|
|
164
164
|
box-shadow: inset 2px 0 0 0 var(--accent-color);
|
|
165
165
|
margin-inline: calc(var(--s0) * -1);
|
|
166
166
|
padding-inline: var(--s0);
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
.velu-context-menu__rss:hover {
|
|
52
52
|
color: var(--accent-color);
|
|
53
53
|
border-color: var(--accent-color);
|
|
54
|
-
background:
|
|
54
|
+
background: rgb(from var(--accent-color) r g b / 8%);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/* The split button: "Copy Page" + a divider + a chevron toggle, joined as one
|
|
@@ -194,12 +194,12 @@
|
|
|
194
194
|
scrollbar-width: thin;
|
|
195
195
|
/* Firefox: thumb then track. */
|
|
196
196
|
scrollbar-color: var(--muted-color)
|
|
197
|
-
|
|
197
|
+
rgb(from var(--muted-color) r g b / 28%);
|
|
198
198
|
/* Chromium overlay scrollbars often ignore ::-webkit-scrollbar-track,
|
|
199
199
|
so paint a permanent rail on the inline-end edge as well. */
|
|
200
200
|
background-image: linear-gradient(
|
|
201
201
|
to left,
|
|
202
|
-
|
|
202
|
+
rgb(from var(--muted-color) r g b / 28%) 10px,
|
|
203
203
|
transparent 10px
|
|
204
204
|
);
|
|
205
205
|
}
|
|
@@ -207,7 +207,7 @@
|
|
|
207
207
|
inline-size: 10px;
|
|
208
208
|
}
|
|
209
209
|
.velu-docs-nav-scroll::-webkit-scrollbar-track {
|
|
210
|
-
background:
|
|
210
|
+
background: rgb(from var(--muted-color) r g b / 28%);
|
|
211
211
|
border-radius: 0;
|
|
212
212
|
}
|
|
213
213
|
.velu-docs-nav-scroll::-webkit-scrollbar-thumb {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
align-items: center;
|
|
47
47
|
padding-block: 0;
|
|
48
48
|
padding-inline: var(--s-3);
|
|
49
|
-
background:
|
|
49
|
+
background: rgb(from var(--accent-color) r g b / 18%);
|
|
50
50
|
border-radius: var(--radius-sm);
|
|
51
51
|
font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
|
|
52
52
|
font-size: var(--f-h6);
|
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
inline-size: 15%;
|
|
206
206
|
block-size: 0.5em;
|
|
207
207
|
border-radius: 999px;
|
|
208
|
-
background:
|
|
208
|
+
background: rgb(from var(--text-color) r g b / 14%);
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
/* ── Device-frame variant ───────────────────────────────────────────────── */
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
font-weight: var(--weight-bold);
|
|
87
87
|
}
|
|
88
88
|
.velu-404__btn--primary:hover {
|
|
89
|
-
background:
|
|
89
|
+
background: rgb(from var(--accent-color) calc(r * 0.88) calc(g * 0.88) calc(b * 0.88));
|
|
90
90
|
}
|
|
91
91
|
.velu-404__btn-ic {
|
|
92
92
|
display: inline-flex;
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
The vote stays changeable: clicking the other button moves it here. */
|
|
46
46
|
.velu-feedback__btn--chosen {
|
|
47
47
|
border-color: var(--accent-color);
|
|
48
|
-
background:
|
|
48
|
+
background: rgb(from var(--accent-color) r g b / 10%);
|
|
49
49
|
color: var(--accent-color);
|
|
50
50
|
}
|
|
51
51
|
.velu-feedback__btn--chosen svg {
|
|
@@ -168,7 +168,7 @@
|
|
|
168
168
|
color: #fff;
|
|
169
169
|
}
|
|
170
170
|
.velu-feedback__action--submit:hover {
|
|
171
|
-
background:
|
|
171
|
+
background: rgb(from var(--accent-color) calc(r * 0.88) calc(g * 0.88) calc(b * 0.88));
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
/* ── Submitted confirmation — sits below the divider line ───────────── */
|
|
@@ -214,13 +214,13 @@
|
|
|
214
214
|
color: var(--accent-color);
|
|
215
215
|
}
|
|
216
216
|
.velu-feedback__heart--1 {
|
|
217
|
-
color:
|
|
217
|
+
color: rgb(from var(--accent-color) calc(r * 0.6 + 255 * 0.4) calc(g * 0.6 + 255 * 0.4) calc(b * 0.6 + 255 * 0.4));
|
|
218
218
|
}
|
|
219
219
|
.velu-feedback__heart--2 {
|
|
220
|
-
color:
|
|
220
|
+
color: rgb(from var(--accent-color) calc(r * 0.55) calc(g * 0.55) calc(b * 0.55));
|
|
221
221
|
}
|
|
222
222
|
.velu-feedback__heart--3 {
|
|
223
|
-
color:
|
|
223
|
+
color: rgb(from var(--accent-color) calc(r * 0.82 + 255 * 0.18) calc(g * 0.82 + 255 * 0.18) calc(b * 0.82 + 255 * 0.18));
|
|
224
224
|
}
|
|
225
225
|
/* Register the per-heart custom props so they're typed + animatable inside the
|
|
226
226
|
keyframes. WebKit (newer iOS Safari) won't interpolate UNregistered custom
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
through faintly underneath. color-mix keeps the recipe token-driven:
|
|
55
55
|
75% --page-bg + transparent → 75% opaque in either theme. */
|
|
56
56
|
.velu-header--scrolled {
|
|
57
|
-
background:
|
|
57
|
+
background: rgb(from var(--page-bg) r g b / 75%);
|
|
58
58
|
backdrop-filter: saturate(140%) blur(12px);
|
|
59
59
|
-webkit-backdrop-filter: saturate(140%) blur(12px);
|
|
60
60
|
/* Lift shadow (theme-aware recipe defined on .velu-header) so the
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
color: var(--accent-color);
|
|
109
109
|
}
|
|
110
110
|
.velu-header__wordmark {
|
|
111
|
-
font-family: var(--font-
|
|
111
|
+
font-family: var(--font-wordmark);
|
|
112
112
|
font-weight: var(--weight-light);
|
|
113
113
|
/* 32px wordmark. */
|
|
114
114
|
font-size: 2rem;
|
|
@@ -174,7 +174,7 @@
|
|
|
174
174
|
color: #fff;
|
|
175
175
|
}
|
|
176
176
|
.velu-header__action--primary:hover {
|
|
177
|
-
background:
|
|
177
|
+
background: rgb(from var(--accent-color) calc(r * 0.88) calc(g * 0.88) calc(b * 0.88));
|
|
178
178
|
}
|
|
179
179
|
/* Plain text link (navbar.links) — no fill or border, just text that goes
|
|
180
180
|
accent on hover. Transparent border keeps it the same height as the
|
|
@@ -573,3 +573,11 @@
|
|
|
573
573
|
font-size: var(--f-h7);
|
|
574
574
|
opacity: 0.7;
|
|
575
575
|
}
|
|
576
|
+
/* Ask AI header entry — icon + "Ask AI" must stay on one line (no wrap
|
|
577
|
+
into a stacked pill) in every theme's header, where it shares a narrow
|
|
578
|
+
flex row with search. Icon-circle themes (kadhir) hide the label
|
|
579
|
+
separately; the nowrap + flex:none here keeps the pill intact. */
|
|
580
|
+
#assistant-entry {
|
|
581
|
+
white-space: nowrap;
|
|
582
|
+
flex: none;
|
|
583
|
+
}
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
cursor: pointer;
|
|
61
61
|
}
|
|
62
62
|
.velu-prompt__copy:hover {
|
|
63
|
-
background:
|
|
63
|
+
background: rgb(from var(--accent-color) calc(r * 0.88) calc(g * 0.88) calc(b * 0.88));
|
|
64
64
|
}
|
|
65
65
|
.velu-prompt__copy:focus-visible {
|
|
66
66
|
outline: var(--border-width) solid var(--text-color);
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
text-decoration: none;
|
|
92
92
|
}
|
|
93
93
|
.velu-prompt__open-cursor:hover {
|
|
94
|
-
background:
|
|
94
|
+
background: rgb(from var(--text-color) calc(r * 0.88) calc(g * 0.88) calc(b * 0.88));
|
|
95
95
|
}
|
|
96
96
|
.velu-prompt__open-cursor:focus-visible {
|
|
97
97
|
outline: var(--border-width) solid var(--accent-color);
|