@aravindc26/velu 0.12.7 → 0.12.9

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.
Files changed (74) hide show
  1. package/package.json +1 -1
  2. package/src/build.ts +13 -0
  3. package/src/cli.ts +51 -9
  4. package/src/engine/app/(docs)/[...slug]/layout.tsx +21 -537
  5. package/src/engine/app/_preview/[sessionId]/[...slug]/layout.tsx +96 -0
  6. package/src/engine/app/_preview/[sessionId]/[...slug]/page.tsx +298 -0
  7. package/src/engine/app/_preview/[sessionId]/layout.tsx +56 -0
  8. package/src/{preview-engine/app → engine/app/_preview}/[sessionId]/page.tsx +7 -3
  9. package/src/engine/app/_preview/api/sessions/[sessionId]/assets/[...path]/route.ts +51 -0
  10. package/src/{preview-engine/app → engine/app/_preview}/api/sessions/[sessionId]/init/route.ts +2 -2
  11. package/src/{preview-engine/app → engine/app/_preview}/api/sessions/[sessionId]/route.ts +3 -3
  12. package/src/{preview-engine/app → engine/app/_preview}/api/sessions/[sessionId]/sync/route.ts +2 -2
  13. package/src/{preview-engine/app → engine/app/_preview}/layout.tsx +4 -1
  14. package/src/engine/app/global.css +0 -3623
  15. package/src/engine/app/layout.tsx +4 -3
  16. package/src/engine/components/sidebar-links.tsx +11 -5
  17. package/src/engine/lib/docs-layout.tsx +605 -0
  18. package/src/engine/lib/layout.shared.ts +7 -7
  19. package/src/engine/lib/preview-config.ts +129 -0
  20. package/src/{preview-engine/lib/content-generator.ts → engine/lib/preview-content.ts} +242 -42
  21. package/src/engine/lib/source.ts +80 -97
  22. package/src/engine/lib/velu.ts +79 -55
  23. package/src/engine/mdx-components.tsx +14 -650
  24. package/src/engine/source.config.ts +11 -89
  25. package/src/engine/tsconfig.json +1 -0
  26. package/src/engine-core/components/assistant.tsx +361 -0
  27. package/src/engine-core/components/banner.tsx +80 -0
  28. package/src/engine-core/components/changelog-filters.tsx +114 -0
  29. package/src/engine-core/components/code-group.tsx +383 -0
  30. package/src/engine-core/components/color.tsx +118 -0
  31. package/src/engine-core/components/copy-page.tsx +223 -0
  32. package/src/engine-core/components/dropdown-switcher.tsx +142 -0
  33. package/src/engine-core/components/expandable.tsx +77 -0
  34. package/src/engine-core/components/header-tab-link.tsx +43 -0
  35. package/src/engine-core/components/icon.tsx +136 -0
  36. package/src/engine-core/components/image-zoom-fallback.tsx +147 -0
  37. package/src/engine-core/components/image.tsx +111 -0
  38. package/src/engine-core/components/lang-switcher.tsx +101 -0
  39. package/src/engine-core/components/manual-api-playground.tsx +154 -0
  40. package/src/engine-core/components/mermaid.tsx +142 -0
  41. package/src/engine-core/components/openapi-toc-sync.tsx +59 -0
  42. package/src/engine-core/components/openapi.tsx +1682 -0
  43. package/src/engine-core/components/page-feedback-api.test.ts +83 -0
  44. package/src/engine-core/components/page-feedback-api.ts +89 -0
  45. package/src/engine-core/components/page-feedback.tsx +200 -0
  46. package/src/engine-core/components/product-switcher.tsx +107 -0
  47. package/src/engine-core/components/prompt.tsx +90 -0
  48. package/src/engine-core/components/providers.tsx +21 -0
  49. package/src/engine-core/components/search.tsx +318 -0
  50. package/src/engine-core/components/sidebar-links.tsx +54 -0
  51. package/src/engine-core/components/synced-tabs.tsx +57 -0
  52. package/src/engine-core/components/theme-toggle.tsx +39 -0
  53. package/src/engine-core/components/toc-examples.tsx +110 -0
  54. package/src/engine-core/components/version-switcher.tsx +95 -0
  55. package/src/engine-core/components/view.tsx +344 -0
  56. package/src/engine-core/css/assistant.css +326 -0
  57. package/src/engine-core/css/copy-page.css +206 -0
  58. package/src/engine-core/css/search.css +142 -0
  59. package/src/engine-core/css/shared.css +3628 -0
  60. package/src/engine-core/lib/remark-plugins.ts +102 -0
  61. package/src/engine-core/lib/source-plugins.ts +105 -0
  62. package/src/engine-core/mdx-components.tsx +654 -0
  63. package/src/engine-core/types.ts +49 -0
  64. package/src/preview-engine/app/[sessionId]/[...slug]/page.tsx +0 -41
  65. package/src/preview-engine/app/[sessionId]/layout.tsx +0 -23
  66. package/src/preview-engine/app/global.css +0 -3
  67. package/src/preview-engine/lib/session-config.ts +0 -86
  68. package/src/preview-engine/lib/source.ts +0 -60
  69. package/src/preview-engine/next.config.mjs +0 -20
  70. package/src/preview-engine/postcss.config.mjs +0 -8
  71. package/src/preview-engine/source.config.ts +0 -26
  72. package/src/preview-engine/tsconfig.json +0 -32
  73. /package/src/{preview-engine/app → engine/app/_preview}/page.tsx +0 -0
  74. /package/src/{preview-engine/lib/auth.ts → engine/lib/preview-auth.ts} +0 -0
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Shared remark plugins used by both main engine and preview engine.
3
+ */
4
+
5
+ const booleanMetaFlags = new Set([
6
+ 'wrap',
7
+ 'copy',
8
+ 'nocopy',
9
+ 'lineNumbers',
10
+ 'linenumbers',
11
+ 'showLineNumbers',
12
+ ]);
13
+
14
+ function quoteTitle(value: string): string {
15
+ return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
16
+ }
17
+
18
+ function ensureTitleMeta(meta: string): string {
19
+ const trimmed = meta.trim();
20
+ if (!trimmed) return trimmed;
21
+ if (/\btitle\s*=/.test(trimmed)) return trimmed;
22
+
23
+ const fileWithRest = trimmed.match(/^([^\s]+?\.[a-z0-9_-]+)(\s+.*)?$/i);
24
+ if (fileWithRest) {
25
+ const file = fileWithRest[1];
26
+ const rest = (fileWithRest[2] ?? '').trim();
27
+ return rest ? `title="${quoteTitle(file)}" ${rest}` : `title="${quoteTitle(file)}"`;
28
+ }
29
+
30
+ if (!trimmed.includes('=') && !trimmed.includes('{') && !trimmed.includes('}')) {
31
+ if (booleanMetaFlags.has(trimmed)) return trimmed;
32
+ return `title="${quoteTitle(trimmed)}"`;
33
+ }
34
+
35
+ return trimmed;
36
+ }
37
+
38
+ /**
39
+ * Remark plugin that converts Mint-style code fence metadata into
40
+ * standard title/highlight syntax that fumadocs understands.
41
+ *
42
+ * Handles:
43
+ * - ```lang filename.ext → title="filename.ext"
44
+ * - highlight=1 or highlight="1,3-5" → {1,3-5}
45
+ * - theme={null} removal
46
+ */
47
+ export function remarkCodeFilenameToTitle() {
48
+ function visit(node: any) {
49
+ if (!node || typeof node !== 'object') return;
50
+
51
+ if (node.type === 'code' && typeof node.meta === 'string') {
52
+ let meta = node.meta.trim();
53
+ meta = ensureTitleMeta(meta);
54
+
55
+ const hlMatch = meta.match(/(?:^|\s)highlight=(?:"([^"]+)"|'([^']+)'|([^\s]+))/i);
56
+ if (hlMatch) {
57
+ const raw = (hlMatch[1] ?? hlMatch[2] ?? hlMatch[3] ?? '').trim();
58
+ const lineSpec = raw.replace(/[{}]/g, '');
59
+ meta = meta.replace(hlMatch[0], '').replace(/\s+/g, ' ').trim();
60
+ if (lineSpec && !/\{\s*\d[\d,\-\s]*\s*\}/.test(meta)) {
61
+ meta = `${meta} {${lineSpec}}`.trim();
62
+ }
63
+ }
64
+
65
+ meta = meta.replace(/\btheme=\{null\}\b/g, '').replace(/\s+/g, ' ').trim();
66
+ node.meta = meta;
67
+ }
68
+ if (node.type === 'code' && typeof node.meta !== 'string') {
69
+ return;
70
+ }
71
+
72
+ if (node.type === 'code' && node.meta === '') {
73
+ delete node.meta;
74
+ }
75
+
76
+ if (node.type === 'code' && typeof node.meta === 'string') {
77
+ node.meta = node.meta.trim();
78
+ if (!node.meta) {
79
+ delete node.meta;
80
+ }
81
+ }
82
+
83
+ const children = node.children;
84
+ if (Array.isArray(children)) {
85
+ for (const child of children) visit(child);
86
+ }
87
+ }
88
+
89
+ return (tree: any) => visit(tree);
90
+ }
91
+
92
+ /**
93
+ * Shared rehype code options for consistent syntax highlighting.
94
+ */
95
+ export const sharedRehypeCodeOptions = {
96
+ lazy: false,
97
+ fallbackLanguage: 'bash',
98
+ langAlias: {
99
+ gradle: 'groovy',
100
+ proguard: 'properties',
101
+ },
102
+ } as const;
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Shared source loader plugins for fumadocs page tree transformations.
3
+ * Used by both main engine and preview engine.
4
+ */
5
+ import { createElement } from 'react';
6
+ import { statusBadgesPlugin } from 'fumadocs-core/source/status-badges';
7
+
8
+ const OPENAPI_METHODS = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD', 'TRACE', 'WEBHOOK']);
9
+
10
+ function methodBadgeClass(method: string): string {
11
+ const upper = method.toUpperCase();
12
+ if (upper === 'POST') return 'velu-openapi-method-badge velu-openapi-method-post';
13
+ if (upper === 'PUT') return 'velu-openapi-method-badge velu-openapi-method-put';
14
+ if (upper === 'PATCH') return 'velu-openapi-method-badge velu-openapi-method-patch';
15
+ if (upper === 'DELETE') return 'velu-openapi-method-badge velu-openapi-method-delete';
16
+ if (upper === 'WEBHOOK') return 'velu-openapi-method-badge velu-openapi-method-webhook';
17
+ return 'velu-openapi-method-badge velu-openapi-method-get';
18
+ }
19
+
20
+ function parseOperationReference(value: string, requireUppercaseMethod = false): { method: string; target: string } | null {
21
+ const trimmed = value.trim();
22
+ if (!trimmed) return null;
23
+ const withSpec = trimmed.match(/^(\S+)\s+([A-Za-z]+)\s+(.+)$/);
24
+ if (withSpec) {
25
+ const rawMethod = withSpec[2];
26
+ const method = withSpec[2].toUpperCase();
27
+ if (requireUppercaseMethod && rawMethod !== method) return null;
28
+ if (!OPENAPI_METHODS.has(method)) return null;
29
+ return { method, target: withSpec[3].trim() };
30
+ }
31
+ const noSpec = trimmed.match(/^([A-Za-z]+)\s+(.+)$/);
32
+ if (noSpec) {
33
+ const rawMethod = noSpec[1];
34
+ const method = noSpec[1].toUpperCase();
35
+ if (requireUppercaseMethod && rawMethod !== method) return null;
36
+ if (!OPENAPI_METHODS.has(method)) return null;
37
+ return { method, target: noSpec[2].trim() };
38
+ }
39
+ return null;
40
+ }
41
+
42
+ function stripMethodPrefix(name: string, method: string): string {
43
+ const regex = new RegExp(`^${method}\\s+`, 'i');
44
+ return name.replace(regex, '').trim();
45
+ }
46
+
47
+ export function openApiSidebarMethodBadgePlugin() {
48
+ return {
49
+ name: 'velu:openapi-sidebar-method-badge',
50
+ transformPageTree: {
51
+ file(node: Record<string, unknown>, filePath?: string) {
52
+ let data: Record<string, unknown> = {};
53
+ if (filePath) {
54
+ const file = (this as { storage?: { read?: (path: string) => unknown } }).storage?.read?.(filePath) as
55
+ | { format?: string; data?: Record<string, unknown> }
56
+ | undefined;
57
+ if (file?.format === 'page') data = file.data ?? {};
58
+ }
59
+
60
+ const nameCandidate = typeof node.name === 'string' ? node.name.trim() : '';
61
+ const titleCandidate = typeof data.title === 'string' ? data.title.trim() : '';
62
+ const openApiCandidate = typeof data.openapi === 'string' ? data.openapi.trim() : '';
63
+ const parsed = openApiCandidate
64
+ ? parseOperationReference(openApiCandidate)
65
+ : parseOperationReference(nameCandidate, true) ?? parseOperationReference(titleCandidate, true);
66
+ if (!parsed) return node;
67
+
68
+ const method = parsed.method;
69
+ const rawName = nameCandidate || titleCandidate || parsed.target;
70
+ const text = stripMethodPrefix(rawName, method) || parsed.target || rawName || method;
71
+ const stableIdRaw = filePath || openApiCandidate || rawName || `${method}-${parsed.target}`;
72
+ const stableId = stableIdRaw.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
73
+
74
+ node.name = createElement(
75
+ 'span',
76
+ { className: 'velu-openapi-sidebar-item', key: `openapi-item-${stableId || 'unknown'}` },
77
+ createElement(
78
+ 'span',
79
+ { className: methodBadgeClass(method), key: `openapi-item-${stableId || 'unknown'}-method` },
80
+ method,
81
+ ),
82
+ createElement(
83
+ 'span',
84
+ { className: 'velu-openapi-sidebar-label', key: `openapi-item-${stableId || 'unknown'}-label` },
85
+ text,
86
+ ),
87
+ );
88
+ return node;
89
+ },
90
+ },
91
+ };
92
+ }
93
+
94
+ export function createStatusBadgesPlugin() {
95
+ return statusBadgesPlugin({
96
+ renderBadge: (status: string) => {
97
+ const normalized = status.trim().toLowerCase();
98
+ const label = normalized === 'deprecated' ? 'Deprecated' : status;
99
+ const className = normalized === 'deprecated'
100
+ ? 'velu-status-badge velu-status-badge-deprecated'
101
+ : 'velu-status-badge';
102
+ return createElement('span', { className, 'data-status': normalized }, label);
103
+ },
104
+ });
105
+ }