@raystack/chronicle 0.11.2 → 0.11.3
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/package.json +1 -1
- package/src/components/ui/search.module.css +39 -23
- package/src/components/ui/search.tsx +46 -39
- package/src/server/api/search.ts +11 -10
- package/src/server/utils/api-markdown.ts +5 -0
- package/src/themes/default/Layout.module.css +5 -1
- package/src/themes/default/Layout.tsx +5 -2
- package/src/themes/default/Page.module.css +22 -4
- package/src/themes/paper/Page.module.css +5 -0
- package/src/types/content.ts +15 -0
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
.dialogContent {
|
|
2
|
-
border-radius: var(--rs-radius-
|
|
2
|
+
border-radius: var(--rs-radius-5);
|
|
3
3
|
padding: 0px;
|
|
4
4
|
width: 80%;
|
|
5
5
|
max-width: 600px;
|
|
6
6
|
position: fixed;
|
|
7
7
|
top: 20%;
|
|
8
|
+
overflow: clip;
|
|
9
|
+
box-shadow: var(--rs-shadow-floating);
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
.input {
|
|
@@ -13,7 +15,8 @@
|
|
|
13
15
|
|
|
14
16
|
.list {
|
|
15
17
|
max-height: 400px;
|
|
16
|
-
|
|
18
|
+
padding: 0 var(--rs-space-2);
|
|
19
|
+
gap: var(--rs-space-2);
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
.list :global([cmdk-group-heading]) {
|
|
@@ -25,35 +28,50 @@
|
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
.item {
|
|
28
|
-
|
|
29
|
-
padding: var(--rs-space-3);
|
|
31
|
+
padding: var(--rs-space-5) var(--rs-space-4);
|
|
30
32
|
gap: var(--rs-space-3);
|
|
31
|
-
border-radius: var(--rs-radius-
|
|
33
|
+
border-radius: var(--rs-radius-3);
|
|
32
34
|
cursor: pointer;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
|
|
36
37
|
.item[data-selected="true"] {
|
|
37
38
|
background: var(--rs-color-background-base-primary-hover);
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
.itemContent {
|
|
41
42
|
display: flex;
|
|
42
|
-
align-items:
|
|
43
|
-
gap:
|
|
43
|
+
align-items: flex-start;
|
|
44
|
+
gap: var(--rs-space-3);
|
|
44
45
|
flex: 1;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
.sectionBadge {
|
|
48
|
-
margin-left: auto;
|
|
49
|
-
flex-shrink: 0;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
48
|
.resultText {
|
|
53
49
|
display: flex;
|
|
54
50
|
flex-direction: column;
|
|
55
|
-
gap:
|
|
51
|
+
gap: var(--rs-space-2);
|
|
56
52
|
min-width: 0;
|
|
53
|
+
flex: 1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.breadcrumb {
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
gap: var(--rs-space-2);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.breadcrumbText {
|
|
63
|
+
font-family: var(--rs-font-body);
|
|
64
|
+
font-size: var(--rs-font-size-small);
|
|
65
|
+
font-weight: var(--rs-font-weight-medium);
|
|
66
|
+
line-height: var(--rs-line-height-small);
|
|
67
|
+
letter-spacing: var(--rs-letter-spacing-small);
|
|
68
|
+
color: var(--rs-color-foreground-base-primary);
|
|
69
|
+
white-space: nowrap;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.breadcrumbSeparator {
|
|
73
|
+
color: var(--rs-color-foreground-base-tertiary);
|
|
74
|
+
font-size: var(--rs-font-size-small);
|
|
57
75
|
}
|
|
58
76
|
|
|
59
77
|
.headingText {
|
|
@@ -64,10 +82,6 @@
|
|
|
64
82
|
color: var(--rs-color-foreground-accent-primary-hover);
|
|
65
83
|
}
|
|
66
84
|
|
|
67
|
-
.separator {
|
|
68
|
-
color: var(--rs-color-foreground-base-secondary);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
85
|
.pageText {
|
|
72
86
|
color: var(--rs-color-foreground-base-primary);
|
|
73
87
|
}
|
|
@@ -77,14 +91,15 @@
|
|
|
77
91
|
}
|
|
78
92
|
|
|
79
93
|
.icon {
|
|
80
|
-
width:
|
|
81
|
-
height:
|
|
94
|
+
width: 16px;
|
|
95
|
+
height: 16px;
|
|
82
96
|
color: var(--rs-color-foreground-base-secondary);
|
|
83
97
|
flex-shrink: 0;
|
|
98
|
+
margin-top: 1px;
|
|
84
99
|
}
|
|
85
100
|
|
|
86
101
|
.itemContent :global([class*="badge-module"]) {
|
|
87
|
-
min-width:
|
|
102
|
+
min-width: auto;
|
|
88
103
|
justify-content: center;
|
|
89
104
|
}
|
|
90
105
|
|
|
@@ -95,14 +110,15 @@
|
|
|
95
110
|
.snippetText {
|
|
96
111
|
font-size: var(--rs-font-size-mini);
|
|
97
112
|
line-height: var(--rs-line-height-mini);
|
|
98
|
-
|
|
113
|
+
letter-spacing: var(--rs-letter-spacing-mini);
|
|
114
|
+
color: var(--rs-color-foreground-base-secondary);
|
|
99
115
|
overflow: hidden;
|
|
100
116
|
text-overflow: ellipsis;
|
|
101
117
|
white-space: nowrap;
|
|
102
118
|
}
|
|
103
119
|
|
|
104
120
|
.matchHighlight {
|
|
105
|
-
color: var(--rs-color-foreground-
|
|
121
|
+
color: var(--rs-color-foreground-base-primary);
|
|
106
122
|
font-weight: var(--rs-font-weight-medium);
|
|
107
123
|
}
|
|
108
124
|
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DocumentIcon,
|
|
3
3
|
HashtagIcon,
|
|
4
|
-
MagnifyingGlassIcon
|
|
4
|
+
MagnifyingGlassIcon,
|
|
5
|
+
CodeBracketIcon,
|
|
6
|
+
ChevronRightIcon
|
|
5
7
|
} from '@heroicons/react/24/outline';
|
|
6
|
-
import {
|
|
8
|
+
import { Command, IconButton, Text } from '@raystack/apsara';
|
|
7
9
|
import { keepPreviousData, useQuery } from '@tanstack/react-query';
|
|
8
10
|
import { debounce } from 'lodash-es';
|
|
9
11
|
import { useCallback, useEffect, useMemo, useState, type ChangeEvent } from 'react';
|
|
10
12
|
import { useNavigate } from 'react-router';
|
|
11
13
|
import { MethodBadge } from '@/components/api/method-badge';
|
|
12
14
|
import { usePageContext } from '@/lib/page-context';
|
|
15
|
+
import { SearchMatchType } from '@/types';
|
|
13
16
|
import styles from './search.module.css';
|
|
14
17
|
|
|
15
18
|
interface SearchResult {
|
|
@@ -17,7 +20,7 @@ interface SearchResult {
|
|
|
17
20
|
url: string;
|
|
18
21
|
type: string;
|
|
19
22
|
content: string;
|
|
20
|
-
match?:
|
|
23
|
+
match?: SearchMatchType;
|
|
21
24
|
snippet?: string;
|
|
22
25
|
section?: string;
|
|
23
26
|
}
|
|
@@ -135,15 +138,7 @@ export function Search({ classNames }: SearchProps) {
|
|
|
135
138
|
onClick={() => onSelect(result.url)}
|
|
136
139
|
className={styles.item}
|
|
137
140
|
>
|
|
138
|
-
<
|
|
139
|
-
{getResultIcon(result)}
|
|
140
|
-
<Text className={styles.pageText}>
|
|
141
|
-
<HighlightedText
|
|
142
|
-
html={stripMethod(result.content)}
|
|
143
|
-
/>
|
|
144
|
-
</Text>
|
|
145
|
-
{result.section && <Badge size="small" className={styles.sectionBadge}>{result.section}</Badge>}
|
|
146
|
-
</div>
|
|
141
|
+
<SearchResultItem result={result} query="" />
|
|
147
142
|
</Command.Item>
|
|
148
143
|
))}
|
|
149
144
|
</Command.Group>
|
|
@@ -156,25 +151,7 @@ export function Search({ classNames }: SearchProps) {
|
|
|
156
151
|
onClick={() => onSelect(result.url)}
|
|
157
152
|
className={styles.item}
|
|
158
153
|
>
|
|
159
|
-
<
|
|
160
|
-
{getResultIcon(result)}
|
|
161
|
-
<div className={styles.resultText}>
|
|
162
|
-
<Text className={styles.pageText}>
|
|
163
|
-
<HighlightQuery text={stripMethod(result.content)} query={search} />
|
|
164
|
-
</Text>
|
|
165
|
-
{result.snippet && result.match === 'heading' && (
|
|
166
|
-
<Text className={styles.snippetText}>
|
|
167
|
-
# <HighlightQuery text={result.snippet} query={search} />
|
|
168
|
-
</Text>
|
|
169
|
-
)}
|
|
170
|
-
{result.snippet && result.match === 'body' && (
|
|
171
|
-
<Text className={styles.snippetText}>
|
|
172
|
-
<HighlightQuery text={result.snippet} query={search} />
|
|
173
|
-
</Text>
|
|
174
|
-
)}
|
|
175
|
-
</div>
|
|
176
|
-
{result.section && <Badge size="small" className={styles.sectionBadge}>{result.section}</Badge>}
|
|
177
|
-
</div>
|
|
154
|
+
<SearchResultItem result={result} query={search} />
|
|
178
155
|
</Command.Item>
|
|
179
156
|
))}
|
|
180
157
|
</Command.Content>
|
|
@@ -185,6 +162,38 @@ export function Search({ classNames }: SearchProps) {
|
|
|
185
162
|
);
|
|
186
163
|
}
|
|
187
164
|
|
|
165
|
+
function SearchResultItem({ result, query }: { result: SearchResult; query: string }) {
|
|
166
|
+
const method = extractMethod(result.content);
|
|
167
|
+
const title = stripMethod(result.content);
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<div className={styles.itemContent}>
|
|
171
|
+
{getResultIcon(result)}
|
|
172
|
+
<div className={styles.resultText}>
|
|
173
|
+
<div className={styles.breadcrumb}>
|
|
174
|
+
{result.section && (
|
|
175
|
+
<>
|
|
176
|
+
<span className={styles.breadcrumbText}>{result.section}</span>
|
|
177
|
+
<ChevronRightIcon width={12} height={12} className={styles.breadcrumbSeparator} />
|
|
178
|
+
</>
|
|
179
|
+
)}
|
|
180
|
+
{method && <MethodBadge method={method} size='micro' />}
|
|
181
|
+
<Text className={styles.breadcrumbText}>
|
|
182
|
+
{query ? <HighlightQuery text={title} query={query} /> : <HighlightedText html={title} />}
|
|
183
|
+
</Text>
|
|
184
|
+
</div>
|
|
185
|
+
{result.snippet && (
|
|
186
|
+
<Text className={styles.snippetText}>
|
|
187
|
+
{query
|
|
188
|
+
? <HighlightQuery text={result.snippet} query={query} />
|
|
189
|
+
: result.snippet}
|
|
190
|
+
</Text>
|
|
191
|
+
)}
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
188
197
|
function deduplicateByUrl(results: SearchResult[]): SearchResult[] {
|
|
189
198
|
const seen = new Set<string>();
|
|
190
199
|
return results.filter(r => {
|
|
@@ -233,15 +242,13 @@ function HighlightQuery({ text, query }: { text: string; query: string }) {
|
|
|
233
242
|
}
|
|
234
243
|
|
|
235
244
|
function getResultIcon(result: SearchResult): React.ReactNode {
|
|
236
|
-
if (
|
|
237
|
-
return
|
|
238
|
-
<DocumentIcon className={styles.icon} />
|
|
239
|
-
) : (
|
|
240
|
-
<HashtagIcon className={styles.icon} />
|
|
241
|
-
);
|
|
245
|
+
if (result.url.startsWith('/apis/')) {
|
|
246
|
+
return <CodeBracketIcon className={styles.icon} />;
|
|
242
247
|
}
|
|
243
|
-
|
|
244
|
-
|
|
248
|
+
if (result.match === SearchMatchType.Heading) {
|
|
249
|
+
return <HashtagIcon className={styles.icon} />;
|
|
250
|
+
}
|
|
251
|
+
return <DocumentIcon className={styles.icon} />;
|
|
245
252
|
}
|
|
246
253
|
|
|
247
254
|
function getPageTitle(url: string): string {
|
package/src/server/api/search.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { getApiConfigsForVersion, loadConfig } from '@/lib/config';
|
|
|
7
7
|
import { loadApiSpecs } from '@/lib/openapi';
|
|
8
8
|
import { extractFrontmatter, getPageSearchContent, getPagesForVersion } from '@/lib/source';
|
|
9
9
|
import { LATEST_CONTEXT, type VersionContext } from '@/lib/version-source';
|
|
10
|
+
import { SearchResultType, SearchMatchType } from '@/types';
|
|
10
11
|
|
|
11
12
|
interface SearchDocument {
|
|
12
13
|
id: string;
|
|
@@ -14,7 +15,7 @@ interface SearchDocument {
|
|
|
14
15
|
title: string;
|
|
15
16
|
headings: string;
|
|
16
17
|
body: string;
|
|
17
|
-
type:
|
|
18
|
+
type: SearchResultType;
|
|
18
19
|
section: string;
|
|
19
20
|
}
|
|
20
21
|
|
|
@@ -104,7 +105,7 @@ async function buildDocs(ctx: VersionContext): Promise<SearchDocument[]> {
|
|
|
104
105
|
title: fm.title,
|
|
105
106
|
headings,
|
|
106
107
|
body: [fm.description ?? '', body].join(' '),
|
|
107
|
-
type:
|
|
108
|
+
type: SearchResultType.Page,
|
|
108
109
|
section: entry?.label ?? dir ?? '',
|
|
109
110
|
});
|
|
110
111
|
}
|
|
@@ -128,7 +129,7 @@ async function buildDocs(ctx: VersionContext): Promise<SearchDocument[]> {
|
|
|
128
129
|
title: `${method.toUpperCase()} ${op.summary ?? opId}`,
|
|
129
130
|
headings: op.summary ?? opId,
|
|
130
131
|
body: [op.description ?? '', pathStr, method.toUpperCase()].join(' '),
|
|
131
|
-
type:
|
|
132
|
+
type: SearchResultType.Api,
|
|
132
133
|
section: spec.name,
|
|
133
134
|
});
|
|
134
135
|
}
|
|
@@ -144,9 +145,9 @@ function findMatch(
|
|
|
144
145
|
title: string,
|
|
145
146
|
headings: string,
|
|
146
147
|
body: string,
|
|
147
|
-
): { match:
|
|
148
|
+
): { match: SearchMatchType; snippet: string; slug?: string } {
|
|
148
149
|
if (title.toLowerCase().includes(query)) {
|
|
149
|
-
return { match:
|
|
150
|
+
return { match: SearchMatchType.Title, snippet: title };
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
const slugger = new GithubSlugger();
|
|
@@ -154,7 +155,7 @@ function findMatch(
|
|
|
154
155
|
for (const h of headingList) {
|
|
155
156
|
const slug = slugger.slug(h);
|
|
156
157
|
if (h.toLowerCase().includes(query)) {
|
|
157
|
-
return { match:
|
|
158
|
+
return { match: SearchMatchType.Heading, snippet: h, slug };
|
|
158
159
|
}
|
|
159
160
|
}
|
|
160
161
|
|
|
@@ -163,10 +164,10 @@ function findMatch(
|
|
|
163
164
|
const start = Math.max(0, idx - 40);
|
|
164
165
|
const end = Math.min(body.length, idx + query.length + 80);
|
|
165
166
|
const snippet = (start > 0 ? '...' : '') + body.slice(start, end).trim() + (end < body.length ? '...' : '');
|
|
166
|
-
return { match:
|
|
167
|
+
return { match: SearchMatchType.Body, snippet };
|
|
167
168
|
}
|
|
168
169
|
|
|
169
|
-
return { match:
|
|
170
|
+
return { match: SearchMatchType.Title, snippet: title };
|
|
170
171
|
}
|
|
171
172
|
|
|
172
173
|
function resolveCtx(tag: string | null): VersionContext {
|
|
@@ -218,8 +219,8 @@ export default defineHandler(async event => {
|
|
|
218
219
|
const queryLower = query.toLowerCase();
|
|
219
220
|
return Response.json((result.rows ?? []).map(r => {
|
|
220
221
|
const { match, snippet, slug } = findMatch(queryLower, r.title as string, r.headings as string, r.body as string);
|
|
221
|
-
const id = match ===
|
|
222
|
-
const url = match ===
|
|
222
|
+
const id = match === SearchMatchType.Heading && slug ? `${r.id}#${slug}` : r.id as string;
|
|
223
|
+
const url = match === SearchMatchType.Heading && slug ? `${r.url}#${slug}` : r.url as string;
|
|
223
224
|
return {
|
|
224
225
|
id,
|
|
225
226
|
url,
|
|
@@ -41,6 +41,11 @@ function generateApiMarkdown(
|
|
|
41
41
|
lines.push(operation.description)
|
|
42
42
|
lines.push('')
|
|
43
43
|
}
|
|
44
|
+
if (operation.externalDocs?.url) {
|
|
45
|
+
const label = operation.externalDocs.description || 'external documentation'
|
|
46
|
+
lines.push(`Read more about this operation in the [${label}](${operation.externalDocs.url}).`)
|
|
47
|
+
lines.push('')
|
|
48
|
+
}
|
|
44
49
|
lines.push(`\`${method}\` \`${path}\``)
|
|
45
50
|
lines.push('')
|
|
46
51
|
|
|
@@ -171,11 +171,15 @@
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
.groupItems {
|
|
174
|
-
padding-left:
|
|
174
|
+
padding-left: 0;
|
|
175
175
|
padding-bottom: var(--rs-space-3);
|
|
176
176
|
gap: 0;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
.navGroup:not([data-depth='0']) .groupItems {
|
|
180
|
+
padding-left: var(--rs-space-4);
|
|
181
|
+
}
|
|
182
|
+
|
|
179
183
|
.navGroup {
|
|
180
184
|
margin-top: 0;
|
|
181
185
|
}
|
|
@@ -28,8 +28,11 @@ import type { ThemeLayoutProps } from '@/types';
|
|
|
28
28
|
import styles from './Layout.module.css';
|
|
29
29
|
import { OpenInAI } from './OpenInAI';
|
|
30
30
|
import { SidebarLogo } from './SidebarLogo';
|
|
31
|
+
|
|
31
32
|
import { VersionSwitcher } from './VersionSwitcher';
|
|
32
33
|
|
|
34
|
+
const MAX_SIDEBAR_DEPTH = 3;
|
|
35
|
+
|
|
33
36
|
const iconMap: Record<string, React.ReactNode> = {
|
|
34
37
|
'rectangle-stack': <RectangleStackIcon width={16} height={16} />,
|
|
35
38
|
'method-get': <MethodBadge method='GET' size='micro' />,
|
|
@@ -249,7 +252,7 @@ function SidebarNode({
|
|
|
249
252
|
}
|
|
250
253
|
|
|
251
254
|
if (item.type === 'folder') {
|
|
252
|
-
if (depth >
|
|
255
|
+
if (depth > MAX_SIDEBAR_DEPTH) return null;
|
|
253
256
|
const icon = typeof item.icon === 'string' ? iconMap[item.icon] : item.icon;
|
|
254
257
|
const hasActiveChild = hasActiveDescendant(item, pathname);
|
|
255
258
|
return (
|
|
@@ -258,7 +261,7 @@ function SidebarNode({
|
|
|
258
261
|
data-depth={depth}
|
|
259
262
|
label={item.name?.toString() ?? ''}
|
|
260
263
|
leadingIcon={icon ?? undefined}
|
|
261
|
-
collapsible={depth
|
|
264
|
+
collapsible={depth >= 1}
|
|
262
265
|
defaultOpen={hasActiveChild}
|
|
263
266
|
classNames={{
|
|
264
267
|
items: styles.groupItems,
|
|
@@ -39,15 +39,22 @@
|
|
|
39
39
|
.content h1 {
|
|
40
40
|
font-size: var(--rs-font-size-t4);
|
|
41
41
|
line-height: var(--rs-line-height-t4);
|
|
42
|
-
margin
|
|
43
|
-
margin-bottom: var(--rs-space-10);
|
|
42
|
+
margin: var(--rs-space-10) 0;
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
.content h2 {
|
|
47
46
|
font-size: var(--rs-font-size-t3);
|
|
48
47
|
line-height: var(--rs-line-height-t3);
|
|
49
|
-
margin-top: var(--rs-space-
|
|
50
|
-
margin-bottom: var(--rs-space-
|
|
48
|
+
margin-top: var(--rs-space-10);
|
|
49
|
+
margin-bottom: var(--rs-space-7);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.content p + h2,
|
|
53
|
+
.content ul + h2,
|
|
54
|
+
.content ol + h2,
|
|
55
|
+
.content div + h2,
|
|
56
|
+
.content table + h2 {
|
|
57
|
+
margin-top: var(--rs-space-13);
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
.content h3 {
|
|
@@ -77,6 +84,7 @@
|
|
|
77
84
|
font-style: normal;
|
|
78
85
|
font-weight: var(--rs-font-weight-regular);
|
|
79
86
|
line-height: 171.429%;
|
|
87
|
+
margin-bottom: var(--rs-space-7);
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
.content ul,
|
|
@@ -90,6 +98,15 @@
|
|
|
90
98
|
margin: var(--rs-space-2) 0;
|
|
91
99
|
}
|
|
92
100
|
|
|
101
|
+
.content table td {
|
|
102
|
+
font-size: var(--rs-font-size-regular);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.content table th {
|
|
106
|
+
font-size: var(--rs-font-size-regular);
|
|
107
|
+
font-weight: var(--rs-font-weight-medium);
|
|
108
|
+
}
|
|
109
|
+
|
|
93
110
|
.content a {
|
|
94
111
|
font-size: inherit;
|
|
95
112
|
}
|
|
@@ -101,6 +118,7 @@
|
|
|
101
118
|
.content img {
|
|
102
119
|
max-width: 100%;
|
|
103
120
|
height: auto;
|
|
121
|
+
margin: var(--rs-space-7) 0;
|
|
104
122
|
}
|
|
105
123
|
|
|
106
124
|
.content table {
|
package/src/types/content.ts
CHANGED
|
@@ -24,6 +24,21 @@ export interface PageNav {
|
|
|
24
24
|
next: PageNavLink | null
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
export const SearchResultType = {
|
|
28
|
+
Page: 'page',
|
|
29
|
+
Api: 'api',
|
|
30
|
+
} as const;
|
|
31
|
+
|
|
32
|
+
export type SearchResultType = (typeof SearchResultType)[keyof typeof SearchResultType];
|
|
33
|
+
|
|
34
|
+
export const SearchMatchType = {
|
|
35
|
+
Title: 'title',
|
|
36
|
+
Heading: 'heading',
|
|
37
|
+
Body: 'body',
|
|
38
|
+
} as const;
|
|
39
|
+
|
|
40
|
+
export type SearchMatchType = (typeof SearchMatchType)[keyof typeof SearchMatchType];
|
|
41
|
+
|
|
27
42
|
export interface Page extends PageNav {
|
|
28
43
|
slug: string[]
|
|
29
44
|
frontmatter: Frontmatter
|