@jaamd/jaad 0.0.0-stage → 0.11.0
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/LICENSE +21 -0
- package/README.md +52 -2
- package/dist/jaad.css +1446 -0
- package/index.ts +45 -0
- package/package.json +70 -3
- package/site.ts +19 -0
- package/src/@types/docs.ts +47 -0
- package/src/advanced.ts +21 -0
- package/src/collection.ts +97 -0
- package/src/components/Footer.astro +18 -0
- package/src/components/Head.astro +119 -0
- package/src/components/Header.astro +47 -0
- package/src/components/LocaleSwitcher.astro +111 -0
- package/src/components/Nav.astro +22 -0
- package/src/components/NavMobile.astro +105 -0
- package/src/components/ScrollToTop.astro +43 -0
- package/src/components/SearchPalette.astro +328 -0
- package/src/components/SocialLinks.astro +27 -0
- package/src/components/ThemeSwitcher.astro +55 -0
- package/src/components/docs/Breadcrumbs.astro +36 -0
- package/src/components/docs/DocsMobileNav.astro +93 -0
- package/src/components/docs/DocsPage.astro +68 -0
- package/src/components/docs/DocsPageNav.astro +53 -0
- package/src/components/docs/DocsSidebarLeft.astro +82 -0
- package/src/components/docs/PageActions.astro +81 -0
- package/src/components/docs/StructuredData.astro +55 -0
- package/src/components/docs/TableOfContents.astro +73 -0
- package/src/components/locale-switcher.ts +6 -0
- package/src/config.ts +236 -0
- package/src/content.ts +42 -0
- package/src/docs-config.ts +30 -0
- package/src/env.d.ts +13 -0
- package/src/fonts/OFL.txt +94 -0
- package/src/fonts/inter.woff2 +0 -0
- package/src/fonts/merriweather-italic.woff2 +0 -0
- package/src/fonts/merriweather.woff2 +0 -0
- package/src/fonts.ts +29 -0
- package/src/forges.ts +60 -0
- package/src/infer.ts +75 -0
- package/src/integration.ts +270 -0
- package/src/layouts/DefaultDocsFrame.astro +31 -0
- package/src/layouts/Docs.astro +168 -0
- package/src/layouts/Page.astro +38 -0
- package/src/locale-tree.ts +51 -0
- package/src/locales.ts +191 -0
- package/src/navigation.ts +97 -0
- package/src/routes/404.astro +16 -0
- package/src/routes/docs-index.astro +19 -0
- package/src/routes/docs-slug.astro +58 -0
- package/src/routes/docs-slug.md.ts +37 -0
- package/src/routes/llms-locale.txt.ts +8 -0
- package/src/routes/llms.txt.ts +4 -0
- package/src/routes/search-index-locale.json.ts +8 -0
- package/src/routes/search-index.json.ts +4 -0
- package/src/social.ts +44 -0
- package/src/styles/components/breadcrumbs.css +13 -0
- package/src/styles/components/docs-mobile-nav.css +9 -0
- package/src/styles/components/docs-page-nav.css +31 -0
- package/src/styles/components/docs-page.css +9 -0
- package/src/styles/components/docs-sidebar.css +42 -0
- package/src/styles/components/footer.css +8 -0
- package/src/styles/components/header.css +20 -0
- package/src/styles/components/locale-switcher.css +20 -0
- package/src/styles/components/nav-mobile.css +15 -0
- package/src/styles/components/nav.css +11 -0
- package/src/styles/components/not-found.css +11 -0
- package/src/styles/components/page-actions.css +13 -0
- package/src/styles/components/scroll-to-top.css +11 -0
- package/src/styles/components/search.css +98 -0
- package/src/styles/components/shell.css +73 -0
- package/src/styles/components/social-links.css +8 -0
- package/src/styles/components/theme-switcher.css +14 -0
- package/src/styles/components/toc.css +21 -0
- package/src/styles/docs.css +112 -0
- package/src/styles/global.css +162 -0
- package/src/styles/jaamd-forward.css +16 -0
- package/src/styles/jaamd-reverse.css +18 -0
- package/src/styles/jaamd.css +11 -0
- package/src/styles/tailwind.css +30 -0
- package/src/themes/index.ts +26 -0
- package/src/ui/en.ts +29 -0
- package/src/ui/it.ts +30 -0
- package/src/ui.ts +49 -0
- package/src/urls.ts +49 -0
- package/src/utils/docs.ts +300 -0
- package/src/utils/endpoints.ts +80 -0
- package/src/utils/helpers.ts +4 -0
- package/src/utils/search.ts +83 -0
- package/src/virtual.ts +48 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
@import 'tailwindcss' source(none);
|
|
2
|
+
@import './components/shell.css';
|
|
3
|
+
@import './components/header.css';
|
|
4
|
+
@import './components/footer.css';
|
|
5
|
+
@import './components/nav.css';
|
|
6
|
+
@import './components/nav-mobile.css';
|
|
7
|
+
@import './components/social-links.css';
|
|
8
|
+
@import './components/theme-switcher.css';
|
|
9
|
+
@import './components/locale-switcher.css';
|
|
10
|
+
@import './components/scroll-to-top.css';
|
|
11
|
+
@import './components/not-found.css';
|
|
12
|
+
@import './components/search.css';
|
|
13
|
+
@import './components/docs-page.css';
|
|
14
|
+
@import './components/docs-sidebar.css';
|
|
15
|
+
@import './components/toc.css';
|
|
16
|
+
@import './components/docs-mobile-nav.css';
|
|
17
|
+
@import './components/docs-page-nav.css';
|
|
18
|
+
@import './components/page-actions.css';
|
|
19
|
+
@import './components/breadcrumbs.css';
|
|
20
|
+
|
|
21
|
+
@theme static {
|
|
22
|
+
/* Seeds */
|
|
23
|
+
|
|
24
|
+
--color-background: #faf8f5;
|
|
25
|
+
--color-surface: #ffffff;
|
|
26
|
+
--color-foreground: #3a3a3a;
|
|
27
|
+
--color-foreground-bright: #1a1a1a;
|
|
28
|
+
--color-primary: #2d2d2d;
|
|
29
|
+
|
|
30
|
+
--color-error: #c45959;
|
|
31
|
+
--color-warning: #d4a259;
|
|
32
|
+
--color-info: #5988c4;
|
|
33
|
+
--color-success: #5a9c7e;
|
|
34
|
+
|
|
35
|
+
/* Derived */
|
|
36
|
+
|
|
37
|
+
--color-background-secondary: color-mix(in oklab, var(--color-foreground) 2%, var(--color-background));
|
|
38
|
+
--color-background-tertiary: color-mix(in oklab, var(--color-foreground) 6%, var(--color-background));
|
|
39
|
+
--color-surface-hover: color-mix(in oklab, var(--color-foreground) 5%, var(--color-background));
|
|
40
|
+
|
|
41
|
+
--color-border-light: color-mix(in oklab, var(--color-foreground) 11%, var(--color-background));
|
|
42
|
+
--color-border: color-mix(in oklab, var(--color-foreground) 17%, var(--color-background));
|
|
43
|
+
--color-border-dark: color-mix(in oklab, var(--color-foreground) 25%, var(--color-background));
|
|
44
|
+
|
|
45
|
+
--color-foreground-muted: color-mix(in oklab, var(--color-foreground) 55%, var(--color-background));
|
|
46
|
+
--color-foreground-secondary: color-mix(in oklab, var(--color-foreground) 72%, var(--color-background));
|
|
47
|
+
|
|
48
|
+
--color-primary-dark: var(--color-foreground-bright);
|
|
49
|
+
--color-primary-light: color-mix(in oklab, var(--color-primary) 83%, var(--color-background));
|
|
50
|
+
|
|
51
|
+
/* Fonts */
|
|
52
|
+
--font-sans: var(--font-inter), -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
53
|
+
--font-serif: var(--font-merriweather), Georgia, serif;
|
|
54
|
+
--font-mono: 'SF Mono', SF Mono, Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
|
55
|
+
|
|
56
|
+
/* Layout */
|
|
57
|
+
--jaad-content-width: 56rem;
|
|
58
|
+
--jaad-chrome-width: var(--jaad-content-width);
|
|
59
|
+
--jaad-sidebar-width: 16rem;
|
|
60
|
+
--jaad-page-padding: 2rem;
|
|
61
|
+
|
|
62
|
+
/* Where a sidebar sits beside centred content. Derived, so moving the
|
|
63
|
+
content width moves the sidebars with it. */
|
|
64
|
+
--jaad-sidebar-offset: calc(50% - (var(--jaad-content-width) / 2) - var(--jaad-sidebar-width) - 2rem);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@layer base {
|
|
68
|
+
html {
|
|
69
|
+
scroll-padding-top: 8rem;
|
|
70
|
+
background-color: var(--color-background);
|
|
71
|
+
color: var(--color-foreground);
|
|
72
|
+
font-family: var(--font-sans);
|
|
73
|
+
font-size: 16px;
|
|
74
|
+
scroll-behavior: smooth;
|
|
75
|
+
scrollbar-gutter: stable;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
h1,
|
|
79
|
+
h2,
|
|
80
|
+
h3 {
|
|
81
|
+
color: var(--color-foreground-bright);
|
|
82
|
+
font-family: var(--font-serif);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
h2 {
|
|
86
|
+
margin-bottom: 1.5rem;
|
|
87
|
+
font-size: 1.5rem;
|
|
88
|
+
font-weight: 600;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
h3 {
|
|
92
|
+
margin-bottom: 1rem;
|
|
93
|
+
font-size: 1.2rem;
|
|
94
|
+
font-weight: normal;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
a {
|
|
98
|
+
@apply text-primary hover:text-primary-dark underline decoration-1 underline-offset-2 transition duration-200 ease-in-out;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
i {
|
|
102
|
+
@apply text-primary italic;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* Utilities layer: wins over .jaad-brand's line-height. */
|
|
107
|
+
@layer utilities {
|
|
108
|
+
.font-brand {
|
|
109
|
+
font-family: var(--font-serif);
|
|
110
|
+
font-weight: 300;
|
|
111
|
+
font-style: italic;
|
|
112
|
+
letter-spacing: -0.01em;
|
|
113
|
+
line-height: 1;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* Dark mode */
|
|
118
|
+
|
|
119
|
+
html.dark {
|
|
120
|
+
color-scheme: dark;
|
|
121
|
+
|
|
122
|
+
--color-background: #0d1117;
|
|
123
|
+
--color-surface: #21262d;
|
|
124
|
+
--color-foreground: #c9d1d9;
|
|
125
|
+
--color-foreground-bright: #e6edf3;
|
|
126
|
+
--color-primary: #c9d1d9;
|
|
127
|
+
|
|
128
|
+
--color-error: #f85149;
|
|
129
|
+
--color-warning: #d29922;
|
|
130
|
+
--color-info: #58a6ff;
|
|
131
|
+
--color-success: #3fb950;
|
|
132
|
+
|
|
133
|
+
--color-background-secondary: color-mix(in oklab, var(--color-foreground) 7%, var(--color-background));
|
|
134
|
+
--color-background-tertiary: color-mix(in oklab, var(--color-foreground) 10%, var(--color-background));
|
|
135
|
+
--color-surface-hover: color-mix(in oklab, var(--color-foreground) 22%, var(--color-background));
|
|
136
|
+
|
|
137
|
+
--color-border-light: color-mix(in oklab, var(--color-foreground) 13%, var(--color-background));
|
|
138
|
+
--color-border: color-mix(in oklab, var(--color-foreground) 22%, var(--color-background));
|
|
139
|
+
--color-border-dark: color-mix(in oklab, var(--color-foreground) 36%, var(--color-background));
|
|
140
|
+
|
|
141
|
+
--color-foreground-muted: color-mix(in oklab, var(--color-foreground) 57%, var(--color-background));
|
|
142
|
+
--color-foreground-secondary: color-mix(in oklab, var(--color-foreground) 71%, var(--color-background));
|
|
143
|
+
|
|
144
|
+
--color-primary-light: color-mix(in oklab, var(--color-primary) 71%, var(--color-background));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* Reduced motion */
|
|
148
|
+
|
|
149
|
+
@media (prefers-reduced-motion: reduce) {
|
|
150
|
+
html {
|
|
151
|
+
scroll-behavior: auto;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
*,
|
|
155
|
+
*::before,
|
|
156
|
+
*::after {
|
|
157
|
+
animation-duration: 0.01ms !important;
|
|
158
|
+
animation-iteration-count: 1 !important;
|
|
159
|
+
transition-duration: 0.01ms !important;
|
|
160
|
+
scroll-behavior: auto !important;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* JAAD's palette drives JAAMD's. Used when the colours come from JAAD — its own
|
|
2
|
+
identity, or a custom theme written in src/jaad.css. */
|
|
3
|
+
|
|
4
|
+
:root {
|
|
5
|
+
--jaamd-bg: var(--color-background);
|
|
6
|
+
--jaamd-color-fg: var(--color-foreground);
|
|
7
|
+
--jaamd-color-fg-bright: var(--color-foreground-bright);
|
|
8
|
+
--jaamd-color-primary: var(--color-primary);
|
|
9
|
+
--jaamd-color-success: var(--color-success);
|
|
10
|
+
|
|
11
|
+
--jaamd-alert-note-color: var(--color-info);
|
|
12
|
+
--jaamd-alert-tip-color: var(--color-success);
|
|
13
|
+
--jaamd-alert-important-color: var(--color-primary);
|
|
14
|
+
--jaamd-alert-warning-color: var(--color-warning);
|
|
15
|
+
--jaamd-alert-caution-color: var(--color-error);
|
|
16
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* JAAMD's palette drives JAAD's, the opposite direction to jaamd-forward.css.
|
|
2
|
+
Never both at once: together they are a cycle, and CSS drops both sides.
|
|
3
|
+
|
|
4
|
+
Only the seeds; global.css derives the rest with color-mix(). */
|
|
5
|
+
|
|
6
|
+
:root,
|
|
7
|
+
html.dark {
|
|
8
|
+
--color-background: var(--jaamd-bg);
|
|
9
|
+
--color-surface: var(--jaamd-surface-1);
|
|
10
|
+
--color-foreground: var(--jaamd-color-fg);
|
|
11
|
+
--color-foreground-bright: var(--jaamd-color-fg-bright);
|
|
12
|
+
--color-primary: var(--jaamd-color-primary);
|
|
13
|
+
|
|
14
|
+
--color-info: var(--jaamd-alert-note-color);
|
|
15
|
+
--color-success: var(--jaamd-alert-tip-color);
|
|
16
|
+
--color-warning: var(--jaamd-alert-warning-color);
|
|
17
|
+
--color-error: var(--jaamd-alert-caution-color);
|
|
18
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* Opt-in (`tailwind: true`): site utilities read JAAD's tokens. No values, no preflight. */
|
|
2
|
+
@import 'tailwindcss/theme.css' layer(theme) theme(reference);
|
|
3
|
+
@import 'tailwindcss/utilities.css' layer(utilities);
|
|
4
|
+
|
|
5
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
6
|
+
|
|
7
|
+
@theme reference {
|
|
8
|
+
--color-background: transparent;
|
|
9
|
+
--color-surface: transparent;
|
|
10
|
+
--color-foreground: transparent;
|
|
11
|
+
--color-foreground-bright: transparent;
|
|
12
|
+
--color-primary: transparent;
|
|
13
|
+
--color-error: transparent;
|
|
14
|
+
--color-warning: transparent;
|
|
15
|
+
--color-info: transparent;
|
|
16
|
+
--color-success: transparent;
|
|
17
|
+
--color-background-secondary: transparent;
|
|
18
|
+
--color-background-tertiary: transparent;
|
|
19
|
+
--color-surface-hover: transparent;
|
|
20
|
+
--color-border-light: transparent;
|
|
21
|
+
--color-border: transparent;
|
|
22
|
+
--color-border-dark: transparent;
|
|
23
|
+
--color-foreground-muted: transparent;
|
|
24
|
+
--color-foreground-secondary: transparent;
|
|
25
|
+
--color-primary-dark: transparent;
|
|
26
|
+
--color-primary-light: transparent;
|
|
27
|
+
--font-sans: transparent;
|
|
28
|
+
--font-serif: transparent;
|
|
29
|
+
--font-mono: transparent;
|
|
30
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { themes } from '@jaamd/jaamd/themes'
|
|
2
|
+
|
|
3
|
+
export interface Preset {
|
|
4
|
+
/** Shiki theme, or a light/dark pair, for code blocks. */
|
|
5
|
+
shiki: string | { light: string; dark: string }
|
|
6
|
+
/** JAAMD theme slug, or null when JAAD's own identity already applies. */
|
|
7
|
+
theme: string | null
|
|
8
|
+
/** `dual` carries a light and a dark palette; the others apply in both modes. */
|
|
9
|
+
mode: 'light' | 'dark' | 'dual'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** JAAMD's palettes, plus `default`: JAAD's own, which needs no stylesheet. */
|
|
13
|
+
export const PRESETS: Record<string, Preset> = {
|
|
14
|
+
default: {
|
|
15
|
+
shiki: { light: 'github-light', dark: 'github-dark' },
|
|
16
|
+
theme: null,
|
|
17
|
+
mode: 'dual',
|
|
18
|
+
},
|
|
19
|
+
...Object.fromEntries(
|
|
20
|
+
themes.map((theme) => [theme.slug, { shiki: theme.shiki, theme: theme.slug, mode: theme.mode }]),
|
|
21
|
+
),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const PRESET_NAMES = Object.keys(PRESETS)
|
|
25
|
+
|
|
26
|
+
export const isPreset = (value: string): boolean => value in PRESETS
|
package/src/ui/en.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** The canonical UI catalogue: its keys define every catalogue JAAD accepts. */
|
|
2
|
+
export const ENGLISH_UI = {
|
|
3
|
+
'breadcrumb.label': 'Breadcrumb',
|
|
4
|
+
'breadcrumb.root': 'Docs',
|
|
5
|
+
home: 'Home',
|
|
6
|
+
'locale.change': 'Change language',
|
|
7
|
+
'nav.documentation': 'Documentation navigation',
|
|
8
|
+
'nav.jumpToSection': 'Jump to section...',
|
|
9
|
+
'nav.mobile': 'Toggle mobile menu',
|
|
10
|
+
'nav.page': 'Page navigation',
|
|
11
|
+
'nav.selectPage': 'Select page',
|
|
12
|
+
'page.copied': 'Copied!',
|
|
13
|
+
'page.copy': 'Copy page',
|
|
14
|
+
'page.edit': 'Edit this page',
|
|
15
|
+
'page.lastUpdated': 'Last updated on',
|
|
16
|
+
scrollTop: 'Scroll to top',
|
|
17
|
+
'search.close': 'close',
|
|
18
|
+
'search.empty': 'No results for',
|
|
19
|
+
'search.label': 'Search documentation',
|
|
20
|
+
'search.loading': 'Loading...',
|
|
21
|
+
'search.navigate': 'navigate',
|
|
22
|
+
'search.open': 'open',
|
|
23
|
+
'search.placeholder': 'Search documentation...',
|
|
24
|
+
'search.results': 'Search results',
|
|
25
|
+
'search.trigger': 'Search docs...',
|
|
26
|
+
'theme.toggle': 'Toggle color theme',
|
|
27
|
+
'toc.label': 'Table of contents',
|
|
28
|
+
'toc.title': 'On This Page',
|
|
29
|
+
} as const
|
package/src/ui/it.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { UiCatalog } from '../ui.ts'
|
|
2
|
+
|
|
3
|
+
export const ITALIAN_UI = {
|
|
4
|
+
'breadcrumb.label': 'Percorso di navigazione',
|
|
5
|
+
'breadcrumb.root': 'Documentazione',
|
|
6
|
+
home: 'Pagina iniziale',
|
|
7
|
+
'locale.change': 'Cambia lingua',
|
|
8
|
+
'nav.documentation': 'Navigazione della documentazione',
|
|
9
|
+
'nav.jumpToSection': 'Vai alla sezione…',
|
|
10
|
+
'nav.mobile': 'Apri o chiudi il menu',
|
|
11
|
+
'nav.page': 'Navigazione tra le pagine',
|
|
12
|
+
'nav.selectPage': 'Seleziona una pagina',
|
|
13
|
+
'page.copied': 'Copiata!',
|
|
14
|
+
'page.copy': 'Copia',
|
|
15
|
+
'page.edit': 'Modifica',
|
|
16
|
+
'page.lastUpdated': 'Ultimo aggiornamento il',
|
|
17
|
+
scrollTop: 'Torna in cima',
|
|
18
|
+
'search.close': 'chiudi',
|
|
19
|
+
'search.empty': 'Nessun risultato per',
|
|
20
|
+
'search.label': 'Cerca nella documentazione',
|
|
21
|
+
'search.loading': 'Caricamento…',
|
|
22
|
+
'search.navigate': 'naviga',
|
|
23
|
+
'search.open': 'apri',
|
|
24
|
+
'search.placeholder': 'Cerca nella documentazione…',
|
|
25
|
+
'search.results': 'Risultati della ricerca',
|
|
26
|
+
'search.trigger': 'Cerca…',
|
|
27
|
+
'theme.toggle': 'Cambia modalità',
|
|
28
|
+
'toc.label': 'Indice dei contenuti',
|
|
29
|
+
'toc.title': 'In questa pagina',
|
|
30
|
+
} satisfies UiCatalog
|
package/src/ui.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ENGLISH_UI } from './ui/en.ts'
|
|
2
|
+
import { ITALIAN_UI } from './ui/it.ts'
|
|
3
|
+
|
|
4
|
+
/** Every string JAAD puts on a page itself. */
|
|
5
|
+
export const UI_DEFAULTS = ENGLISH_UI
|
|
6
|
+
|
|
7
|
+
export type UiKey = keyof typeof UI_DEFAULTS
|
|
8
|
+
export type UiCatalog = Record<UiKey, string>
|
|
9
|
+
|
|
10
|
+
export type UiOverrides = Record<string, Partial<Record<string, string>>>
|
|
11
|
+
|
|
12
|
+
const UI_CATALOGS = {
|
|
13
|
+
en: ENGLISH_UI,
|
|
14
|
+
it: ITALIAN_UI,
|
|
15
|
+
} satisfies Record<string, UiCatalog>
|
|
16
|
+
|
|
17
|
+
function localeCandidates(locale: string): string[] {
|
|
18
|
+
const exact = locale.toLowerCase()
|
|
19
|
+
const base = exact.split('-', 1)[0]
|
|
20
|
+
return exact === base ? [exact] : [exact, base]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Locale keys in config are BCP-47 and therefore case-insensitive. */
|
|
24
|
+
function localeEntry<T>(entries: Record<string, T>, locale: string) {
|
|
25
|
+
return Object.entries(entries).find(([candidate]) => candidate.toLowerCase() === locale)?.[1]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** User overrides win, from the exact locale to its base language. Bundled
|
|
29
|
+
* catalogues follow the same order; English remains the final fallback. */
|
|
30
|
+
export function uiString(overrides: UiOverrides, locale: string, key: UiKey): string {
|
|
31
|
+
const candidates = localeCandidates(locale)
|
|
32
|
+
|
|
33
|
+
for (const candidate of candidates) {
|
|
34
|
+
const value = localeEntry(overrides, candidate)?.[key]
|
|
35
|
+
if (value !== undefined) return value
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (const candidate of candidates) {
|
|
39
|
+
const catalogue = localeEntry<UiCatalog>(UI_CATALOGS, candidate)
|
|
40
|
+
if (catalogue) return catalogue[key]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return UI_DEFAULTS[key]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const UI_KEYS = Object.keys(UI_DEFAULTS) as UiKey[]
|
|
47
|
+
|
|
48
|
+
/** Kept internal: consumers customise strings through `ui`. */
|
|
49
|
+
export const UI_SUPPORTED_LOCALES = Object.keys(UI_CATALOGS)
|
package/src/urls.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { localeSegment } from './locales.ts'
|
|
2
|
+
|
|
3
|
+
/** Empty at the site root, otherwise one leading slash and no trailing slash. */
|
|
4
|
+
export function normaliseBasePath(value: string): string {
|
|
5
|
+
const trimmed = value.replace(/^\/+/, '').replace(/\/+$/, '')
|
|
6
|
+
return trimmed ? `/${trimmed}` : ''
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Apply Astro's deployment base to an application-local, root-relative href. */
|
|
10
|
+
export function withDeploymentBase(href: string, deploymentBase: string): string {
|
|
11
|
+
if (!href.startsWith('/') || href.startsWith('//')) return href
|
|
12
|
+
|
|
13
|
+
const prefix = normaliseBasePath(deploymentBase)
|
|
14
|
+
if (!prefix) return href
|
|
15
|
+
return href === '/' ? `${prefix}/` : `${prefix}${href}`
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface DocsUrlOptions {
|
|
19
|
+
/** routeBase, normalised here: empty at the application root. */
|
|
20
|
+
docsBase: string
|
|
21
|
+
/** Astro's deployment base. */
|
|
22
|
+
deploymentBase?: string
|
|
23
|
+
/** Locale of the page, when the docs are localised. */
|
|
24
|
+
locale?: string
|
|
25
|
+
/** The one locale that owns the unprefixed urls. */
|
|
26
|
+
defaultLocale?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Public href for a documentation page. Empty slug means the opening page. */
|
|
30
|
+
export function docsPageHref(slug: string, urls: DocsUrlOptions): string {
|
|
31
|
+
return docsHref(slug, urls)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Raw Markdown keeps the page's named slug, including for the opener. */
|
|
35
|
+
export function docsMarkdownHref(slug: string, urls: DocsUrlOptions): string {
|
|
36
|
+
return docsHref(slug, urls, '.md')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function docsHref(
|
|
40
|
+
slug: string,
|
|
41
|
+
{ docsBase, deploymentBase = '', locale, defaultLocale }: DocsUrlOptions,
|
|
42
|
+
suffix = '',
|
|
43
|
+
): string {
|
|
44
|
+
const mount = normaliseBasePath(docsBase)
|
|
45
|
+
const path = [localeSegment(locale, defaultLocale), slug].filter(Boolean).join('/')
|
|
46
|
+
// Without a suffix an empty path is the mount itself; with one it is a file.
|
|
47
|
+
const href = path || suffix ? `${mount}/${path}${suffix}` : mount || '/'
|
|
48
|
+
return withDeploymentBase(href, deploymentBase)
|
|
49
|
+
}
|