@raystack/chronicle 0.1.0-canary.5a2be79

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 (107) hide show
  1. package/bin/chronicle.js +2 -0
  2. package/dist/cli/index.js +9980 -0
  3. package/next.config.mjs +10 -0
  4. package/package.json +63 -0
  5. package/source.config.ts +50 -0
  6. package/src/app/[[...slug]]/layout.tsx +15 -0
  7. package/src/app/[[...slug]]/page.tsx +57 -0
  8. package/src/app/api/apis-proxy/route.ts +59 -0
  9. package/src/app/api/health/route.ts +3 -0
  10. package/src/app/api/search/route.ts +90 -0
  11. package/src/app/apis/[[...slug]]/layout.module.css +22 -0
  12. package/src/app/apis/[[...slug]]/layout.tsx +26 -0
  13. package/src/app/apis/[[...slug]]/page.tsx +57 -0
  14. package/src/app/layout.tsx +26 -0
  15. package/src/app/llms-full.txt/route.ts +18 -0
  16. package/src/app/llms.txt/route.ts +15 -0
  17. package/src/app/providers.tsx +8 -0
  18. package/src/cli/commands/build.ts +32 -0
  19. package/src/cli/commands/dev.ts +33 -0
  20. package/src/cli/commands/init.ts +155 -0
  21. package/src/cli/commands/serve.ts +53 -0
  22. package/src/cli/commands/start.ts +33 -0
  23. package/src/cli/index.ts +21 -0
  24. package/src/cli/utils/config.ts +43 -0
  25. package/src/cli/utils/index.ts +3 -0
  26. package/src/cli/utils/process.ts +7 -0
  27. package/src/cli/utils/resolve.ts +4 -0
  28. package/src/cli/utils/scaffold.ts +131 -0
  29. package/src/components/api/code-snippets.module.css +7 -0
  30. package/src/components/api/code-snippets.tsx +76 -0
  31. package/src/components/api/endpoint-page.module.css +58 -0
  32. package/src/components/api/endpoint-page.tsx +283 -0
  33. package/src/components/api/field-row.module.css +126 -0
  34. package/src/components/api/field-row.tsx +204 -0
  35. package/src/components/api/field-section.module.css +24 -0
  36. package/src/components/api/field-section.tsx +100 -0
  37. package/src/components/api/index.ts +8 -0
  38. package/src/components/api/json-editor.module.css +9 -0
  39. package/src/components/api/json-editor.tsx +61 -0
  40. package/src/components/api/key-value-editor.module.css +13 -0
  41. package/src/components/api/key-value-editor.tsx +62 -0
  42. package/src/components/api/method-badge.module.css +4 -0
  43. package/src/components/api/method-badge.tsx +29 -0
  44. package/src/components/api/response-panel.module.css +8 -0
  45. package/src/components/api/response-panel.tsx +44 -0
  46. package/src/components/common/breadcrumb.tsx +3 -0
  47. package/src/components/common/button.tsx +3 -0
  48. package/src/components/common/callout.module.css +7 -0
  49. package/src/components/common/callout.tsx +27 -0
  50. package/src/components/common/code-block.tsx +3 -0
  51. package/src/components/common/dialog.tsx +3 -0
  52. package/src/components/common/index.ts +10 -0
  53. package/src/components/common/input-field.tsx +3 -0
  54. package/src/components/common/sidebar.tsx +3 -0
  55. package/src/components/common/switch.tsx +3 -0
  56. package/src/components/common/table.tsx +3 -0
  57. package/src/components/common/tabs.tsx +3 -0
  58. package/src/components/mdx/code.module.css +42 -0
  59. package/src/components/mdx/code.tsx +27 -0
  60. package/src/components/mdx/details.module.css +37 -0
  61. package/src/components/mdx/details.tsx +18 -0
  62. package/src/components/mdx/image.tsx +38 -0
  63. package/src/components/mdx/index.tsx +35 -0
  64. package/src/components/mdx/link.tsx +38 -0
  65. package/src/components/mdx/mermaid.module.css +9 -0
  66. package/src/components/mdx/mermaid.tsx +37 -0
  67. package/src/components/mdx/paragraph.module.css +8 -0
  68. package/src/components/mdx/paragraph.tsx +19 -0
  69. package/src/components/mdx/table.tsx +40 -0
  70. package/src/components/ui/breadcrumbs.tsx +72 -0
  71. package/src/components/ui/client-theme-switcher.tsx +18 -0
  72. package/src/components/ui/footer.module.css +27 -0
  73. package/src/components/ui/footer.tsx +30 -0
  74. package/src/components/ui/search.module.css +104 -0
  75. package/src/components/ui/search.tsx +202 -0
  76. package/src/lib/api-routes.ts +120 -0
  77. package/src/lib/config.ts +55 -0
  78. package/src/lib/get-llm-text.ts +10 -0
  79. package/src/lib/index.ts +2 -0
  80. package/src/lib/openapi.ts +188 -0
  81. package/src/lib/remark-unused-directives.ts +30 -0
  82. package/src/lib/schema.ts +99 -0
  83. package/src/lib/snippet-generators.ts +87 -0
  84. package/src/lib/source.ts +67 -0
  85. package/src/themes/default/Layout.module.css +81 -0
  86. package/src/themes/default/Layout.tsx +133 -0
  87. package/src/themes/default/Page.module.css +46 -0
  88. package/src/themes/default/Page.tsx +21 -0
  89. package/src/themes/default/Toc.module.css +48 -0
  90. package/src/themes/default/Toc.tsx +66 -0
  91. package/src/themes/default/font.ts +6 -0
  92. package/src/themes/default/index.ts +13 -0
  93. package/src/themes/paper/ChapterNav.module.css +71 -0
  94. package/src/themes/paper/ChapterNav.tsx +96 -0
  95. package/src/themes/paper/Layout.module.css +33 -0
  96. package/src/themes/paper/Layout.tsx +25 -0
  97. package/src/themes/paper/Page.module.css +174 -0
  98. package/src/themes/paper/Page.tsx +107 -0
  99. package/src/themes/paper/ReadingProgress.module.css +132 -0
  100. package/src/themes/paper/ReadingProgress.tsx +294 -0
  101. package/src/themes/paper/index.ts +8 -0
  102. package/src/themes/registry.ts +14 -0
  103. package/src/types/config.ts +69 -0
  104. package/src/types/content.ts +35 -0
  105. package/src/types/index.ts +3 -0
  106. package/src/types/theme.ts +22 -0
  107. package/tsconfig.json +30 -0
@@ -0,0 +1,100 @@
1
+ 'use client'
2
+
3
+ import { Flex, Text, Tabs, CodeBlock } from '@raystack/apsara'
4
+ import type { SchemaField } from '@/lib/schema'
5
+ import { FieldRow } from './field-row'
6
+ import { JsonEditor } from './json-editor'
7
+ import styles from './field-section.module.css'
8
+
9
+ interface FieldSectionProps {
10
+ title: string
11
+ label?: string
12
+ fields: SchemaField[]
13
+ locations?: Record<string, string>
14
+ jsonExample?: string
15
+ editableJson?: boolean
16
+ onJsonChange?: (value: string) => void
17
+ alwaysShow?: boolean
18
+ editable?: boolean
19
+ values?: Record<string, unknown>
20
+ onValuesChange?: (values: Record<string, unknown>) => void
21
+ children?: React.ReactNode
22
+ }
23
+
24
+ export function FieldSection({
25
+ title, label, fields, locations, jsonExample,
26
+ editableJson, onJsonChange, alwaysShow,
27
+ editable, values, onValuesChange, children,
28
+ }: FieldSectionProps) {
29
+ if (fields.length === 0 && !children && !alwaysShow) return null
30
+
31
+ const fieldsContent = fields.length > 0 ? (
32
+ <Flex direction="column">
33
+ {fields.map((field) => (
34
+ <FieldRow
35
+ key={field.name}
36
+ field={field}
37
+ location={locations?.[field.name]}
38
+ editable={editable}
39
+ value={values?.[field.name]}
40
+ onChange={editable ? (name, val) => {
41
+ onValuesChange?.({ ...values, [name]: val })
42
+ } : undefined}
43
+ />
44
+ ))}
45
+ </Flex>
46
+ ) : !children ? (
47
+ <Text size={2} className={styles.noFields}>No fields defined</Text>
48
+ ) : null
49
+
50
+ if (jsonExample !== undefined || alwaysShow) {
51
+ return (
52
+ <Flex direction="column">
53
+ <Flex align="center" justify="between" className={styles.header}>
54
+ <Text size={4} weight="medium">{title}</Text>
55
+ {label && <Text size={2} className={styles.label}>{label}</Text>}
56
+ </Flex>
57
+ <div className={styles.separator} />
58
+ <Tabs defaultValue="fields" className={styles.tabs}>
59
+ <Tabs.List>
60
+ <Tabs.Trigger value="fields">Fields</Tabs.Trigger>
61
+ <Tabs.Trigger value="json">JSON</Tabs.Trigger>
62
+ </Tabs.List>
63
+ <Tabs.Content value="fields">
64
+ {fieldsContent}
65
+ {children}
66
+ </Tabs.Content>
67
+ <Tabs.Content value="json">
68
+ {editableJson ? (
69
+ <JsonEditor
70
+ value={jsonExample ?? '{}'}
71
+ onChange={onJsonChange}
72
+ />
73
+ ) : (
74
+ <CodeBlock>
75
+ <CodeBlock.Header>
76
+ <CodeBlock.CopyButton />
77
+ </CodeBlock.Header>
78
+ <CodeBlock.Content>
79
+ <CodeBlock.Code language="json">{jsonExample ?? '{}'}</CodeBlock.Code>
80
+ </CodeBlock.Content>
81
+ </CodeBlock>
82
+ )}
83
+ </Tabs.Content>
84
+ </Tabs>
85
+ </Flex>
86
+ )
87
+ }
88
+
89
+ return (
90
+ <Flex direction="column">
91
+ <Flex align="center" justify="between" className={styles.header}>
92
+ <Text size={4} weight="medium">{title}</Text>
93
+ {label && <Text size={2} className={styles.label}>{label}</Text>}
94
+ </Flex>
95
+ <div className={styles.separator} />
96
+ {fieldsContent}
97
+ {children}
98
+ </Flex>
99
+ )
100
+ }
@@ -0,0 +1,8 @@
1
+ export { EndpointPage } from './endpoint-page'
2
+ export { MethodBadge } from './method-badge'
3
+ export { FieldSection } from './field-section'
4
+ export { FieldRow } from './field-row'
5
+ export { CodeSnippets } from './code-snippets'
6
+ export { ResponsePanel } from './response-panel'
7
+ export { JsonEditor } from './json-editor'
8
+ export { KeyValueEditor } from './key-value-editor'
@@ -0,0 +1,9 @@
1
+ .editor {
2
+ border: 1px solid var(--rs-color-border-base-primary);
3
+ border-radius: 8px;
4
+ overflow: hidden;
5
+ }
6
+
7
+ .editor .cm-editor {
8
+ font-size: var(--rs-font-size-small);
9
+ }
@@ -0,0 +1,61 @@
1
+ 'use client'
2
+
3
+ import { useRef, useEffect } from 'react'
4
+ import { useTheme } from '@raystack/apsara'
5
+ import { EditorView, basicSetup } from 'codemirror'
6
+ import { EditorState } from '@codemirror/state'
7
+ import { json } from '@codemirror/lang-json'
8
+ import { oneDark } from '@codemirror/theme-one-dark'
9
+ import styles from './json-editor.module.css'
10
+
11
+ interface JsonEditorProps {
12
+ value: string
13
+ onChange?: (value: string) => void
14
+ readOnly?: boolean
15
+ }
16
+
17
+ export function JsonEditor({ value, onChange, readOnly }: JsonEditorProps) {
18
+ const containerRef = useRef<HTMLDivElement>(null)
19
+ const viewRef = useRef<EditorView | null>(null)
20
+ const { theme } = useTheme()
21
+
22
+ const isDark = theme === 'dark'
23
+
24
+ useEffect(() => {
25
+ if (!containerRef.current) return
26
+
27
+ const extensions = [
28
+ basicSetup,
29
+ json(),
30
+ EditorView.lineWrapping,
31
+ ...(isDark ? [oneDark] : []),
32
+ ...(readOnly ? [EditorState.readOnly.of(true)] : []),
33
+ ...(onChange
34
+ ? [EditorView.updateListener.of((update) => {
35
+ if (update.docChanged) {
36
+ onChange(update.state.doc.toString())
37
+ }
38
+ })]
39
+ : []),
40
+ ]
41
+
42
+ const state = EditorState.create({ doc: value, extensions })
43
+ const view = new EditorView({ state, parent: containerRef.current })
44
+ viewRef.current = view
45
+
46
+ return () => view.destroy()
47
+ }, [isDark, readOnly, onChange])
48
+
49
+ useEffect(() => {
50
+ const view = viewRef.current
51
+ if (!view) return
52
+ const current = view.state.doc.toString()
53
+ if (value !== current) {
54
+ view.dispatch({
55
+ changes: { from: 0, to: current.length, insert: value },
56
+ })
57
+ }
58
+ }, [value])
59
+
60
+ return <div ref={containerRef} className={styles.editor} />
61
+ }
@@ -0,0 +1,13 @@
1
+ .editor {
2
+ padding: var(--rs-space-3) 0;
3
+ }
4
+
5
+ .row {
6
+ width: 100%;
7
+ }
8
+
9
+ .input {
10
+ flex: 1;
11
+ min-width: 0;
12
+ }
13
+
@@ -0,0 +1,62 @@
1
+ 'use client'
2
+
3
+ import { Flex, InputField, IconButton, Button } from '@raystack/apsara'
4
+ import { TrashIcon, PlusIcon } from '@heroicons/react/24/outline'
5
+ import styles from './key-value-editor.module.css'
6
+
7
+ export interface KeyValueEntry {
8
+ key: string
9
+ value: string
10
+ }
11
+
12
+ interface KeyValueEditorProps {
13
+ entries: KeyValueEntry[]
14
+ onChange: (entries: KeyValueEntry[]) => void
15
+ }
16
+
17
+ export function KeyValueEditor({ entries, onChange }: KeyValueEditorProps) {
18
+ const updateEntry = (index: number, field: 'key' | 'value', val: string) => {
19
+ const updated = [...entries]
20
+ updated[index] = { ...updated[index], [field]: val }
21
+ onChange(updated)
22
+ }
23
+
24
+ const removeEntry = (index: number) => {
25
+ onChange(entries.filter((_, i) => i !== index))
26
+ }
27
+
28
+ const addEntry = () => {
29
+ onChange([...entries, { key: '', value: '' }])
30
+ }
31
+
32
+ return (
33
+ <Flex direction="column" gap="small" className={styles.editor}>
34
+ {entries.map((entry, i) => (
35
+ <Flex key={i} align="center" gap="small" className={styles.row}>
36
+ <div className={styles.input}>
37
+ <InputField
38
+ size="small"
39
+ placeholder="Header name"
40
+ value={entry.key}
41
+ onChange={(e) => updateEntry(i, 'key', e.target.value)}
42
+ />
43
+ </div>
44
+ <div className={styles.input}>
45
+ <InputField
46
+ size="small"
47
+ placeholder="Value"
48
+ value={entry.value}
49
+ onChange={(e) => updateEntry(i, 'value', e.target.value)}
50
+ />
51
+ </div>
52
+ <IconButton size={1} aria-label={`Delete ${entry.key || 'entry'}`} onClick={() => removeEntry(i)}>
53
+ <TrashIcon width={14} height={14} />
54
+ </IconButton>
55
+ </Flex>
56
+ ))}
57
+ <Button variant="ghost" size="small" onClick={addEntry}>
58
+ <PlusIcon width={14} height={14} /> Add header
59
+ </Button>
60
+ </Flex>
61
+ )
62
+ }
@@ -0,0 +1,4 @@
1
+ .badge {
2
+ font-family: monospace;
3
+ letter-spacing: 0.5px;
4
+ }
@@ -0,0 +1,29 @@
1
+ 'use client'
2
+
3
+ import { Badge } from '@raystack/apsara'
4
+ import styles from './method-badge.module.css'
5
+
6
+ type BadgeVariant = 'accent' | 'danger' | 'success' | 'neutral' | 'warning' | 'gradient'
7
+
8
+ const methodVariants: Record<string, BadgeVariant> = {
9
+ GET: 'accent',
10
+ POST: 'success',
11
+ PUT: 'warning',
12
+ DELETE: 'danger',
13
+ PATCH: 'neutral',
14
+ }
15
+
16
+ interface MethodBadgeProps {
17
+ method: string
18
+ size?: 'micro' | 'small' | 'regular'
19
+ }
20
+
21
+ export function MethodBadge({ method, size = 'small' }: MethodBadgeProps) {
22
+ const variant = methodVariants[method.toUpperCase()] ?? 'neutral'
23
+
24
+ return (
25
+ <Badge variant={variant} size={size} className={styles.badge}>
26
+ {method.toUpperCase()}
27
+ </Badge>
28
+ )
29
+ }
@@ -0,0 +1,8 @@
1
+ .panel {
2
+ width: 100%;
3
+ }
4
+
5
+ /* stylelint-disable-next-line selector-pseudo-class-no-unknown */
6
+ .panel :global([class*="code-block-module_header"]) {
7
+ justify-content: space-between;
8
+ }
@@ -0,0 +1,44 @@
1
+ 'use client'
2
+
3
+ import { CodeBlock } from '@raystack/apsara'
4
+ import styles from './response-panel.module.css'
5
+
6
+ interface ResponsePanelProps {
7
+ responses: {
8
+ status: string
9
+ description?: string
10
+ jsonExample?: string
11
+ }[]
12
+ }
13
+
14
+ export function ResponsePanel({ responses }: ResponsePanelProps) {
15
+ const withExamples = responses.filter((r) => r.jsonExample)
16
+ if (withExamples.length === 0) return null
17
+
18
+ const defaultValue = withExamples[0].status
19
+
20
+ return (
21
+ <CodeBlock defaultValue={defaultValue} className={styles.panel}>
22
+ <CodeBlock.Header>
23
+ <CodeBlock.LanguageSelect>
24
+ <CodeBlock.LanguageSelectTrigger />
25
+ <CodeBlock.LanguageSelectContent>
26
+ {withExamples.map((resp) => (
27
+ <CodeBlock.LanguageSelectItem key={resp.status} value={resp.status}>
28
+ {resp.status} {resp.description ?? resp.status}
29
+ </CodeBlock.LanguageSelectItem>
30
+ ))}
31
+ </CodeBlock.LanguageSelectContent>
32
+ </CodeBlock.LanguageSelect>
33
+ <CodeBlock.CopyButton />
34
+ </CodeBlock.Header>
35
+ <CodeBlock.Content>
36
+ {withExamples.map((resp) => (
37
+ <CodeBlock.Code key={resp.status} value={resp.status} language="json">
38
+ {resp.jsonExample!}
39
+ </CodeBlock.Code>
40
+ ))}
41
+ </CodeBlock.Content>
42
+ </CodeBlock>
43
+ )
44
+ }
@@ -0,0 +1,3 @@
1
+ 'use client'
2
+
3
+ export { Breadcrumb } from '@raystack/apsara'
@@ -0,0 +1,3 @@
1
+ 'use client'
2
+
3
+ export { Button } from '@raystack/apsara'
@@ -0,0 +1,7 @@
1
+ .callout {
2
+ margin: var(--rs-space-5) 0;
3
+ }
4
+
5
+ .callout p:last-child {
6
+ margin-bottom: 0;
7
+ }
@@ -0,0 +1,27 @@
1
+ 'use client'
2
+
3
+ import React from 'react'
4
+ import { Callout } from '@raystack/apsara'
5
+ import styles from './callout.module.css'
6
+
7
+ function CalloutContainer(props: React.ComponentProps<typeof Callout>) {
8
+ return <Callout outline width="100%" className={styles.callout} {...props} />
9
+ }
10
+
11
+ function CalloutTitle({ children }: { children: React.ReactNode }) {
12
+ return <strong>{children}</strong>
13
+ }
14
+
15
+ function CalloutDescription({ children }: { children: React.ReactNode }) {
16
+ return <>{children}</>
17
+ }
18
+
19
+ function MdxBlockquote(props: React.ComponentProps<'blockquote'>) {
20
+ return (
21
+ <Callout type="grey" outline width="100%" className={styles.callout}>
22
+ {props.children}
23
+ </Callout>
24
+ )
25
+ }
26
+
27
+ export { Callout, CalloutContainer, CalloutTitle, CalloutDescription, MdxBlockquote }
@@ -0,0 +1,3 @@
1
+ 'use client'
2
+
3
+ export { CodeBlock } from '@raystack/apsara'
@@ -0,0 +1,3 @@
1
+ 'use client'
2
+
3
+ export { Dialog } from '@raystack/apsara'
@@ -0,0 +1,10 @@
1
+ export { Sidebar } from './sidebar'
2
+ export { Table } from './table'
3
+ export { Dialog } from './dialog'
4
+ export { InputField } from './input-field'
5
+ export { Tabs } from './tabs'
6
+ export { Breadcrumb } from './breadcrumb'
7
+ export { Button } from './button'
8
+ export { CodeBlock } from './code-block'
9
+ export { Callout } from './callout'
10
+ export { Switch } from './switch'
@@ -0,0 +1,3 @@
1
+ 'use client'
2
+
3
+ export { InputField } from '@raystack/apsara'
@@ -0,0 +1,3 @@
1
+ 'use client'
2
+
3
+ export { Sidebar } from '@raystack/apsara'
@@ -0,0 +1,3 @@
1
+ 'use client'
2
+
3
+ export { Switch } from '@raystack/apsara'
@@ -0,0 +1,3 @@
1
+ 'use client'
2
+
3
+ export { Table } from '@raystack/apsara'
@@ -0,0 +1,3 @@
1
+ 'use client'
2
+
3
+ export { Tabs } from '@raystack/apsara'
@@ -0,0 +1,42 @@
1
+ .codeBlock {
2
+ border-radius: var(--rs-radius-2);
3
+ overflow: hidden;
4
+ margin: var(--rs-space-5) 0;
5
+ border: 1px solid var(--rs-color-border-base-primary);
6
+ }
7
+
8
+ .codeHeader {
9
+ padding: var(--rs-space-3) var(--rs-space-4);
10
+ background: var(--rs-color-background-base-secondary);
11
+ border-bottom: 1px solid var(--rs-color-border-base-primary);
12
+ font-size: var(--rs-font-size-mini);
13
+ color: var(--rs-color-text-base-secondary);
14
+ }
15
+
16
+ .pre {
17
+ margin: 0;
18
+ padding: var(--rs-space-4);
19
+ overflow-x: auto;
20
+ background: var(--rs-color-background-base-primary);
21
+ }
22
+
23
+ .pre code {
24
+ font-family: var(--rs-font-mono);
25
+ font-size: var(--rs-font-size-small);
26
+ line-height: 1.6;
27
+ }
28
+
29
+ .pre code span {
30
+ color: var(--shiki-light);
31
+ }
32
+
33
+ :global([data-theme=dark]) .pre code span {
34
+ color: var(--shiki-dark);
35
+ }
36
+
37
+ .inlineCode {
38
+ background: var(--rs-color-background-neutral-secondary);
39
+ padding: var(--rs-space-1) var(--rs-space-2);
40
+ border-radius: var(--rs-radius-1);
41
+ font-size: inherit;
42
+ }
@@ -0,0 +1,27 @@
1
+ 'use client'
2
+
3
+ import type { ComponentProps } from 'react'
4
+ import styles from './code.module.css'
5
+
6
+ type PreProps = ComponentProps<'pre'> & {
7
+ 'data-language'?: string
8
+ title?: string
9
+ }
10
+
11
+ export function MdxCode({ children, className, ...props }: ComponentProps<'code'>) {
12
+ if (className || (typeof children === 'object')) {
13
+ return <code className={className} {...props}>{children}</code>
14
+ }
15
+ return <code className={styles.inlineCode} {...props}>{children}</code>
16
+ }
17
+
18
+ export function MdxPre({ children, title, className, ...props }: PreProps) {
19
+ return (
20
+ <div className={styles.codeBlock}>
21
+ {title && <div className={styles.codeHeader}>{title}</div>}
22
+ <pre className={`${styles.pre} ${className ?? ''}`} {...props}>
23
+ {children}
24
+ </pre>
25
+ </div>
26
+ )
27
+ }
@@ -0,0 +1,37 @@
1
+ .details {
2
+ border: 1px solid var(--rs-color-border-base-primary);
3
+ border-radius: var(--rs-radius-2);
4
+ margin: var(--rs-space-5) 0;
5
+ }
6
+
7
+ .summary {
8
+ padding: var(--rs-space-4) var(--rs-space-5);
9
+ cursor: pointer;
10
+ font-weight: 500;
11
+ font-size: var(--rs-font-size-small);
12
+ color: var(--rs-color-text-base-primary);
13
+ background: var(--rs-color-background-base-secondary);
14
+ list-style: none;
15
+ display: flex;
16
+ align-items: center;
17
+ gap: var(--rs-space-3);
18
+ }
19
+
20
+ .summary::-webkit-details-marker {
21
+ display: none;
22
+ }
23
+
24
+ .summary::before {
25
+ content: '▶';
26
+ font-size: 10px;
27
+ transition: transform 0.2s ease;
28
+ color: var(--rs-color-text-base-secondary);
29
+ }
30
+
31
+ .details[open] > .summary::before {
32
+ transform: rotate(90deg);
33
+ }
34
+
35
+ .content {
36
+ padding: var(--rs-space-4) var(--rs-space-5);
37
+ }
@@ -0,0 +1,18 @@
1
+ import type { ComponentProps } from 'react'
2
+ import styles from './details.module.css'
3
+
4
+ export function MdxDetails({ children, className, ...props }: ComponentProps<'details'>) {
5
+ return (
6
+ <details className={`${styles.details} ${className ?? ''}`} {...props}>
7
+ {children}
8
+ </details>
9
+ )
10
+ }
11
+
12
+ export function MdxSummary({ children, className, ...props }: ComponentProps<'summary'>) {
13
+ return (
14
+ <summary className={`${styles.summary} ${className ?? ''}`} {...props}>
15
+ {children}
16
+ </summary>
17
+ )
18
+ }
@@ -0,0 +1,38 @@
1
+ 'use client'
2
+
3
+ import NextImage from 'next/image'
4
+ import type { ComponentProps } from 'react'
5
+
6
+ type ImageProps = Omit<ComponentProps<'img'>, 'src'> & {
7
+ src?: string
8
+ width?: number | string
9
+ height?: number | string
10
+ }
11
+
12
+ export function Image({ src, alt, width, height, ...props }: ImageProps) {
13
+ if (!src || typeof src !== 'string') return null
14
+
15
+ const isExternal = src.startsWith('http://') || src.startsWith('https://')
16
+
17
+ if (isExternal) {
18
+ return (
19
+ // eslint-disable-next-line @next/next/no-img-element
20
+ <img
21
+ src={src}
22
+ alt={alt ?? ''}
23
+ width={width}
24
+ height={height}
25
+ {...props}
26
+ />
27
+ )
28
+ }
29
+
30
+ return (
31
+ <NextImage
32
+ src={src}
33
+ alt={alt ?? ''}
34
+ width={typeof width === 'string' ? parseInt(width, 10) : (width ?? 800)}
35
+ height={typeof height === 'string' ? parseInt(height, 10) : (height ?? 400)}
36
+ />
37
+ )
38
+ }
@@ -0,0 +1,35 @@
1
+ import type { MDXComponents } from 'mdx/types'
2
+ import { Image } from './image'
3
+ import { Link } from './link'
4
+ import { MdxTable, MdxThead, MdxTbody, MdxTr, MdxTh, MdxTd } from './table'
5
+ import { MdxPre, MdxCode } from './code'
6
+ import { MdxDetails, MdxSummary } from './details'
7
+ import { Mermaid } from './mermaid'
8
+ import { MdxParagraph } from './paragraph'
9
+ import { CalloutContainer, CalloutTitle, CalloutDescription, MdxBlockquote } from '@/components/common/callout'
10
+ import { Tabs } from '@raystack/apsara'
11
+
12
+ export const mdxComponents: MDXComponents = {
13
+ p: MdxParagraph,
14
+ img: Image,
15
+ a: Link,
16
+ table: MdxTable,
17
+ thead: MdxThead,
18
+ tbody: MdxTbody,
19
+ tr: MdxTr,
20
+ th: MdxTh,
21
+ td: MdxTd,
22
+ code: MdxCode,
23
+ pre: MdxPre,
24
+ details: MdxDetails,
25
+ summary: MdxSummary,
26
+ blockquote: MdxBlockquote,
27
+ Callout: CalloutContainer,
28
+ CalloutTitle,
29
+ CalloutDescription,
30
+ Tabs,
31
+ Mermaid,
32
+ }
33
+
34
+ export { Image } from './image'
35
+ export { Link } from './link'