@veluai/velu 0.2.48 → 0.2.49

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.
@@ -121,9 +121,11 @@ The presets are not a certification of pixel equality for every component or cus
121
121
 
122
122
  Features without full Velu equivalents require review: remaining Mintlify themes, advanced
123
123
  fonts and styling, redirects, analytics/support integrations, hosting settings,
124
- authentication, personalization, hidden-page routing, legacy version filtering,
124
+ authentication, standalone public personalization, hidden-page routing, legacy version filtering,
125
125
  complex dropdown/global navigation, AsyncAPI, manually authored `api:` pages,
126
- multi-file/remote endpoint snapshots, OpenAPI overlays and `x-mint` extensions.
126
+ multi-file/remote endpoint snapshots, and OpenAPI overlays.
127
+ OpenAPI `x-mint` / `x-mint-enum` personalization keys are rewritten to `x-velu` /
128
+ `x-velu-enum`. The generator still reads leftover `x-mint` keys in unmigrated specs.
127
129
  Access/visibility settings are reported as errors; they must be migrated before
128
130
  publishing. Unlisted content is copied but Velu currently renders navigation
129
131
  pages only. Snapshot API pages must be regenerated or edited when their specs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veluai/velu",
3
- "version": "0.2.48",
3
+ "version": "0.2.49",
4
4
  "type": "module",
5
5
  "bin": "./dist/cli.js",
6
6
  "publishConfig": {
@@ -11,6 +11,24 @@ import CodeBlock, { CodeGroup } from './CodeBlock.jsx';
11
11
  import ApiSamples from './ApiSamples.jsx';
12
12
  import Callout from './Callout.jsx';
13
13
  import { sendApiRequest } from '../lib/api-send.js';
14
+ import {
15
+ getReaderGroups,
16
+ visibleToGroups,
17
+ initialPlaygroundValues,
18
+ persistPlaygroundValues,
19
+ personalizedServer,
20
+ } from '../lib/reader-user.js';
21
+
22
+ function visibleFields(rows = [], groups) {
23
+ return rows.filter((row) => visibleToGroups(row.groups, groups));
24
+ }
25
+
26
+ function visibleEnum(row, groups) {
27
+ if (!Array.isArray(row?.enum)) return row?.enum;
28
+ const mapping = row.veluEnum || row.mintEnum;
29
+ if (!mapping) return row.enum;
30
+ return row.enum.filter((value) => visibleToGroups(mapping[String(value)], groups));
31
+ }
14
32
 
15
33
  /**
16
34
  * ApiReferencePage — the full, auto-generated reference page for one OpenAPI
@@ -25,24 +43,29 @@ import { sendApiRequest } from '../lib/api-send.js';
25
43
  *
26
44
  * @param {{ operation: object, api?: { proxy?: boolean, server?: string } }} props
27
45
  */
28
- const LOCATION_LABEL = { path: 'Path Parameters', query: 'Query Parameters', header: 'Header Parameters' };
46
+ const LOCATION_LABEL = { path: 'Path Parameters', query: 'Query Parameters', header: 'Header Parameters', cookie: 'Cookie Parameters' };
29
47
 
30
48
  export default function ApiReferencePage({ operation, samples = [], apiOperations = [], api = {}, children, preset }) {
31
49
  const [open, setOpen] = useState(false);
32
- // Which operation the playground is currently showing. Defaults to this
33
- // page's own operation; the dropdown can switch it to any sibling.
34
50
  const [activeId, setActiveId] = useState(operation.id);
51
+ const [groups, setGroups] = useState(() => getReaderGroups());
52
+ useEffect(() => {
53
+ const sync = () => setGroups(getReaderGroups());
54
+ sync();
55
+ if (typeof window === 'undefined') return undefined;
56
+ window.addEventListener('velu:user', sync);
57
+ return () => window.removeEventListener('velu:user', sync);
58
+ }, []);
35
59
 
36
- // The switchable list — fall back to just this page's operation.
37
60
  const ops =
38
61
  apiOperations.length > 0
39
- ? apiOperations
62
+ ? apiOperations.filter((o) => visibleToGroups((o.operation || o).groups, groups))
40
63
  : [{ id: operation.id, method: operation.method, title: operation.title, operation, samples, api }];
41
- const current = ops.find((o) => o.id === activeId) || { operation, samples, api };
64
+ const current = ops.find((o) => o.id === activeId) || ops[0] || { operation, samples, api };
42
65
  // Each operation carries its own (section-level) api config; fall back to
43
66
  // this page's config.
44
67
  const currentApi = current.api ?? api;
45
- const server = currentApi.server || current.operation.servers?.[0] || '';
68
+ const server = personalizedServer(currentApi.server || current.operation.servers?.[0] || '');
46
69
 
47
70
  // While the playground modal is open, close on Escape and lock the
48
71
  // page scroll so the dimmed backdrop reads as a true overlay.
@@ -94,8 +117,8 @@ export default function ApiReferencePage({ operation, samples = [], apiOperation
94
117
  )}
95
118
 
96
119
  {/* Parameters, by location */}
97
- {['path', 'query', 'header'].map((loc) => {
98
- const rows = operation.parameters?.[loc] || [];
120
+ {['path', 'query', 'header', 'cookie'].map((loc) => {
121
+ const rows = visibleFields(operation.parameters?.[loc] || [], groups);
99
122
  if (!rows.length) return null;
100
123
  return (
101
124
  <api-section key={loc} class="velu-api-page__section">
@@ -103,7 +126,7 @@ export default function ApiReferencePage({ operation, samples = [], apiOperation
103
126
  {rows.map((p, i) => (
104
127
  <Field key={i} name={p.name} type={p.type} required={p.required}>
105
128
  {p.description}
106
- {p.enum && <EnumHint values={p.enum} />}
129
+ {visibleEnum(p, groups)?.length ? <EnumHint values={visibleEnum(p, groups)} /> : null}
107
130
  </Field>
108
131
  ))}
109
132
  </api-section>
@@ -115,10 +138,10 @@ export default function ApiReferencePage({ operation, samples = [], apiOperation
115
138
  <api-section class="velu-api-page__section">
116
139
  <h2 data-component="api-section-heading">Body</h2>
117
140
  {['thulir', 'aalam', 'nila', 'kadhir'].includes(preset) && operation.body.contentType && <span className="velu-api-body-type">{operation.body.contentType}</span>}
118
- {operation.body.fields.map((f, i) => (
141
+ {visibleFields(operation.body.fields, groups).map((f, i) => (
119
142
  <Field key={i} name={f.name} type={f.type} required={f.required}>
120
143
  {f.description}
121
- {f.enum && <EnumHint values={f.enum} />}
144
+ {visibleEnum(f, groups)?.length ? <EnumHint values={visibleEnum(f, groups)} /> : null}
122
145
  </Field>
123
146
  ))}
124
147
  </api-section>
@@ -183,7 +206,7 @@ function ResponseSections({ responses, preset }) {
183
206
  if (preset === 'vepa' || ['thulir', 'aalam', 'nila', 'kadhir'].includes(preset)) return <div className="velu-vepa-responses">{responses.map((r, i) => <section key={i}>
184
207
  <div className="velu-vepa-responses__status">{r.status}{r.contentType && ` · ${r.contentType}`}</div>
185
208
  {r.description && <p>{r.description}</p>}
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>)}
209
+ {(['thulir', 'aalam', 'nila', 'kadhir'].includes(preset) ? [...visibleFields(r.fields, getReaderGroups())].sort((a, b) => Number(Boolean(b.required)) - Number(Boolean(a.required))) : visibleFields(r.fields, getReaderGroups()))?.map((f, j) => <Field key={f.name || j} name={f.name} type={f.type} required={f.required}>{f.description}{visibleEnum(f, getReaderGroups())?.length ? <EnumHint values={visibleEnum(f, getReaderGroups())} /> : null}</Field>)}
187
210
  </section>)}</div>;
188
211
  return (
189
212
  <AccordionGroup className="velu-api-resp">
@@ -202,9 +225,10 @@ function ResponseSections({ responses, preset }) {
202
225
  }
203
226
  >
204
227
  {r.description && <p className="velu-api-resp__desc">{r.description}</p>}
205
- {r.fields?.map((f, j) => (
228
+ {visibleFields(r.fields, getReaderGroups()).map((f, j) => (
206
229
  <Field key={j} name={f.name} type={f.type} required={f.required}>
207
230
  {f.description}
231
+ {visibleEnum(f, getReaderGroups())?.length ? <EnumHint values={visibleEnum(f, getReaderGroups())} /> : null}
208
232
  </Field>
209
233
  ))}
210
234
  </Accordion>
@@ -226,7 +250,7 @@ function Playground({
226
250
  proxy,
227
251
  onClose,
228
252
  }) {
229
- const [values, setValues] = useState({});
253
+ const [values, setValues] = useState(() => initialPlaygroundValues(operation));
230
254
  const [bodyText, setBodyText] = useState(
231
255
  operation.body ? JSON.stringify(operation.body.example ?? {}, null, 2) : '',
232
256
  );
@@ -234,7 +258,11 @@ function Playground({
234
258
  const [resp, setResp] = useState(null);
235
259
 
236
260
  const set = (loc, name, v) =>
237
- setValues((prev) => ({ ...prev, [loc]: { ...(prev[loc] || {}), [name]: v } }));
261
+ setValues((prev) => {
262
+ const next = { ...prev, [loc]: { ...(prev[loc] || {}), [name]: v } };
263
+ persistPlaygroundValues(operation.id, next);
264
+ return next;
265
+ });
238
266
 
239
267
  const onSend = useCallback(async () => {
240
268
  setBusy(true);
@@ -251,8 +279,8 @@ function Playground({
251
279
 
252
280
  const sections = [];
253
281
  if (operation.auth?.length) sections.push(['auth', 'Authorization', operation.auth]);
254
- for (const loc of ['path', 'query', 'header']) {
255
- const rows = operation.parameters?.[loc] || [];
282
+ for (const loc of ['path', 'query', 'header', 'cookie']) {
283
+ const rows = visibleFields(operation.parameters?.[loc] || [], getReaderGroups());
256
284
  if (rows.length) sections.push([loc, LOCATION_LABEL[loc].replace(' Parameters', ''), rows]);
257
285
  }
258
286
 
@@ -49,6 +49,17 @@ export { default as Search } from './components/Search.jsx';
49
49
  export { default as pagefindSearch } from './lib/pagefind.js';
50
50
  export { createDocsAssistant } from './lib/docs-assistant.js';
51
51
  export { createPageFeedback } from './lib/page-feedback.js';
52
+ export {
53
+ setReaderIdentity,
54
+ emptyReaderIdentity,
55
+ identityFromUserData,
56
+ getReaderUser,
57
+ getReaderGroups,
58
+ getPlaygroundInputs,
59
+ visibleToGroups,
60
+ filterSidebarSections,
61
+ pageRequiredGroups,
62
+ } from './lib/reader-user.js';
52
63
  export { default as Image } from './components/Image.jsx';
53
64
  export {
54
65
  default as CodeBlock,
@@ -0,0 +1,135 @@
1
+ /** Identified-reader personalization (Mintlify-compatible `user` + playground). */
2
+
3
+ export function emptyReaderIdentity() {
4
+ return { user: {}, groups: [], apiPlaygroundInputs: {} };
5
+ }
6
+
7
+ export function identityFromUserData(data) {
8
+ const empty = emptyReaderIdentity();
9
+ if (!data || typeof data !== 'object' || Array.isArray(data)) return empty;
10
+ if (typeof data.expiresAt === 'number' && data.expiresAt * 1000 <= Date.now()) return empty;
11
+ const groups = Array.isArray(data.groups)
12
+ ? [...new Set(data.groups.filter((g) => typeof g === 'string' && g.trim()).map((g) => g.trim()))].slice(0, 200)
13
+ : [];
14
+ const user = data.content && typeof data.content === 'object' && !Array.isArray(data.content) ? data.content : {};
15
+ const apiPlaygroundInputs =
16
+ data.apiPlaygroundInputs && typeof data.apiPlaygroundInputs === 'object' && !Array.isArray(data.apiPlaygroundInputs)
17
+ ? data.apiPlaygroundInputs
18
+ : {};
19
+ return { user, groups, apiPlaygroundInputs };
20
+ }
21
+
22
+ export function pageRequiredGroups(page) {
23
+ const fromFrontmatter = page?.frontmatter?.groups;
24
+ if (Array.isArray(fromFrontmatter) && fromFrontmatter.length) return fromFrontmatter;
25
+ const fromOperation = page?.operation?.groups;
26
+ if (Array.isArray(fromOperation) && fromOperation.length) return fromOperation;
27
+ return [];
28
+ }
29
+
30
+ export function filterSidebarSections(sections = [], groups = getReaderGroups(), pagesMap = {}) {
31
+ const keepItems = (items) =>
32
+ (items || []).flatMap((item) => {
33
+ if (item?.items) {
34
+ const nested = keepItems(item.items);
35
+ return nested.length ? [{ ...item, items: nested }] : [];
36
+ }
37
+ const required = Array.isArray(item?.groups) && item.groups.length
38
+ ? item.groups
39
+ : pageRequiredGroups(pagesMap[item?.href]);
40
+ return visibleToGroups(required, groups) ? [item] : [];
41
+ });
42
+ return (sections || [])
43
+ .map((section) => {
44
+ const items = keepItems(section.items);
45
+ return items.length ? { ...section, items } : null;
46
+ })
47
+ .filter(Boolean);
48
+ }
49
+
50
+ export function setReaderIdentity(identity = {}) {
51
+ const next = {
52
+ user: identity.user && typeof identity.user === 'object' && !Array.isArray(identity.user) ? identity.user : {},
53
+ groups: Array.isArray(identity.groups) ? identity.groups.filter((g) => typeof g === 'string') : [],
54
+ apiPlaygroundInputs:
55
+ identity.apiPlaygroundInputs && typeof identity.apiPlaygroundInputs === 'object' && !Array.isArray(identity.apiPlaygroundInputs)
56
+ ? identity.apiPlaygroundInputs
57
+ : {},
58
+ };
59
+ if (typeof globalThis === 'undefined') return next;
60
+ globalThis.velu = { ...(globalThis.velu || {}), ...next };
61
+ globalThis.mintlify = { ...(globalThis.mintlify || {}), user: next.user };
62
+ if (typeof globalThis.dispatchEvent === 'function' && typeof globalThis.Event === 'function') {
63
+ globalThis.dispatchEvent(new Event('velu:user'));
64
+ globalThis.dispatchEvent(new Event('mintlify:user'));
65
+ }
66
+ return next;
67
+ }
68
+
69
+ export function getReaderUser() {
70
+ return globalThis.velu?.user && typeof globalThis.velu.user === 'object' ? globalThis.velu.user : {};
71
+ }
72
+
73
+ export function getReaderGroups() {
74
+ return Array.isArray(globalThis.velu?.groups) ? globalThis.velu.groups : [];
75
+ }
76
+
77
+ export function getPlaygroundInputs() {
78
+ const inputs = globalThis.velu?.apiPlaygroundInputs;
79
+ return inputs && typeof inputs === 'object' ? inputs : {};
80
+ }
81
+
82
+ export function visibleToGroups(required, groups = getReaderGroups()) {
83
+ if (!Array.isArray(required) || required.length === 0) return true;
84
+ return groups.some((g) => required.includes(g));
85
+ }
86
+
87
+ export function initialPlaygroundValues(operation) {
88
+ const inputs = getPlaygroundInputs();
89
+ const values = { auth: {}, path: {}, query: {}, header: {}, cookie: {} };
90
+ for (const a of operation?.auth || []) {
91
+ const raw = inputs.header?.[a.name];
92
+ if (typeof raw !== 'string' || !raw) continue;
93
+ values.auth[a.name] = a.prefix && raw.startsWith(`${a.prefix} `) ? raw.slice(a.prefix.length + 1) : raw;
94
+ }
95
+ for (const loc of ['path', 'query', 'header', 'cookie']) {
96
+ for (const p of operation?.parameters?.[loc] || []) {
97
+ const raw = inputs[loc]?.[p.name];
98
+ if (typeof raw === 'string') values[loc][p.name] = raw;
99
+ }
100
+ }
101
+ if (typeof globalThis.localStorage === 'undefined') return values;
102
+ try {
103
+ const stored = JSON.parse(globalThis.localStorage.getItem(`velu-playground:${operation?.id}`) || 'null');
104
+ if (stored && typeof stored === 'object') {
105
+ for (const loc of Object.keys(values)) {
106
+ values[loc] = { ...values[loc], ...(stored[loc] || {}) };
107
+ }
108
+ }
109
+ } catch {
110
+ /* ignore malformed local overrides */
111
+ }
112
+ return values;
113
+ }
114
+
115
+ export function persistPlaygroundValues(operationId, values) {
116
+ if (typeof globalThis.localStorage === 'undefined' || !operationId) return;
117
+ try {
118
+ globalThis.localStorage.setItem(`velu-playground:${operationId}`, JSON.stringify(values || {}));
119
+ } catch {
120
+ /* quota / private mode */
121
+ }
122
+ }
123
+
124
+ export function personalizedServer(fallback) {
125
+ const vars = getPlaygroundInputs().server || {};
126
+ let url = (typeof vars.baseUrl === 'string' && vars.baseUrl)
127
+ || (typeof vars.url === 'string' && vars.url)
128
+ || fallback
129
+ || '';
130
+ for (const [key, value] of Object.entries(vars)) {
131
+ if (key === 'baseUrl' || key === 'url' || typeof value !== 'string') continue;
132
+ url = url.replaceAll(`{${key}}`, value);
133
+ }
134
+ return url;
135
+ }
package/src/navigation.js CHANGED
@@ -478,5 +478,9 @@ function itemFor(pageNode, pagesMap, ctx) {
478
478
  }
479
479
  const method = pageNode.method ?? pagesMap[href]?.operation?.method;
480
480
  if (method) item.method = method;
481
+ const groups = pageNode.groups
482
+ ?? (Array.isArray(fm?.groups) && fm.groups.length ? fm.groups : undefined)
483
+ ?? pagesMap[href]?.operation?.groups;
484
+ if (Array.isArray(groups) && groups.length) item.groups = groups;
481
485
  return item;
482
486
  }
@@ -58,6 +58,10 @@ import {
58
58
  ApiReferencePage,
59
59
  NotFound,
60
60
  VeluMark,
61
+ setReaderIdentity,
62
+ emptyReaderIdentity,
63
+ identityFromUserData,
64
+ filterSidebarSections,
61
65
  } from 'velu-ui';
62
66
  import { X, ChevronDown } from 'lucide-react';
63
67
  import ErrorBoundary from './ErrorBoundary.jsx';
@@ -107,14 +111,29 @@ function sectionToEndpoints(items) {
107
111
  else
108
112
  out.push({
109
113
  method: it.method,
110
- label: it.label,
114
+ name: it.label,
111
115
  href: it.href,
112
- icon: it.icon,
113
116
  });
114
117
  }
115
118
  return out;
116
119
  }
117
120
 
121
+ function flattenSidebarPages(sections) {
122
+ const out = [];
123
+ const walk = (items) => {
124
+ for (const it of items ?? []) {
125
+ if (it.items) walk(it.items);
126
+ else {
127
+ const page = { label: it.label, href: it.href };
128
+ if (it.description) page.description = it.description;
129
+ out.push(page);
130
+ }
131
+ }
132
+ };
133
+ for (const section of sections ?? []) walk(section.items);
134
+ return out;
135
+ }
136
+
118
137
  const CALLOUT_TYPES = [
119
138
  'note',
120
139
  'warning',
@@ -788,6 +807,53 @@ function DocsPage() {
788
807
  const location = useLocation();
789
808
  const navigate = useNavigate();
790
809
  const pathname = normalizeUrl(location.pathname);
810
+ const [readerUser, setReaderUser] = React.useState(() =>
811
+ typeof window === 'undefined' ? {} : window.velu?.user || {},
812
+ );
813
+ const [readerGroups, setReaderGroups] = React.useState(() =>
814
+ typeof window === 'undefined' ? [] : window.velu?.groups || [],
815
+ );
816
+ const [sharedSession, setSharedSession] = React.useState(null);
817
+ React.useEffect(() => {
818
+ const sync = () => {
819
+ setReaderUser(window.velu?.user || {});
820
+ setReaderGroups(Array.isArray(window.velu?.groups) ? window.velu.groups : []);
821
+ };
822
+ sync();
823
+ window.addEventListener('velu:user', sync);
824
+ return () => window.removeEventListener('velu:user', sync);
825
+ }, []);
826
+ React.useEffect(() => {
827
+ if (IS_DEV_PREVIEW || site.readerPrivate || site.guarded) return undefined;
828
+ let alive = true;
829
+ (async () => {
830
+ try {
831
+ const config = await fetch(`${BASE_PATH}/@velu-reader/personalization`, {
832
+ credentials: 'same-origin',
833
+ cache: 'no-store',
834
+ }).then((response) => (response.ok ? response.json() : null));
835
+ if (!alive || config?.method !== 'shared_session' || !config.infoUrl) return;
836
+ setSharedSession({ loginUrl: config.loginUrl || null, identified: false });
837
+ const response = await fetch(config.infoUrl, { credentials: 'include', cache: 'no-store' });
838
+ if (!alive) return;
839
+ if (!response.ok) {
840
+ setReaderIdentity(emptyReaderIdentity());
841
+ return;
842
+ }
843
+ const identity = identityFromUserData(await response.json());
844
+ setReaderIdentity(identity);
845
+ setSharedSession({
846
+ loginUrl: config.loginUrl || null,
847
+ identified: identity.groups.length > 0 || Object.keys(identity.user).length > 0,
848
+ });
849
+ } catch {
850
+ if (alive) setReaderIdentity(emptyReaderIdentity());
851
+ }
852
+ })();
853
+ return () => {
854
+ alive = false;
855
+ };
856
+ }, []);
791
857
  const entry = pages[pathname];
792
858
  // `navigation`/`pages` are module constants in dev/build (the deps never
793
859
  // change there); the instant preview's virtual-site substitute reassigns
@@ -797,6 +863,18 @@ function DocsPage() {
797
863
  () => resolve(pathname, navigation, pages),
798
864
  [pathname, navigation, pages],
799
865
  );
866
+ const visibleNav = React.useMemo(() => {
867
+ if (!nav || site.readerPrivate || site.guarded) return nav;
868
+ const sidebarSections = filterSidebarSections(nav.sidebarSections, readerGroups, pages);
869
+ const ordered = flattenSidebarPages(sidebarSections);
870
+ const idx = ordered.findIndex((page) => page.href === pathname);
871
+ return {
872
+ ...nav,
873
+ sidebarSections,
874
+ prev: idx > 0 ? ordered[idx - 1] : undefined,
875
+ next: idx >= 0 && idx < ordered.length - 1 ? ordered[idx + 1] : undefined,
876
+ };
877
+ }, [nav, pathname, readerGroups]);
800
878
 
801
879
  // Search source: Pagefind for content/excerpts, but resolve each result's
802
880
  // breadcrumb from the real NAVIGATION (a page's nav group can differ from its
@@ -1257,6 +1335,14 @@ function DocsPage() {
1257
1335
  kind: 'outlined',
1258
1336
  component: 'logout-link',
1259
1337
  });
1338
+ } else if (sharedSession?.loginUrl && !sharedSession.identified) {
1339
+ navActions.push({
1340
+ label: 'Log in',
1341
+ href: sharedSession.loginUrl,
1342
+ kind: 'outlined',
1343
+ external: true,
1344
+ component: 'login-link',
1345
+ });
1260
1346
  }
1261
1347
 
1262
1348
  // Callback ref so this rebinds when the footer mounts/unmounts.
@@ -1676,7 +1762,7 @@ function DocsPage() {
1676
1762
  the sidebar shape). */}
1677
1763
  {nav?.sidebarHasApi ? (
1678
1764
  <ApiSidebar
1679
- sections={(nav?.sidebarSections ?? []).map((s) => ({
1765
+ sections={(visibleNav?.sidebarSections ?? []).map((s) => ({
1680
1766
  title: s.title,
1681
1767
  icon: s.icon,
1682
1768
  endpoints: sectionToEndpoints(s.items),
@@ -1686,7 +1772,7 @@ function DocsPage() {
1686
1772
  />
1687
1773
  ) : (
1688
1774
  <Sidebar
1689
- sections={nav?.sidebarSections ?? []}
1775
+ sections={visibleNav?.sidebarSections ?? []}
1690
1776
  activeHref={pathname}
1691
1777
  linkComponent={RouterLink}
1692
1778
  />
@@ -1877,7 +1963,7 @@ function DocsPage() {
1877
1963
  <mdx-content key={site.theme === 'vepa' ? pathname : undefined} id={(site.theme === 'vepa' || ['thulir', 'aalam', 'nila', 'kadhir'].includes(site.theme)) && !entry?.api ? 'content' : undefined} class="velu-prose">
1878
1964
  {PageComponent ? (
1879
1965
  <ErrorBoundary key={pathname} file={entry?.relPath}>
1880
- {entry?.mdxApi ? <ApiReferencePage {...entry.mdxApi} preset={site.theme}><PageComponent /></ApiReferencePage> : <PageComponent />}
1966
+ {entry?.mdxApi ? <ApiReferencePage {...entry.mdxApi} preset={site.theme}><PageComponent user={readerUser} /></ApiReferencePage> : <PageComponent user={readerUser} />}
1881
1967
  </ErrorBoundary>
1882
1968
  ) : (
1883
1969
  <p style={{ color: 'var(--muted-color)' }}>
@@ -1910,14 +1996,14 @@ function DocsPage() {
1910
1996
  sidebar reading order of the active section. The wrapper
1911
1997
  doubles as the AskBar fade anchor on themes without a
1912
1998
  PageFeedback widget (see the observer above). */}
1913
- {chrome.articleChrome && !frontmatter.hidePagination && (nav?.prev || nav?.next) && (
1999
+ {chrome.articleChrome && !frontmatter.hidePagination && (visibleNav?.prev || visibleNav?.next) && (
1914
2000
  <div ref={setNavAnchorEl}>
1915
2001
  <PageNav
1916
2002
  id="pagination"
1917
2003
  style={['thulir', 'aalam', 'nila', 'kadhir'].includes(site.theme) ? undefined : { marginTop: 'var(--s2)' }}
1918
2004
  variant={site.theme === 'aalam' ? 'aalam' : 'default'}
1919
- prev={nav?.prev}
1920
- next={nav?.next}
2005
+ prev={visibleNav?.prev}
2006
+ next={visibleNav?.next}
1921
2007
  linkComponent={RouterLink}
1922
2008
  />
1923
2009
  </div>