@dominikcz/greg 0.9.27
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/README.md +397 -0
- package/bin/greg.js +241 -0
- package/bin/init.js +351 -0
- package/bin/templates/docs/getting-started.md +47 -0
- package/bin/templates/docs/index.md +11 -0
- package/bin/templates/greg.config.js +39 -0
- package/bin/templates/greg.config.ts +38 -0
- package/bin/templates/index.html +16 -0
- package/bin/templates/src/App.svelte +5 -0
- package/bin/templates/src/app.css +20 -0
- package/bin/templates/src/main.js +9 -0
- package/bin/templates/svelte.config.js +1 -0
- package/bin/templates/tsconfig.json +21 -0
- package/bin/templates/vite.config.js +23 -0
- package/docs/__partials/markdown/examples/basic.md +4 -0
- package/docs/__partials/markdown/examples/diff.md +10 -0
- package/docs/__partials/markdown/examples/focus.md +5 -0
- package/docs/__partials/markdown/examples/language-title.md +3 -0
- package/docs/__partials/markdown/examples/line-highlighting.md +5 -0
- package/docs/__partials/markdown/examples/line-numbers.md +5 -0
- package/docs/__partials/note.md +4 -0
- package/docs/guide/__shared-warning.md +4 -0
- package/docs/guide/asset-handling.md +88 -0
- package/docs/guide/deploying.md +162 -0
- package/docs/guide/getting-started.md +334 -0
- package/docs/guide/index.md +23 -0
- package/docs/guide/localization.md +290 -0
- package/docs/guide/markdown/code.md +95 -0
- package/docs/guide/markdown/components-and-mermaid.md +43 -0
- package/docs/guide/markdown/containers.md +110 -0
- package/docs/guide/markdown/header-anchors.md +34 -0
- package/docs/guide/markdown/includes.md +84 -0
- package/docs/guide/markdown/index.md +20 -0
- package/docs/guide/markdown/inline-attributes.md +21 -0
- package/docs/guide/markdown/links-and-toc.md +64 -0
- package/docs/guide/markdown/math.md +54 -0
- package/docs/guide/markdown/syntax-highlighting.md +75 -0
- package/docs/guide/routing.md +150 -0
- package/docs/guide/using-svelte.md +88 -0
- package/docs/guide/versioning.md +281 -0
- package/docs/incompatibilities.md +48 -0
- package/docs/index.md +43 -0
- package/docs/reference/badge.md +100 -0
- package/docs/reference/carbon-ads.md +46 -0
- package/docs/reference/code-group.md +126 -0
- package/docs/reference/home-page.md +232 -0
- package/docs/reference/index.md +18 -0
- package/docs/reference/markdowndocs.md +275 -0
- package/docs/reference/outline.md +79 -0
- package/docs/reference/search.md +263 -0
- package/docs/reference/steps.md +200 -0
- package/docs/reference/team-page.md +189 -0
- package/docs/reference/theme.md +150 -0
- package/fakeDocsGenerator/generate_docs.js +310 -0
- package/package.json +92 -0
- package/scripts/build-versions.js +609 -0
- package/scripts/generate-static.js +79 -0
- package/scripts/render-markdown.js +420 -0
- package/src/lib/MarkdownDocs/AiChat.svelte +936 -0
- package/src/lib/MarkdownDocs/BackToTop.svelte +68 -0
- package/src/lib/MarkdownDocs/Breadcrumb.svelte +68 -0
- package/src/lib/MarkdownDocs/DocsNavigation.svelte +149 -0
- package/src/lib/MarkdownDocs/DocsSiteHeader.svelte +758 -0
- package/src/lib/MarkdownDocs/DocsVersionSwitcher.svelte +103 -0
- package/src/lib/MarkdownDocs/MarkdownDocs.svelte +2115 -0
- package/src/lib/MarkdownDocs/MarkdownRenderer.svelte +487 -0
- package/src/lib/MarkdownDocs/Outline.svelte +238 -0
- package/src/lib/MarkdownDocs/PrevNext.svelte +115 -0
- package/src/lib/MarkdownDocs/SearchModal.svelte +1241 -0
- package/src/lib/MarkdownDocs/TreeView.svelte +32 -0
- package/src/lib/MarkdownDocs/TreeViewItem.svelte +219 -0
- package/src/lib/MarkdownDocs/VersionOutdatedNotice.svelte +72 -0
- package/src/lib/MarkdownDocs/__tests__/codeDirectives.test.js +54 -0
- package/src/lib/MarkdownDocs/__tests__/common.test.js +41 -0
- package/src/lib/MarkdownDocs/__tests__/docsExamplesLint.test.js +77 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/docs/markdown/__partial-basic.md +3 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/docs/markdown/snippet.js +9 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/includes/part.md +11 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/includes/wrapper.md +5 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/snippets/sample.js +8 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/snippets/sample.md +5 -0
- package/src/lib/MarkdownDocs/__tests__/helpers.js +67 -0
- package/src/lib/MarkdownDocs/__tests__/localeUtils.test.js +204 -0
- package/src/lib/MarkdownDocs/__tests__/markdown.test.js +704 -0
- package/src/lib/MarkdownDocs/__tests__/markdownRendererRuntime.test.js +65 -0
- package/src/lib/MarkdownDocs/__tests__/searchIndexBuilder.test.js +117 -0
- package/src/lib/MarkdownDocs/__tests__/sqliteStore.test.js +202 -0
- package/src/lib/MarkdownDocs/__tests__/useRouter.test.js +16 -0
- package/src/lib/MarkdownDocs/ai/adapters/customAdapter.js +14 -0
- package/src/lib/MarkdownDocs/ai/adapters/customAdapter.ts +43 -0
- package/src/lib/MarkdownDocs/ai/adapters/ollamaAdapter.js +81 -0
- package/src/lib/MarkdownDocs/ai/adapters/ollamaAdapter.ts +116 -0
- package/src/lib/MarkdownDocs/ai/adapters/openaiAdapter.js +92 -0
- package/src/lib/MarkdownDocs/ai/adapters/openaiAdapter.ts +137 -0
- package/src/lib/MarkdownDocs/ai/aiProvider.ts +31 -0
- package/src/lib/MarkdownDocs/ai/characters.js +52 -0
- package/src/lib/MarkdownDocs/ai/characters.ts +69 -0
- package/src/lib/MarkdownDocs/ai/chunkStore.ts +25 -0
- package/src/lib/MarkdownDocs/ai/chunker.js +85 -0
- package/src/lib/MarkdownDocs/ai/chunker.ts +135 -0
- package/src/lib/MarkdownDocs/ai/docLinker.js +26 -0
- package/src/lib/MarkdownDocs/ai/docLinker.ts +36 -0
- package/src/lib/MarkdownDocs/ai/promptBuilder.js +33 -0
- package/src/lib/MarkdownDocs/ai/promptBuilder.ts +53 -0
- package/src/lib/MarkdownDocs/ai/ragPipeline.js +54 -0
- package/src/lib/MarkdownDocs/ai/ragPipeline.ts +106 -0
- package/src/lib/MarkdownDocs/ai/stores/memoryStore.js +88 -0
- package/src/lib/MarkdownDocs/ai/stores/memoryStore.ts +112 -0
- package/src/lib/MarkdownDocs/ai/stores/sqliteStore.ts +372 -0
- package/src/lib/MarkdownDocs/ai/types.ts +71 -0
- package/src/lib/MarkdownDocs/aiServer.js +288 -0
- package/src/lib/MarkdownDocs/codeDirectives.js +191 -0
- package/src/lib/MarkdownDocs/codeFenceInfo.js +45 -0
- package/src/lib/MarkdownDocs/codeGroup.ts +46 -0
- package/src/lib/MarkdownDocs/common.ts +47 -0
- package/src/lib/MarkdownDocs/docsUtils.js +281 -0
- package/src/lib/MarkdownDocs/index.plugins.js +22 -0
- package/src/lib/MarkdownDocs/layouts/LayoutDoc.svelte +8 -0
- package/src/lib/MarkdownDocs/layouts/LayoutHome.svelte +58 -0
- package/src/lib/MarkdownDocs/layouts/LayoutPage.svelte +9 -0
- package/src/lib/MarkdownDocs/loadGregConfig.js +82 -0
- package/src/lib/MarkdownDocs/localeUtils.ts +682 -0
- package/src/lib/MarkdownDocs/markdownRendererRuntime.ts +314 -0
- package/src/lib/MarkdownDocs/mermaidThemes.js +319 -0
- package/src/lib/MarkdownDocs/navigationUtils.js +22 -0
- package/src/lib/MarkdownDocs/rehypeCodeGroup.js +326 -0
- package/src/lib/MarkdownDocs/rehypeCodeTitle.js +96 -0
- package/src/lib/MarkdownDocs/rehypeToc.js +170 -0
- package/src/lib/MarkdownDocs/remarkCodeMeta.js +22 -0
- package/src/lib/MarkdownDocs/remarkContainers.js +329 -0
- package/src/lib/MarkdownDocs/remarkCustomAnchors.js +42 -0
- package/src/lib/MarkdownDocs/remarkEscapeSvelte.js +33 -0
- package/src/lib/MarkdownDocs/remarkGlobalComponents.js +65 -0
- package/src/lib/MarkdownDocs/remarkImports.js +461 -0
- package/src/lib/MarkdownDocs/remarkImportsBrowser.js +349 -0
- package/src/lib/MarkdownDocs/remarkInlineAttrs.js +95 -0
- package/src/lib/MarkdownDocs/remarkMathToHtml.js +138 -0
- package/src/lib/MarkdownDocs/searchIndexBuilder.js +497 -0
- package/src/lib/MarkdownDocs/searchServer.js +263 -0
- package/src/lib/MarkdownDocs/treeViewTypes.ts +11 -0
- package/src/lib/MarkdownDocs/useRouter.svelte.ts +114 -0
- package/src/lib/MarkdownDocs/useSplitter.svelte.ts +33 -0
- package/src/lib/MarkdownDocs/versioningDefaults.js +20 -0
- package/src/lib/MarkdownDocs/vitePluginAiServer.js +204 -0
- package/src/lib/MarkdownDocs/vitePluginCopyDocs.js +153 -0
- package/src/lib/MarkdownDocs/vitePluginFrontmatter.js +109 -0
- package/src/lib/MarkdownDocs/vitePluginGregConfig.js +108 -0
- package/src/lib/MarkdownDocs/vitePluginSearchIndex.js +57 -0
- package/src/lib/MarkdownDocs/vitePluginSearchServer.js +190 -0
- package/src/lib/components/Badge.svelte +59 -0
- package/src/lib/components/Button.svelte +138 -0
- package/src/lib/components/CarbonAds.svelte +99 -0
- package/src/lib/components/CodeGroup.svelte +102 -0
- package/src/lib/components/Feature.svelte +209 -0
- package/src/lib/components/Features.svelte +123 -0
- package/src/lib/components/Hero.svelte +399 -0
- package/src/lib/components/Image.svelte +128 -0
- package/src/lib/components/Link.svelte +105 -0
- package/src/lib/components/SocialLink.svelte +84 -0
- package/src/lib/components/SocialLinks.svelte +33 -0
- package/src/lib/components/Steps.svelte +143 -0
- package/src/lib/components/TeamMember.svelte +273 -0
- package/src/lib/components/TeamMembers.svelte +81 -0
- package/src/lib/components/TeamPage.svelte +65 -0
- package/src/lib/components/TeamPageSection.svelte +108 -0
- package/src/lib/components/TeamPageTitle.svelte +89 -0
- package/src/lib/components/index.js +24 -0
- package/src/lib/portal/context.js +12 -0
- package/src/lib/portal/index.js +3 -0
- package/src/lib/portal/portal.svelte +14 -0
- package/src/lib/portal/slot.svelte +8 -0
- package/src/lib/scss/__code.scss +128 -0
- package/src/lib/scss/__containers.scss +99 -0
- package/src/lib/scss/__markdown.scss +447 -0
- package/src/lib/scss/__scrollbar.scss +60 -0
- package/src/lib/scss/__steps.scss +100 -0
- package/src/lib/scss/__theme.scss +238 -0
- package/src/lib/scss/__toc.scss +55 -0
- package/src/lib/scss/__utilities.scss +7 -0
- package/src/lib/scss/greg.scss +9 -0
- package/src/lib/spinner/spinner.svelte +42 -0
- package/svelte.config.js +146 -0
- package/types/index.d.ts +456 -0
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
type TopNavItem = {
|
|
2
|
+
text: string;
|
|
3
|
+
link?: string;
|
|
4
|
+
target?: string;
|
|
5
|
+
items?: TopNavItem[];
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
type ThemeableImage =
|
|
9
|
+
| string
|
|
10
|
+
| { src: string; alt?: string }
|
|
11
|
+
| { light: string; dark: string; alt?: string };
|
|
12
|
+
|
|
13
|
+
type SocialLinkItem = {
|
|
14
|
+
icon: string | { svg: string };
|
|
15
|
+
link: string;
|
|
16
|
+
ariaLabel?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type OutlineLevel = false | number | [number, number] | "deep";
|
|
20
|
+
type OutlineOption = OutlineLevel | { level?: OutlineLevel; label?: string };
|
|
21
|
+
|
|
22
|
+
type BadgeSpec = string | { text: string; type?: string };
|
|
23
|
+
|
|
24
|
+
type SidebarItem = {
|
|
25
|
+
text: string;
|
|
26
|
+
link?: string;
|
|
27
|
+
items?: SidebarItem[];
|
|
28
|
+
auto?: string;
|
|
29
|
+
badge?: BadgeSpec;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type LocaleThemeConfig = {
|
|
33
|
+
nav?: TopNavItem[];
|
|
34
|
+
sidebar?: "auto" | SidebarItem[];
|
|
35
|
+
outline?: OutlineOption | boolean;
|
|
36
|
+
lastUpdatedText?: string;
|
|
37
|
+
langMenuLabel?: string;
|
|
38
|
+
sidebarMenuLabel?: string;
|
|
39
|
+
skipToContentLabel?: string;
|
|
40
|
+
returnToTopLabel?: string;
|
|
41
|
+
darkModeSwitchLabel?: string;
|
|
42
|
+
lightModeSwitchTitle?: string;
|
|
43
|
+
darkModeSwitchTitle?: string;
|
|
44
|
+
docFooter?: {
|
|
45
|
+
prev?: string | false;
|
|
46
|
+
next?: string | false;
|
|
47
|
+
};
|
|
48
|
+
siteTitle?: string | false;
|
|
49
|
+
logo?: ThemeableImage;
|
|
50
|
+
socialLinks?: SocialLinkItem[];
|
|
51
|
+
editLink?: {
|
|
52
|
+
pattern: string;
|
|
53
|
+
text?: string;
|
|
54
|
+
};
|
|
55
|
+
footer?: {
|
|
56
|
+
message?: string;
|
|
57
|
+
copyright?: string;
|
|
58
|
+
};
|
|
59
|
+
aside?: boolean | "left";
|
|
60
|
+
lastUpdated?: {
|
|
61
|
+
text?: string;
|
|
62
|
+
formatOptions?: Intl.DateTimeFormatOptions & { forceLocale?: boolean };
|
|
63
|
+
};
|
|
64
|
+
externalLinkIcon?: boolean;
|
|
65
|
+
search?: {
|
|
66
|
+
locales?: Record<
|
|
67
|
+
string,
|
|
68
|
+
{
|
|
69
|
+
button?: {
|
|
70
|
+
buttonText?: string;
|
|
71
|
+
buttonAriaLabel?: string;
|
|
72
|
+
};
|
|
73
|
+
modal?: {
|
|
74
|
+
displayDetails?: string;
|
|
75
|
+
resetButtonTitle?: string;
|
|
76
|
+
backButtonTitle?: string;
|
|
77
|
+
noResultsText?: string;
|
|
78
|
+
footer?: {
|
|
79
|
+
selectText?: string;
|
|
80
|
+
navigateText?: string;
|
|
81
|
+
closeText?: string;
|
|
82
|
+
};
|
|
83
|
+
searchBox?: {
|
|
84
|
+
placeholder?: string;
|
|
85
|
+
resetButtonTitle?: string;
|
|
86
|
+
resetButtonAriaLabel?: string;
|
|
87
|
+
cancelButtonText?: string;
|
|
88
|
+
cancelButtonAriaLabel?: string;
|
|
89
|
+
};
|
|
90
|
+
startScreen?: {
|
|
91
|
+
recentSearchesTitle?: string;
|
|
92
|
+
noRecentSearchesText?: string;
|
|
93
|
+
saveRecentSearchButtonTitle?: string;
|
|
94
|
+
removeRecentSearchButtonTitle?: string;
|
|
95
|
+
favoriteSearchesTitle?: string;
|
|
96
|
+
removeFavoriteSearchButtonTitle?: string;
|
|
97
|
+
};
|
|
98
|
+
errorScreen?: {
|
|
99
|
+
titleText?: string;
|
|
100
|
+
helpText?: string;
|
|
101
|
+
};
|
|
102
|
+
loadingScreen?: {
|
|
103
|
+
loadingText?: string;
|
|
104
|
+
};
|
|
105
|
+
ai?: {
|
|
106
|
+
tabLabel?: string;
|
|
107
|
+
placeholder?: string;
|
|
108
|
+
startText?: string;
|
|
109
|
+
loadingText?: string;
|
|
110
|
+
errorText?: string;
|
|
111
|
+
sourcesLabel?: string;
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
>;
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export type LocaleConfig = {
|
|
120
|
+
lang?: string;
|
|
121
|
+
dir?: "ltr" | "rtl";
|
|
122
|
+
title?: string;
|
|
123
|
+
description?: string;
|
|
124
|
+
label?: string;
|
|
125
|
+
link?: string;
|
|
126
|
+
themeConfig?: LocaleThemeConfig;
|
|
127
|
+
[key: string]: unknown;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export type LocaleEntry = {
|
|
131
|
+
key: string;
|
|
132
|
+
segment: string;
|
|
133
|
+
srcDir: string;
|
|
134
|
+
config: LocaleConfig;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
type ResolveDefaults = {
|
|
138
|
+
mainTitle: string;
|
|
139
|
+
nav: TopNavItem[];
|
|
140
|
+
sidebar: "auto" | SidebarItem[];
|
|
141
|
+
outline: OutlineOption | boolean;
|
|
142
|
+
langMenuLabel?: string;
|
|
143
|
+
sidebarMenuLabel?: string;
|
|
144
|
+
skipToContentLabel?: string;
|
|
145
|
+
returnToTopLabel?: string;
|
|
146
|
+
darkModeSwitchLabel?: string;
|
|
147
|
+
lightModeSwitchTitle?: string;
|
|
148
|
+
darkModeSwitchTitle?: string;
|
|
149
|
+
docFooter?: {
|
|
150
|
+
prev?: string | false;
|
|
151
|
+
next?: string | false;
|
|
152
|
+
};
|
|
153
|
+
siteTitle?: string | false;
|
|
154
|
+
logo?: ThemeableImage;
|
|
155
|
+
socialLinks?: SocialLinkItem[];
|
|
156
|
+
editLink?: {
|
|
157
|
+
pattern: string;
|
|
158
|
+
text?: string;
|
|
159
|
+
};
|
|
160
|
+
footer?: {
|
|
161
|
+
message?: string;
|
|
162
|
+
copyright?: string;
|
|
163
|
+
};
|
|
164
|
+
aside?: boolean | "left";
|
|
165
|
+
lastUpdated?: {
|
|
166
|
+
text?: string;
|
|
167
|
+
formatOptions?: Intl.DateTimeFormatOptions & { forceLocale?: boolean };
|
|
168
|
+
};
|
|
169
|
+
externalLinkIcon?: boolean;
|
|
170
|
+
searchButtonLabel?: string;
|
|
171
|
+
searchModalLabel?: string;
|
|
172
|
+
searchPlaceholder?: string;
|
|
173
|
+
searchLoadingText?: string;
|
|
174
|
+
searchErrorText?: string;
|
|
175
|
+
searchSearchingText?: string;
|
|
176
|
+
searchNoResultsText?: string;
|
|
177
|
+
searchStartText?: string;
|
|
178
|
+
searchResultsAriaLabel?: string;
|
|
179
|
+
searchNavigateText?: string;
|
|
180
|
+
searchSelectText?: string;
|
|
181
|
+
searchCloseText?: string;
|
|
182
|
+
aiTabLabel?: string;
|
|
183
|
+
aiPlaceholder?: string;
|
|
184
|
+
aiLoadingText?: string;
|
|
185
|
+
aiErrorText?: string;
|
|
186
|
+
aiStartText?: string;
|
|
187
|
+
aiSourcesLabel?: string;
|
|
188
|
+
aiClearChatLabel?: string;
|
|
189
|
+
aiSendLabel?: string;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const EXTERNAL_LINK_RE = /^(?:[a-z][a-z\d+\-.]*:|\/\/)/i;
|
|
193
|
+
|
|
194
|
+
// ── Built-in locale strings ──────────────────────────────────────────────────
|
|
195
|
+
// Provides default UI translations per language tag so users don't need to
|
|
196
|
+
// repeat common strings in their greg.config.js.
|
|
197
|
+
// Keys: lowercase BCP-47 language tag (full e.g. "en-us" or base e.g. "en").
|
|
198
|
+
// Lookup order: full tag → base tag → empty (falls through to defaults).
|
|
199
|
+
//
|
|
200
|
+
// Structure mirrors LocaleThemeConfig. The `search` key holds the per-locale
|
|
201
|
+
// search config (button + modal) without the `locales[key]` wrapping — that
|
|
202
|
+
// wrapping is injected dynamically in getBuiltInThemeConfig() using the actual
|
|
203
|
+
// locale key, so the data stays key-agnostic.
|
|
204
|
+
|
|
205
|
+
type SearchLocaleModal = {
|
|
206
|
+
noResultsText?: string;
|
|
207
|
+
footer?: {
|
|
208
|
+
selectText?: string;
|
|
209
|
+
navigateText?: string;
|
|
210
|
+
closeText?: string;
|
|
211
|
+
};
|
|
212
|
+
searchBox?: { placeholder?: string };
|
|
213
|
+
startScreen?: { noRecentSearchesText?: string };
|
|
214
|
+
errorScreen?: { titleText?: string };
|
|
215
|
+
loadingScreen?: { loadingText?: string };
|
|
216
|
+
/** Greg-specific: "Searching…" status text. */
|
|
217
|
+
searchingText?: string;
|
|
218
|
+
/** Greg-specific: aria-label for the results list. */
|
|
219
|
+
resultsAriaLabel?: string;
|
|
220
|
+
ai?: {
|
|
221
|
+
tabLabel?: string;
|
|
222
|
+
placeholder?: string;
|
|
223
|
+
startText?: string;
|
|
224
|
+
loadingText?: string;
|
|
225
|
+
errorText?: string;
|
|
226
|
+
sourcesLabel?: string;
|
|
227
|
+
clearChatLabel?: string;
|
|
228
|
+
sendLabel?: string;
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
type SearchLocaleEntry = {
|
|
233
|
+
button?: { buttonText?: string; buttonAriaLabel?: string };
|
|
234
|
+
modal?: SearchLocaleModal;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
/** LocaleThemeConfig shape, but with `search` stored one level shallower
|
|
238
|
+
* (the locale key is unknown at definition time and injected on retrieval). */
|
|
239
|
+
type BuiltInLocaleThemeConfig = Omit<LocaleThemeConfig, "search"> & {
|
|
240
|
+
search?: SearchLocaleEntry;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const BUILT_IN_LOCALE_STRINGS: Record<string, BuiltInLocaleThemeConfig> = {
|
|
244
|
+
en: {
|
|
245
|
+
langMenuLabel: "Change language",
|
|
246
|
+
sidebarMenuLabel: "Menu",
|
|
247
|
+
skipToContentLabel: "Skip to content",
|
|
248
|
+
returnToTopLabel: "Back to top",
|
|
249
|
+
darkModeSwitchLabel: "Appearance",
|
|
250
|
+
lightModeSwitchTitle: "Switch to light theme",
|
|
251
|
+
darkModeSwitchTitle: "Switch to dark theme",
|
|
252
|
+
lastUpdatedText: "Last updated:",
|
|
253
|
+
docFooter: { prev: "Previous page", next: "Next page" },
|
|
254
|
+
search: {
|
|
255
|
+
button: { buttonText: "Search...", buttonAriaLabel: "Search" },
|
|
256
|
+
modal: {
|
|
257
|
+
searchBox: { placeholder: "Search docs..." },
|
|
258
|
+
loadingScreen: { loadingText: "Loading index..." },
|
|
259
|
+
errorScreen: { titleText: "Failed to load search index." },
|
|
260
|
+
noResultsText: "No results for",
|
|
261
|
+
startScreen: { noRecentSearchesText: "Start typing to search across all documentation." },
|
|
262
|
+
footer: { navigateText: "navigate", selectText: "open", closeText: "close" },
|
|
263
|
+
searchingText: "Searching...",
|
|
264
|
+
resultsAriaLabel: "Search results",
|
|
265
|
+
ai: {
|
|
266
|
+
tabLabel: "Ask AI",
|
|
267
|
+
placeholder: "Ask a question about the docs\u2026",
|
|
268
|
+
loadingText: "Thinking\u2026",
|
|
269
|
+
errorText: "Something went wrong. Please try again.",
|
|
270
|
+
startText: "Ask me anything about this documentation. My answers are based exclusively on the docs.",
|
|
271
|
+
sourcesLabel: "Sources",
|
|
272
|
+
clearChatLabel: "Clear chat",
|
|
273
|
+
sendLabel: "Send",
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
pl: {
|
|
279
|
+
langMenuLabel: "Zmień język",
|
|
280
|
+
sidebarMenuLabel: "Menu",
|
|
281
|
+
skipToContentLabel: "Przejdź do treści",
|
|
282
|
+
returnToTopLabel: "Wróć do góry",
|
|
283
|
+
darkModeSwitchLabel: "Wygląd",
|
|
284
|
+
lightModeSwitchTitle: "Przełącz na jasny motyw",
|
|
285
|
+
darkModeSwitchTitle: "Przełącz na ciemny motyw",
|
|
286
|
+
lastUpdatedText: "Ostatnia aktualizacja:",
|
|
287
|
+
docFooter: { prev: "Poprzednia strona", next: "Następna strona" },
|
|
288
|
+
search: {
|
|
289
|
+
button: { buttonText: "Szukaj...", buttonAriaLabel: "Wyszukiwarka" },
|
|
290
|
+
modal: {
|
|
291
|
+
searchBox: { placeholder: "Szukaj w dokumentacji..." },
|
|
292
|
+
loadingScreen: { loadingText: "Wczytywanie indeksu..." },
|
|
293
|
+
errorScreen: { titleText: "Nie udało się wczytać indeksu wyszukiwania." },
|
|
294
|
+
noResultsText: "Brak wyników dla",
|
|
295
|
+
startScreen: { noRecentSearchesText: "Zacznij pisać, aby przeszukać całą dokumentację." },
|
|
296
|
+
footer: { navigateText: "nawiguj", selectText: "otwórz", closeText: "zamknij" },
|
|
297
|
+
searchingText: "Szukam...",
|
|
298
|
+
resultsAriaLabel: "Wyniki wyszukiwania",
|
|
299
|
+
ai: {
|
|
300
|
+
tabLabel: "Zapytaj AI",
|
|
301
|
+
placeholder: "Zadaj pytanie o dokumentację\u2026",
|
|
302
|
+
loadingText: "Myślę\u2026",
|
|
303
|
+
errorText: "Coś poszło nie tak. Spróbuj ponownie.",
|
|
304
|
+
startText: "Zapytaj mnie o cokolwiek z tej dokumentacji. Moje odpowiedzi bazują wyłącznie na dokumentacji.",
|
|
305
|
+
sourcesLabel: "Źródła",
|
|
306
|
+
clearChatLabel: "Wyczyść czat",
|
|
307
|
+
sendLabel: "Wyślij",
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
/** Returns built-in locale strings as a proper LocaleThemeConfig, with the
|
|
315
|
+
* search locale entry nested under the given localeKey. */
|
|
316
|
+
function getBuiltInThemeConfig(lang?: string, localeKey = "/"): LocaleThemeConfig {
|
|
317
|
+
if (!lang) return {};
|
|
318
|
+
const normalized = lang.toLowerCase();
|
|
319
|
+
const base = normalized.split("-")[0];
|
|
320
|
+
const data = BUILT_IN_LOCALE_STRINGS[normalized] ?? BUILT_IN_LOCALE_STRINGS[base];
|
|
321
|
+
if (!data) return {};
|
|
322
|
+
const { search, ...rest } = data;
|
|
323
|
+
if (!search) return rest;
|
|
324
|
+
return { ...rest, search: { locales: { [localeKey]: search } } };
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** Deep-merges `override` into `base` (explicit values win). Arrays are
|
|
328
|
+
* replaced wholesale; plain objects are merged recursively. */
|
|
329
|
+
function deepMerge<T>(base: T, override: T): T {
|
|
330
|
+
if (override === undefined || override === null) return base;
|
|
331
|
+
if (base === undefined || base === null) return override;
|
|
332
|
+
if (typeof override !== "object" || typeof base !== "object") return override;
|
|
333
|
+
if (Array.isArray(override) || Array.isArray(base)) {
|
|
334
|
+
return (Array.isArray(override) ? override : base) as T;
|
|
335
|
+
}
|
|
336
|
+
const result: Record<string, unknown> = { ...(base as Record<string, unknown>) };
|
|
337
|
+
for (const key of Object.keys(override as Record<string, unknown>)) {
|
|
338
|
+
const ov = (override as Record<string, unknown>)[key];
|
|
339
|
+
if (ov === undefined) continue;
|
|
340
|
+
result[key] = deepMerge(result[key], ov);
|
|
341
|
+
}
|
|
342
|
+
return result as T;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function splitPathAndSuffix(raw: string): { path: string; suffix: string } {
|
|
346
|
+
const value = String(raw || "").trim();
|
|
347
|
+
const hashIndex = value.indexOf("#");
|
|
348
|
+
const queryIndex = value.indexOf("?");
|
|
349
|
+
const firstSuffixIndex =
|
|
350
|
+
hashIndex === -1
|
|
351
|
+
? queryIndex
|
|
352
|
+
: queryIndex === -1
|
|
353
|
+
? hashIndex
|
|
354
|
+
: Math.min(hashIndex, queryIndex);
|
|
355
|
+
|
|
356
|
+
if (firstSuffixIndex === -1) {
|
|
357
|
+
return { path: value, suffix: "" };
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return {
|
|
361
|
+
path: value.slice(0, firstSuffixIndex),
|
|
362
|
+
suffix: value.slice(firstSuffixIndex),
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function resolveThemeLink(
|
|
367
|
+
rawLink: string,
|
|
368
|
+
args: {
|
|
369
|
+
baseSrcDir: string;
|
|
370
|
+
localeSrcDir: string;
|
|
371
|
+
localeSegment: string;
|
|
372
|
+
},
|
|
373
|
+
): string {
|
|
374
|
+
const value = String(rawLink || "").trim();
|
|
375
|
+
if (!value) return value;
|
|
376
|
+
if (EXTERNAL_LINK_RE.test(value)) return value;
|
|
377
|
+
if (value.startsWith("#") || value.startsWith("?")) return value;
|
|
378
|
+
|
|
379
|
+
const { path, suffix } = splitPathAndSuffix(value);
|
|
380
|
+
if (!path) return suffix || value;
|
|
381
|
+
|
|
382
|
+
const baseSrcDir = normalizeSrcDir(args.baseSrcDir);
|
|
383
|
+
const localeSrcDir = normalizeSrcDir(args.localeSrcDir);
|
|
384
|
+
const localeSegment = args.localeSegment
|
|
385
|
+
? normalizeSrcDir(args.localeSegment)
|
|
386
|
+
: "";
|
|
387
|
+
|
|
388
|
+
if (path === "/") return `${localeSrcDir}${suffix}`;
|
|
389
|
+
|
|
390
|
+
if (path.startsWith(baseSrcDir + "/") || path === baseSrcDir) {
|
|
391
|
+
return `${normalizeSrcDir(path)}${suffix}`;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (
|
|
395
|
+
localeSegment &&
|
|
396
|
+
(path === localeSegment || path.startsWith(localeSegment + "/"))
|
|
397
|
+
) {
|
|
398
|
+
return `${normalizeSrcDir(baseSrcDir + path)}${suffix}`;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (path.startsWith("/")) {
|
|
402
|
+
return `${normalizeSrcDir(localeSrcDir + path)}${suffix}`;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return `${normalizeSrcDir(`${localeSrcDir}/${path}`)}${suffix}`;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function normalizeTopNavLinks(
|
|
409
|
+
nav: TopNavItem[],
|
|
410
|
+
args: {
|
|
411
|
+
baseSrcDir: string;
|
|
412
|
+
localeSrcDir: string;
|
|
413
|
+
localeSegment: string;
|
|
414
|
+
},
|
|
415
|
+
): TopNavItem[] {
|
|
416
|
+
return (nav ?? []).map((item) => ({
|
|
417
|
+
...item,
|
|
418
|
+
link:
|
|
419
|
+
typeof item.link === "string"
|
|
420
|
+
? resolveThemeLink(item.link, args)
|
|
421
|
+
: item.link,
|
|
422
|
+
items: item.items ? normalizeTopNavLinks(item.items, args) : item.items,
|
|
423
|
+
}));
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function normalizeSidebarLinks(
|
|
427
|
+
sidebar: SidebarItem[],
|
|
428
|
+
args: {
|
|
429
|
+
baseSrcDir: string;
|
|
430
|
+
localeSrcDir: string;
|
|
431
|
+
localeSegment: string;
|
|
432
|
+
},
|
|
433
|
+
): SidebarItem[] {
|
|
434
|
+
return (sidebar ?? []).map((item) => ({
|
|
435
|
+
...item,
|
|
436
|
+
link:
|
|
437
|
+
typeof item.link === "string"
|
|
438
|
+
? resolveThemeLink(item.link, args)
|
|
439
|
+
: item.link,
|
|
440
|
+
items: item.items
|
|
441
|
+
? normalizeSidebarLinks(item.items, args)
|
|
442
|
+
: item.items,
|
|
443
|
+
}));
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function resolveSearchLocaleConfig(
|
|
447
|
+
themeConfig: LocaleThemeConfig,
|
|
448
|
+
localeKey: string,
|
|
449
|
+
) {
|
|
450
|
+
const localesMap = themeConfig.search?.locales ?? {};
|
|
451
|
+
const normalized = normalizeLocaleKey(localeKey);
|
|
452
|
+
const trimmed = normalized.replace(/^\/+|\/+$/g, "");
|
|
453
|
+
const candidateKeys =
|
|
454
|
+
normalized === "/"
|
|
455
|
+
? ["/", "root"]
|
|
456
|
+
: [normalized, normalized.replace(/\/$/, ""), trimmed];
|
|
457
|
+
|
|
458
|
+
for (const key of candidateKeys) {
|
|
459
|
+
const value = localesMap[key];
|
|
460
|
+
if (value) return value;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return undefined;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
export function normalizeSrcDir(path: string): string {
|
|
467
|
+
const value = String(path || "").trim();
|
|
468
|
+
if (!value || value === "/") return "/";
|
|
469
|
+
return "/" + value.replace(/^\/+|\/+$/g, "");
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
export function normalizeLocaleKey(key: string): string {
|
|
473
|
+
const value = String(key || "").trim();
|
|
474
|
+
if (!value || value === "root" || value === "/") return "/";
|
|
475
|
+
const withSlashes = `/${value.replace(/^\/+|\/+$/g, "")}/`;
|
|
476
|
+
return withSlashes === "//" ? "/" : withSlashes;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
export function keyToLocaleSegment(key: string): string {
|
|
480
|
+
const normalized = normalizeLocaleKey(key);
|
|
481
|
+
if (normalized === "/") return "";
|
|
482
|
+
return normalized.replace(/\/$/, "");
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
export function getLocaleEntries(
|
|
486
|
+
baseSrcDir: string,
|
|
487
|
+
locales: Record<string, LocaleConfig>,
|
|
488
|
+
): LocaleEntry[] {
|
|
489
|
+
const base = normalizeSrcDir(baseSrcDir);
|
|
490
|
+
const rawEntries = Object.entries(locales ?? {});
|
|
491
|
+
if (!rawEntries.length) {
|
|
492
|
+
return [{ key: "/", segment: "", srcDir: base, config: {} }];
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return rawEntries.map(([key, config]) => {
|
|
496
|
+
const segment = keyToLocaleSegment(key);
|
|
497
|
+
const srcDir = normalizeSrcDir(segment ? `${base}${segment}` : base);
|
|
498
|
+
return { key: normalizeLocaleKey(key), segment, srcDir, config };
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
export function resolveLocaleForPath(
|
|
503
|
+
activePath: string,
|
|
504
|
+
baseSrcDir: string,
|
|
505
|
+
locales: Record<string, LocaleConfig>,
|
|
506
|
+
defaults: ResolveDefaults,
|
|
507
|
+
) {
|
|
508
|
+
const cleanPath = normalizeSrcDir(activePath || "/");
|
|
509
|
+
const normalizedBaseSrcDir = normalizeSrcDir(baseSrcDir);
|
|
510
|
+
const entries = getLocaleEntries(baseSrcDir, locales);
|
|
511
|
+
const matched =
|
|
512
|
+
[...entries]
|
|
513
|
+
.sort((a, b) => b.segment.length - a.segment.length)
|
|
514
|
+
.find(
|
|
515
|
+
(entry) =>
|
|
516
|
+
cleanPath === entry.srcDir ||
|
|
517
|
+
cleanPath.startsWith(entry.srcDir + "/") ||
|
|
518
|
+
(entry.segment && (
|
|
519
|
+
cleanPath === entry.segment ||
|
|
520
|
+
cleanPath.startsWith(entry.segment + "/")
|
|
521
|
+
)),
|
|
522
|
+
) ?? entries[0];
|
|
523
|
+
|
|
524
|
+
const themeConfig = matched.config.themeConfig ?? {};
|
|
525
|
+
const normalizedNav = normalizeTopNavLinks(themeConfig.nav ?? defaults.nav, {
|
|
526
|
+
baseSrcDir: normalizedBaseSrcDir,
|
|
527
|
+
localeSrcDir: matched.srcDir,
|
|
528
|
+
localeSegment: matched.segment,
|
|
529
|
+
});
|
|
530
|
+
const normalizedSidebar = Array.isArray(themeConfig.sidebar ?? defaults.sidebar)
|
|
531
|
+
? normalizeSidebarLinks(
|
|
532
|
+
(themeConfig.sidebar ?? defaults.sidebar) as SidebarItem[],
|
|
533
|
+
{
|
|
534
|
+
baseSrcDir: normalizedBaseSrcDir,
|
|
535
|
+
localeSrcDir: matched.srcDir,
|
|
536
|
+
localeSegment: matched.segment,
|
|
537
|
+
},
|
|
538
|
+
)
|
|
539
|
+
: (themeConfig.sidebar ?? defaults.sidebar);
|
|
540
|
+
// Deep-merge built-in defaults under explicit themeConfig — explicit always wins.
|
|
541
|
+
const builtInThemeConfig = getBuiltInThemeConfig(matched.config.lang, matched.key);
|
|
542
|
+
const mergedThemeConfig = deepMerge(builtInThemeConfig, themeConfig);
|
|
543
|
+
const searchLocale = resolveSearchLocaleConfig(mergedThemeConfig, matched.key);
|
|
544
|
+
// Narrow to SearchLocaleModal so we can access Greg-specific extensions
|
|
545
|
+
// (searchingText, resultsAriaLabel) without type errors.
|
|
546
|
+
const modal = searchLocale?.modal as (SearchLocaleModal | undefined);
|
|
547
|
+
return {
|
|
548
|
+
key: matched.key,
|
|
549
|
+
lang: matched.config.lang,
|
|
550
|
+
dir: matched.config.dir,
|
|
551
|
+
srcDir: matched.srcDir,
|
|
552
|
+
allSrcDirs: entries.map((entry) => entry.srcDir),
|
|
553
|
+
mainTitle: matched.config.title ?? defaults.mainTitle,
|
|
554
|
+
nav: normalizedNav,
|
|
555
|
+
sidebar: normalizedSidebar,
|
|
556
|
+
outline: mergedThemeConfig.outline ?? defaults.outline,
|
|
557
|
+
lastUpdatedText: mergedThemeConfig.lastUpdatedText,
|
|
558
|
+
langMenuLabel: mergedThemeConfig.langMenuLabel ?? defaults.langMenuLabel,
|
|
559
|
+
sidebarMenuLabel: mergedThemeConfig.sidebarMenuLabel ?? defaults.sidebarMenuLabel,
|
|
560
|
+
skipToContentLabel: mergedThemeConfig.skipToContentLabel ?? defaults.skipToContentLabel,
|
|
561
|
+
returnToTopLabel: mergedThemeConfig.returnToTopLabel ?? defaults.returnToTopLabel,
|
|
562
|
+
darkModeSwitchLabel: mergedThemeConfig.darkModeSwitchLabel ?? defaults.darkModeSwitchLabel,
|
|
563
|
+
lightModeSwitchTitle: mergedThemeConfig.lightModeSwitchTitle ?? defaults.lightModeSwitchTitle,
|
|
564
|
+
darkModeSwitchTitle: mergedThemeConfig.darkModeSwitchTitle ?? defaults.darkModeSwitchTitle,
|
|
565
|
+
docFooter: mergedThemeConfig.docFooter ?? defaults.docFooter,
|
|
566
|
+
siteTitle: mergedThemeConfig.siteTitle ?? defaults.siteTitle,
|
|
567
|
+
logo: mergedThemeConfig.logo ?? defaults.logo,
|
|
568
|
+
socialLinks: mergedThemeConfig.socialLinks ?? defaults.socialLinks,
|
|
569
|
+
editLink: mergedThemeConfig.editLink ?? defaults.editLink,
|
|
570
|
+
footer: mergedThemeConfig.footer ?? defaults.footer,
|
|
571
|
+
aside: mergedThemeConfig.aside ?? defaults.aside,
|
|
572
|
+
lastUpdated: mergedThemeConfig.lastUpdated ?? defaults.lastUpdated,
|
|
573
|
+
externalLinkIcon: mergedThemeConfig.externalLinkIcon ?? defaults.externalLinkIcon,
|
|
574
|
+
searchButtonLabel: searchLocale?.button?.buttonText ?? defaults.searchButtonLabel,
|
|
575
|
+
searchModalLabel: searchLocale?.button?.buttonAriaLabel ?? defaults.searchModalLabel,
|
|
576
|
+
searchPlaceholder: modal?.searchBox?.placeholder ?? defaults.searchPlaceholder,
|
|
577
|
+
searchLoadingText: modal?.loadingScreen?.loadingText ?? defaults.searchLoadingText,
|
|
578
|
+
searchErrorText: modal?.errorScreen?.titleText ?? defaults.searchErrorText,
|
|
579
|
+
searchSearchingText: modal?.searchingText ?? defaults.searchSearchingText,
|
|
580
|
+
searchNoResultsText: modal?.noResultsText ?? defaults.searchNoResultsText,
|
|
581
|
+
searchStartText: modal?.startScreen?.noRecentSearchesText ?? defaults.searchStartText,
|
|
582
|
+
searchResultsAriaLabel: modal?.resultsAriaLabel ?? defaults.searchResultsAriaLabel,
|
|
583
|
+
searchNavigateText: modal?.footer?.navigateText ?? defaults.searchNavigateText,
|
|
584
|
+
searchSelectText: modal?.footer?.selectText ?? defaults.searchSelectText,
|
|
585
|
+
searchCloseText: modal?.footer?.closeText ?? defaults.searchCloseText,
|
|
586
|
+
aiTabLabel: modal?.ai?.tabLabel ?? defaults.aiTabLabel,
|
|
587
|
+
aiPlaceholder: modal?.ai?.placeholder ?? defaults.aiPlaceholder,
|
|
588
|
+
aiLoadingText: modal?.ai?.loadingText ?? defaults.aiLoadingText,
|
|
589
|
+
aiErrorText: modal?.ai?.errorText ?? defaults.aiErrorText,
|
|
590
|
+
aiStartText: modal?.ai?.startText ?? defaults.aiStartText,
|
|
591
|
+
aiSourcesLabel: modal?.ai?.sourcesLabel ?? defaults.aiSourcesLabel,
|
|
592
|
+
aiClearChatLabel: modal?.ai?.clearChatLabel ?? defaults.aiClearChatLabel,
|
|
593
|
+
aiSendLabel: modal?.ai?.sendLabel ?? defaults.aiSendLabel,
|
|
594
|
+
entries,
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function hasMarkdownForPath(
|
|
599
|
+
frontmatters: Record<string, unknown>,
|
|
600
|
+
srcDir: string,
|
|
601
|
+
routePath: string,
|
|
602
|
+
): boolean {
|
|
603
|
+
const rel = routePath.replace(srcDir, "").replace(/^\//, "");
|
|
604
|
+
const candidates = rel
|
|
605
|
+
? (srcDir === "/"
|
|
606
|
+
? [`/${rel}.md`, `/${rel}/index.md`]
|
|
607
|
+
: [`${srcDir}/${rel}.md`, `${srcDir}/${rel}/index.md`])
|
|
608
|
+
: (srcDir === "/"
|
|
609
|
+
? [`/index.md`]
|
|
610
|
+
: [`${srcDir}/index.md`, `${srcDir}index.md`]);
|
|
611
|
+
return candidates.some((candidate) => candidate in frontmatters);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function getLocaleLabel(entry: LocaleEntry): string {
|
|
615
|
+
if (entry.config.label) return entry.config.label;
|
|
616
|
+
if (entry.config.lang) return entry.config.lang;
|
|
617
|
+
if (entry.key === "/") return "Default";
|
|
618
|
+
return entry.key.replace(/^\/+|\/+$/g, "").toUpperCase();
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
export function getLocaleSwitchItems(args: {
|
|
622
|
+
entries: LocaleEntry[];
|
|
623
|
+
activePath: string;
|
|
624
|
+
activeSrcDir: string;
|
|
625
|
+
activeLocaleKey: string;
|
|
626
|
+
frontmatters: Record<string, unknown>;
|
|
627
|
+
preservePath?: boolean;
|
|
628
|
+
}) {
|
|
629
|
+
const {
|
|
630
|
+
entries,
|
|
631
|
+
activePath,
|
|
632
|
+
activeSrcDir,
|
|
633
|
+
activeLocaleKey,
|
|
634
|
+
frontmatters,
|
|
635
|
+
preservePath = true,
|
|
636
|
+
} = args;
|
|
637
|
+
if (entries.length <= 1) return [];
|
|
638
|
+
|
|
639
|
+
const EXTERNAL_LINK_RE = /^(?:[a-z][a-z\d+\-.]*:|\/\/)/i;
|
|
640
|
+
|
|
641
|
+
const relPath =
|
|
642
|
+
preservePath &&
|
|
643
|
+
(activePath === activeSrcDir ||
|
|
644
|
+
(activeSrcDir === "/"
|
|
645
|
+
? activePath.startsWith("/")
|
|
646
|
+
: activePath.startsWith(activeSrcDir + "/")))
|
|
647
|
+
? (activeSrcDir === "/"
|
|
648
|
+
? (activePath === "/" ? "" : activePath)
|
|
649
|
+
: activePath.slice(activeSrcDir.length))
|
|
650
|
+
: "";
|
|
651
|
+
|
|
652
|
+
return entries.map((entry) => {
|
|
653
|
+
const mappedPath = normalizeSrcDir(entry.srcDir + relPath);
|
|
654
|
+
const localeLink =
|
|
655
|
+
typeof entry.config.link === "string"
|
|
656
|
+
? entry.config.link.trim()
|
|
657
|
+
: "";
|
|
658
|
+
|
|
659
|
+
let link: string = localeLink
|
|
660
|
+
? EXTERNAL_LINK_RE.test(localeLink)
|
|
661
|
+
? localeLink
|
|
662
|
+
: normalizeSrcDir(localeLink)
|
|
663
|
+
: hasMarkdownForPath(frontmatters, entry.srcDir, mappedPath)
|
|
664
|
+
? mappedPath
|
|
665
|
+
: entry.srcDir;
|
|
666
|
+
// For non-root locales without an explicit link, strip the docsBase prefix
|
|
667
|
+
// so the language switcher navigates to the locale-segment root (e.g. '/pl')
|
|
668
|
+
// rather than the docsBase-prefixed path (e.g. '/documentation/pl').
|
|
669
|
+
if (!localeLink && entry.segment) {
|
|
670
|
+
const base = entry.srcDir.slice(0, entry.srcDir.length - entry.segment.length);
|
|
671
|
+
if (base && (link === base || link.startsWith(base + "/"))) {
|
|
672
|
+
link = link.slice(base.length) || "/";
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return {
|
|
676
|
+
key: entry.key,
|
|
677
|
+
label: getLocaleLabel(entry),
|
|
678
|
+
link,
|
|
679
|
+
active: entry.key === activeLocaleKey,
|
|
680
|
+
};
|
|
681
|
+
});
|
|
682
|
+
}
|