@astrojs/starlight 0.24.5 → 0.25.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/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # @astrojs/starlight
2
2
 
3
+ ## 0.25.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#2025](https://github.com/withastro/starlight/pull/2025) [`47f32c1`](https://github.com/withastro/starlight/commit/47f32c196c5d840a6a45799ddf123d17c77274b0) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Removes the `/` search shortcut for [accessibility reasons](https://www.w3.org/WAI/WCAG21/Understanding/character-key-shortcuts.html).
8
+
9
+ ⚠️ **Potentially breaking change:** The `search.shortcutLabel` UI string has been removed. If you were using this string in your custom UI, you will need to update your code.
10
+
11
+ - [#2064](https://github.com/withastro/starlight/pull/2064) [`c5b47cb`](https://github.com/withastro/starlight/commit/c5b47cbe1242f0b29b9212db72fe26590ab57d88) Thanks [@SnowDingo](https://github.com/SnowDingo)! - Improves styling of Markdown tables to work better in different contexts, including against different background colours like when used in asides.
12
+
13
+ - [#2031](https://github.com/withastro/starlight/pull/2031) [`2bab648`](https://github.com/withastro/starlight/commit/2bab648be9ddc6bfe05562650b773f5158a9ed42) Thanks [@delucis](https://github.com/delucis)! - Makes sidebar entry parsing stricter in Starlight config
14
+
15
+ **⚠️ Potentially breaking change:** Previously Starlight would accept a sidebar entry that matched one of its expected shapes, even if it included additional properties. For example, including both `link` and `items` was considered valid, with `items` being ignored. Now, it is an error to include more than one of `link`, `items`, or `autogenerate` in a sidebar entry.
16
+
17
+ If you see errors after updating, look for sidebar entries in the Starlight configuration in `astro.config.mjs` that include too many keys and remove the one that was previously ignored.
18
+
19
+ - [#1874](https://github.com/withastro/starlight/pull/1874) [`eeba06e`](https://github.com/withastro/starlight/commit/eeba06ea7df962e8f0520e145d28b8c17cd32c18) Thanks [@lorenzolewis](https://github.com/lorenzolewis)! - Adds a new syntax for specifying sidebar link items for internal links
20
+
21
+ You can now specify an internal page using only its slug, either as a string, or as an object with a `slug` property:
22
+
23
+ ```js
24
+ starlight({
25
+ title: 'Docs with easier sidebars',
26
+ sidebar: ['getting-started', { slug: 'guides/installation' }],
27
+ });
28
+ ```
29
+
30
+ Starlight will use the linked page’s frontmatter to configure the sidebar link.
31
+
32
+ ### Patch Changes
33
+
34
+ - [#2081](https://github.com/withastro/starlight/pull/2081) [`f0181d2`](https://github.com/withastro/starlight/commit/f0181d2689248a46ff3eb6fc604bfcd95d4cb1aa) Thanks [@andrii-bodnar](https://github.com/andrii-bodnar)! - Updates the Ukrainian UI translations
35
+
3
36
  ## 0.24.5
4
37
 
5
38
  ### Patch Changes
@@ -5,6 +5,7 @@ export default defineConfig({
5
5
  integrations: [
6
6
  starlight({
7
7
  title: 'Basics',
8
+ pagefind: false,
8
9
  }),
9
10
  ],
10
11
  });
@@ -6,6 +6,7 @@ export default defineConfig({
6
6
  integrations: [
7
7
  starlight({
8
8
  title: 'Custom src directory',
9
+ pagefind: false,
9
10
  }),
10
11
  ],
11
12
  });
@@ -4,6 +4,8 @@ import { build, preview } from 'astro';
4
4
 
5
5
  export { expect, type Locator } from '@playwright/test';
6
6
 
7
+ process.env.ASTRO_TELEMETRY_DISABLED = 'true';
8
+
7
9
  // Setup a test environment that will build and start a preview server for a given fixture path and
8
10
  // provide a Starlight Playwright fixture accessible from within all tests.
9
11
  export async function testFactory(fixturePath: string) {
@@ -20,17 +20,17 @@ const pagefindTranslations = {
20
20
  data-translations={JSON.stringify(pagefindTranslations)}
21
21
  data-strip-trailing-slash={project.trailingSlash === 'never'}
22
22
  >
23
- <button data-open-modal disabled>
24
- {
25
- /* The span is `aria-hidden` because it is not shown on small screens. Instead, the icon label is used for accessibility purposes. */
26
- }
27
- <Icon name="magnifier" label={labels['search.label']} />
23
+ <button
24
+ data-open-modal
25
+ disabled
26
+ aria-label={labels['search.label']}
27
+ aria-keyshortcuts="Control+K"
28
+ >
29
+ <Icon name="magnifier" />
28
30
  <span class="sl-hidden md:sl-block" aria-hidden="true">{labels['search.label']}</span>
29
- <Icon
30
- name="forward-slash"
31
- class="sl-hidden md:sl-block"
32
- label={labels['search.shortcutLabel']}
33
- />
31
+ <kbd class="sl-hidden md:sl-flex" style="display: none;">
32
+ <kbd>{labels['search.ctrlKey']}</kbd><kbd>K</kbd>
33
+ </kbd>
34
34
  </button>
35
35
 
36
36
  <dialog style="padding:0" aria-label={labels['search.label']}>
@@ -56,6 +56,27 @@ const pagefindTranslations = {
56
56
  </dialog>
57
57
  </site-search>
58
58
 
59
+ {
60
+ /**
61
+ * This is intentionally inlined to avoid briefly showing an invalid shortcut.
62
+ * Purposely using the deprecated `navigator.platform` property to detect Apple devices, as the
63
+ * user agent is spoofed by some browsers when opening the devtools.
64
+ */
65
+ }
66
+ <script is:inline>
67
+ (() => {
68
+ const openBtn = document.querySelector('button[data-open-modal]');
69
+ const shortcut = openBtn?.querySelector('kbd');
70
+ if (!openBtn || !(shortcut instanceof HTMLElement)) return;
71
+ const platformKey = shortcut.querySelector('kbd');
72
+ if (platformKey && /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)) {
73
+ platformKey.textContent = '⌘';
74
+ openBtn.setAttribute('aria-keyshortcuts', 'Meta+K');
75
+ }
76
+ shortcut.style.display = '';
77
+ })();
78
+ </script>
79
+
59
80
  <script>
60
81
  class SiteSearch extends HTMLElement {
61
82
  constructor() {
@@ -96,18 +117,11 @@ const pagefindTranslations = {
96
117
  window.removeEventListener('click', onClick);
97
118
  });
98
119
 
99
- // Listen for `/`, `ctrl + k`, and `cmd + k` keyboard shortcuts.
120
+ // Listen for `ctrl + k` and `cmd + k` keyboard shortcuts.
100
121
  window.addEventListener('keydown', (e) => {
101
- const isInput =
102
- document.activeElement instanceof HTMLElement &&
103
- (['input', 'select', 'textarea'].includes(document.activeElement.tagName.toLowerCase()) ||
104
- document.activeElement.isContentEditable);
105
122
  if ((e.metaKey === true || e.ctrlKey === true) && e.key === 'k') {
106
123
  dialog.open ? closeModal() : openModal();
107
124
  e.preventDefault();
108
- } else if (e.key === '/' && !dialog.open && !isInput) {
109
- openModal();
110
- e.preventDefault();
111
125
  }
112
126
  });
113
127
 
@@ -169,7 +183,7 @@ const pagefindTranslations = {
169
183
  border: 1px solid var(--sl-color-gray-5);
170
184
  border-radius: 0.5rem;
171
185
  padding-inline-start: 0.75rem;
172
- padding-inline-end: 1rem;
186
+ padding-inline-end: 0.5rem;
173
187
  background-color: var(--sl-color-black);
174
188
  color: var(--sl-color-gray-2);
175
189
  font-size: var(--sl-text-sm);
@@ -186,6 +200,18 @@ const pagefindTranslations = {
186
200
  }
187
201
  }
188
202
 
203
+ button > kbd {
204
+ border-radius: 0.25rem;
205
+ font-size: var(--sl-text-2xs);
206
+ gap: 0.25em;
207
+ padding-inline: 0.375rem;
208
+ background-color: var(--sl-color-gray-6);
209
+ }
210
+
211
+ kbd {
212
+ font-family: var(--__sl-font);
213
+ }
214
+
189
215
  dialog {
190
216
  margin: 0;
191
217
  background-color: var(--sl-color-gray-6);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrojs/starlight",
3
- "version": "0.24.5",
3
+ "version": "0.25.0",
4
4
  "description": "Build beautiful, high-performance documentation websites with Astro",
5
5
  "keywords": [
6
6
  "docs",
@@ -175,7 +175,7 @@
175
175
  },
176
176
  "devDependencies": {
177
177
  "@astrojs/markdown-remark": "^5.1.0",
178
- "@playwright/test": "^1.44.1",
178
+ "@playwright/test": "^1.45.0",
179
179
  "@types/node": "^18.16.19",
180
180
  "@vitest/coverage-v8": "^1.6.0",
181
181
  "astro": "^4.10.2",
@@ -12,5 +12,6 @@ export default defineConfig({
12
12
  },
13
13
  ],
14
14
  testMatch: '__e2e__/*.test.ts',
15
+ timeout: 40 * 1_000,
15
16
  workers: 1,
16
17
  });
package/schemas/i18n.ts CHANGED
@@ -55,9 +55,11 @@ function starlightI18nSchema() {
55
55
 
56
56
  'search.label': z.string().describe('Text displayed in the search bar.'),
57
57
 
58
- 'search.shortcutLabel': z
58
+ 'search.ctrlKey': z
59
59
  .string()
60
- .describe('Accessible label for the shortcut key to open the search modal.'),
60
+ .describe(
61
+ 'Visible representation of the Control key potentially used in the shortcut key to open the search modal.'
62
+ ),
61
63
 
62
64
  'search.cancelLabel': z
63
65
  .string()
@@ -33,7 +33,7 @@ const SidebarLinkItemSchema = SidebarBaseSchema.extend({
33
33
  link: z.string(),
34
34
  /** HTML attributes to add to the link item. */
35
35
  attrs: SidebarLinkItemHTMLAttributesSchema(),
36
- });
36
+ }).strict();
37
37
  export type SidebarLinkItem = z.infer<typeof SidebarLinkItemSchema>;
38
38
 
39
39
  const AutoSidebarGroupSchema = SidebarGroupSchema.extend({
@@ -50,7 +50,7 @@ const AutoSidebarGroupSchema = SidebarGroupSchema.extend({
50
50
  /** How many directories deep to include from this directory in the sidebar. Default: `Infinity`. */
51
51
  // depth: z.number().optional(),
52
52
  }),
53
- });
53
+ }).strict();
54
54
  export type AutoSidebarGroup = z.infer<typeof AutoSidebarGroupSchema>;
55
55
 
56
56
  type ManualSidebarGroupInput = z.input<typeof SidebarGroupSchema> & {
@@ -58,6 +58,8 @@ type ManualSidebarGroupInput = z.input<typeof SidebarGroupSchema> & {
58
58
  items: Array<
59
59
  | z.input<typeof SidebarLinkItemSchema>
60
60
  | z.input<typeof AutoSidebarGroupSchema>
61
+ | z.input<typeof InternalSidebarLinkItemSchema>
62
+ | z.input<typeof InternalSidebarLinkItemShorthandSchema>
61
63
  | ManualSidebarGroupInput
62
64
  >;
63
65
  };
@@ -67,6 +69,8 @@ type ManualSidebarGroupOutput = z.output<typeof SidebarGroupSchema> & {
67
69
  items: Array<
68
70
  | z.output<typeof SidebarLinkItemSchema>
69
71
  | z.output<typeof AutoSidebarGroupSchema>
72
+ | z.output<typeof InternalSidebarLinkItemSchema>
73
+ | z.output<typeof InternalSidebarLinkItemShorthandSchema>
70
74
  | ManualSidebarGroupOutput
71
75
  >;
72
76
  };
@@ -78,13 +82,34 @@ const ManualSidebarGroupSchema: z.ZodType<
78
82
  > = SidebarGroupSchema.extend({
79
83
  /** Array of links and subcategories to display in this category. */
80
84
  items: z.lazy(() =>
81
- z.union([SidebarLinkItemSchema, ManualSidebarGroupSchema, AutoSidebarGroupSchema]).array()
85
+ z
86
+ .union([
87
+ SidebarLinkItemSchema,
88
+ ManualSidebarGroupSchema,
89
+ AutoSidebarGroupSchema,
90
+ InternalSidebarLinkItemSchema,
91
+ InternalSidebarLinkItemShorthandSchema,
92
+ ])
93
+ .array()
82
94
  ),
95
+ }).strict();
96
+
97
+ const InternalSidebarLinkItemSchema = SidebarBaseSchema.partial({ label: true }).extend({
98
+ /** The link to this item’s content. Must be a slug of a Content Collection entry. */
99
+ slug: z.string(),
100
+ /** HTML attributes to add to the link item. */
101
+ attrs: SidebarLinkItemHTMLAttributesSchema(),
83
102
  });
103
+ const InternalSidebarLinkItemShorthandSchema = z
104
+ .string()
105
+ .transform((slug) => InternalSidebarLinkItemSchema.parse({ slug }));
106
+ export type InternalSidebarLinkItem = z.output<typeof InternalSidebarLinkItemSchema>;
84
107
 
85
108
  export const SidebarItemSchema = z.union([
86
109
  SidebarLinkItemSchema,
87
110
  ManualSidebarGroupSchema,
88
111
  AutoSidebarGroupSchema,
112
+ InternalSidebarLinkItemSchema,
113
+ InternalSidebarLinkItemShorthandSchema,
89
114
  ]);
90
115
  export type SidebarItem = z.infer<typeof SidebarItemSchema>;
@@ -100,17 +100,40 @@
100
100
  padding-inline-start: 1rem;
101
101
  }
102
102
 
103
+ /* Table styling */
103
104
  .sl-markdown-content table:not(:where(.not-content *)) {
104
105
  display: block;
105
106
  overflow: auto;
106
- border-collapse: collapse;
107
- }
108
- .sl-markdown-content tr:nth-child(2n):not(:where(.not-content *)) {
109
- background-color: var(--sl-color-gray-7, var(--sl-color-gray-6));
107
+ border-spacing: 0;
110
108
  }
111
109
  .sl-markdown-content :is(th, td):not(:where(.not-content *)) {
112
- border: 1px solid var(--sl-color-hairline-light);
113
- padding: 0.375rem 0.8125rem;
110
+ border-bottom: 1px solid var(--sl-color-gray-5);
111
+ padding: 0.5rem 1rem;
112
+ /* Align text to the top of the row in multiline tables. */
113
+ vertical-align: baseline;
114
+ }
115
+ .sl-markdown-content :is(th:first-child, td:first-child):not(:where(.not-content *)) {
116
+ padding-inline-start: 0;
117
+ }
118
+ .sl-markdown-content :is(th:last-child, td:last-child):not(:where(.not-content *)) {
119
+ padding-inline-end: 0;
120
+ }
121
+ .sl-markdown-content th:not(:where(.not-content *)) {
122
+ color: var(--sl-color-white);
123
+ font-weight: 600;
124
+ }
125
+ /* Align headings to the start of the line unless set by the `align` attribute. */
126
+ .sl-markdown-content th:not([align]):not(:where(.not-content *)) {
127
+ text-align: start;
128
+ }
129
+ /* Table styles inside asides */
130
+ .sl-markdown-content .starlight-aside :is(th, td):not(:where(.not-content *)) {
131
+ border-color: var(--sl-color-gray-4);
132
+ }
133
+ @supports (border-color: color-mix(in srgb, var(--sl-color-asides-text-accent) 30%, transparent)) {
134
+ .sl-markdown-content .starlight-aside :is(th, td):not(:where(.not-content *)) {
135
+ border-color: color-mix(in srgb, var(--sl-color-asides-text-accent) 30%, transparent);
136
+ }
114
137
  }
115
138
 
116
139
  .sl-markdown-content hr:not(:where(.not-content *)) {
package/style/props.css CHANGED
@@ -58,6 +58,7 @@
58
58
  0px 4px 2px hsla(0, 0%, 0%, 0.25);
59
59
 
60
60
  /* Text size and line height */
61
+ --sl-text-2xs: 0.75rem; /* 12px */
61
62
  --sl-text-xs: 0.8125rem; /* 13px */
62
63
  --sl-text-sm: 0.875rem; /* 14px */
63
64
  --sl-text-base: 1rem; /* 16px */
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "تخطَّ إلى المحتوى",
3
3
  "search.label": "ابحث",
4
- "search.shortcutLabel": "(إضغط / للبحث)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "إلغاء",
6
6
  "search.devWarning": "البحث متوفر فقط في بنيات اﻹنتاج. \n جرب بناء المشروع ومعاينته على جهازك",
7
7
  "themeSelect.accessibleLabel": "اختر سمة",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Přeskočit na obsah",
3
3
  "search.label": "Hledat",
4
- "search.shortcutLabel": "(Vyhledejte stisknutím /)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Zrušit",
6
6
  "search.devWarning": "Vyhledávání je dostupné pouze v produkčních sestaveních. \nZkuste sestavit a zobrazit náhled webu a otestovat jej lokálně.",
7
7
  "themeSelect.accessibleLabel": "Vyberte motiv",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Gå til indhold",
3
3
  "search.label": "Søg",
4
- "search.shortcutLabel": "(Tryk / for at søge)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Annuller",
6
6
  "search.devWarning": "Søgning er kun tilgængeligt i produktions versioner. \nPrøv at bygge siden og forhåndsvis den for at teste det lokalt.",
7
7
  "themeSelect.accessibleLabel": "Vælg tema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Zum Inhalt springen",
3
3
  "search.label": "Suchen",
4
- "search.shortcutLabel": "(Drücke / zum Suchen)",
4
+ "search.ctrlKey": "Strg",
5
5
  "search.cancelLabel": "Abbrechen",
6
6
  "search.devWarning": "Search is only available in production builds. \nTry building and previewing the site to test it out locally.",
7
7
  "themeSelect.accessibleLabel": "Farbschema wählen",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Skip to content",
3
3
  "search.label": "Search",
4
- "search.shortcutLabel": "(Press / to Search)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Cancel",
6
6
  "search.devWarning": "Search is only available in production builds. \nTry building and previewing the site to test it out locally.",
7
7
  "themeSelect.accessibleLabel": "Select theme",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Saltearse al contenido",
3
3
  "search.label": "Buscar",
4
- "search.shortcutLabel": "(Presiona / para buscar)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Interrumpir",
6
6
  "search.devWarning": "La búsqueda solo está disponible en las versiones de producción. \nIntenta construir y previsualizar el sitio para probarlo localmente.",
7
7
  "themeSelect.accessibleLabel": "Seleccionar tema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "رفتن به محتوا",
3
3
  "search.label": "جستجو",
4
- "search.shortcutLabel": "(برای جستجو / را فشار دهید)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "لغو",
6
6
  "search.devWarning": "جستجو تنها در نسخه‌های تولیدی در دسترس است. \nسعی کنید سایت را بسازید و پیش‌نمایش آن را به صورت محلی آزمایش کنید.",
7
7
  "themeSelect.accessibleLabel": "انتخاب پوسته",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Aller au contenu",
3
3
  "search.label": "Rechercher",
4
- "search.shortcutLabel": "(Presser / pour rechercher)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Annuler",
6
6
  "search.devWarning": "La recherche est disponible uniquement en mode production. \nEssayez de construire puis de prévisualiser votre site pour tester la recherche localement.",
7
7
  "themeSelect.accessibleLabel": "Selectionner le thème",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Ir ao contido",
3
3
  "search.label": "Busca",
4
- "search.shortcutLabel": "(Preme / para Busca)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Deixar",
6
6
  "search.devWarning": "A busca só está dispoñible nas versións de producción. \nTrata de construir e ollear o sitio para probalo localmente.",
7
7
  "themeSelect.accessibleLabel": "Seleciona tema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "דלגו לתוכן",
3
3
  "search.label": "חיפוש",
4
- "search.shortcutLabel": "(לחצו / כדי לחפש)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "ביטול",
6
6
  "search.devWarning": "החיפוש זמין רק בסביבת ייצור. \nנסו לבנות ולהציג תצוגה מקדימה של האתר כדי לבדוק אותו באופן מקומי.",
7
7
  "themeSelect.accessibleLabel": "בחרו פרופיל צבע",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "इसे छोड़कर कंटेंट पर जाएं",
3
3
  "search.label": "खोजें",
4
- "search.shortcutLabel": "(खोजने के लिए / दबाएँ)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "रद्द करे",
6
6
  "search.devWarning": "खोज केवल उत्पादन बिल्ड में उपलब्ध है। \nस्थानीय स्तर पर परीक्षण करने के लिए साइट बनाए और उसका पूर्वावलोकन करने का प्रयास करें।",
7
7
  "themeSelect.accessibleLabel": "थीम चुनें",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Lewati ke konten",
3
3
  "search.label": "Pencarian",
4
- "search.shortcutLabel": "(Tekan / untuk mencari)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Batal",
6
6
  "search.devWarning": "Pencarian hanya tersedia pada build produksi. \nLakukan proses build dan pratinjau situs Anda sebelum mencoba di lingkungan lokal.",
7
7
  "themeSelect.accessibleLabel": "Pilih tema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Salta ai contenuti",
3
3
  "search.label": "Cerca",
4
- "search.shortcutLabel": "(Usa / per cercare)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Cancella",
6
6
  "search.devWarning": "La ricerca è disponibile solo nelle build di produzione. \nProvare ad eseguire il processo di build e visualizzare la preview del sito per testarlo localmente.",
7
7
  "themeSelect.accessibleLabel": "Seleziona tema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "コンテンツにスキップ",
3
3
  "search.label": "検索",
4
- "search.shortcutLabel": "(/を押して検索)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "キャンセル",
6
6
  "search.devWarning": "検索はプロダクションビルドでのみ利用可能です。\nローカルでテストするには、サイトをビルドしてプレビューしてください。",
7
7
  "themeSelect.accessibleLabel": "テーマの選択",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "컨텐츠로 건너뛰기",
3
3
  "search.label": "검색",
4
- "search.shortcutLabel": "(검색하려면 / 를 누르세요)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "취소",
6
6
  "search.devWarning": "검색 기능은 운영 환경에서만 사용할 수 있습니다. \n로컬에서 테스트하려면 사이트를 빌드하고 미리보기를 해보세요.",
7
7
  "themeSelect.accessibleLabel": "테마 선택",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Gå til innholdet",
3
3
  "search.label": "Søk",
4
- "search.shortcutLabel": "(Trykk / for å søke)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Avbryt",
6
6
  "search.devWarning": "Søk er bare tilgjengelig i produksjonsbygg. \nPrøv å bygg siden og forhåndsvis den for å teste det lokalt.",
7
7
  "themeSelect.accessibleLabel": "Velg tema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Ga naar inhoud",
3
3
  "search.label": "Zoeken",
4
- "search.shortcutLabel": "(Druk op / om te zoeken)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Annuleren",
6
6
  "search.devWarning": "Zoeken is alleen beschikbaar tijdens productie. \nProbeer om de site te builden en er een preview van te bekijken om lokaal te testen.",
7
7
  "themeSelect.accessibleLabel": "Selecteer thema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Przejdź do głównej zawartości",
3
3
  "search.label": "Szukaj",
4
- "search.shortcutLabel": "(Naciśnij /, aby wyszukać)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Anuluj",
6
6
  "search.devWarning": "Wyszukiwanie jest dostępne tylko w buildach produkcyjnych. \nSpróbuj zbudować i uruchomić aplikację, aby przetestować lokalnie.",
7
7
  "themeSelect.accessibleLabel": "Wybierz motyw",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Pular para o conteúdo",
3
3
  "search.label": "Pesquisar",
4
- "search.shortcutLabel": "(Pressione / para Pesquisar)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Cancelar",
6
6
  "search.devWarning": "A pesquisa está disponível apenas em builds em produção. \nTente fazer a build e pré-visualize o site para testar localmente.",
7
7
  "themeSelect.accessibleLabel": "Selecionar tema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Sari la conținut",
3
3
  "search.label": "Caută",
4
- "search.shortcutLabel": "(Apasă pe / ca să cauți)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Anulează",
6
6
  "search.devWarning": "Căutarea este disponibilă numai în versiunea de producție. \nÎncercă să construiești și să previzualizezi site-ul pentru a-l testa local.",
7
7
  "themeSelect.accessibleLabel": "Selectează tema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Перейти к содержимому",
3
3
  "search.label": "Поиск",
4
- "search.shortcutLabel": "(Нажмите / для поиска)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Отменить",
6
6
  "search.devWarning": "Поиск доступен только в производственных сборках. \nПопробуйте выполнить сборку и просмотреть сайт, чтобы протестировать его локально.",
7
7
  "themeSelect.accessibleLabel": "Выберите тему",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Preskočiť na obsah",
3
3
  "search.label": "Hľadať",
4
- "search.shortcutLabel": "(Stlačením / hľadajte)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Zrušiť",
6
6
  "search.devWarning": "Vyhľadávanie je dostupné len v produkčných zostaveniach. \nSkúste vytvoriť a zobraziť náhľad stránky lokálne.",
7
7
  "themeSelect.accessibleLabel": "Vyberte tému",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Hoppa till innehåll",
3
3
  "search.label": "Sök",
4
- "search.shortcutLabel": "(Tryck / för att söka)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Avbryt",
6
6
  "search.devWarning": "Sökfunktionen är endast tillgänglig i produktionsbyggen. \nProva att bygga och förhandsvisa siten för att testa det lokalt.",
7
7
  "themeSelect.accessibleLabel": "Välj tema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "İçeriğe geç",
3
3
  "search.label": "Ara",
4
- "search.shortcutLabel": "(Aramak için / bas)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "İptal",
6
6
  "search.devWarning": "Arama yalnızca üretim derlemelerinde kullanılabilir. \nYerel bilgisayarınızda test etmek için siteyi derleyin ve önizleme yapın.",
7
7
  "themeSelect.accessibleLabel": "Tema seç",
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "skipLink.label": "Перейти до вмісту",
3
3
  "search.label": "Пошук",
4
- "search.shortcutLabel": "(Натисніть / для Пошуку)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Скасувати",
6
- "search.devWarning": "Пошук доступний лише у виробничих зборках. \nСпробуйте зібрати та переглянути сайт, щоби протестувати його локально",
6
+ "search.devWarning": "Пошук доступний лише у виробничих збірках. \nСпробуйте зібрати та переглянути сайт, щоби протестувати його локально",
7
7
  "themeSelect.accessibleLabel": "Обрати тему",
8
8
  "themeSelect.dark": "Темна",
9
9
  "themeSelect.light": "Світла",
@@ -18,12 +18,12 @@
18
18
  "page.lastUpdated": "Останнє оновлення:",
19
19
  "page.previousLink": "Назад",
20
20
  "page.nextLink": "Далі",
21
- "page.draft": "This content is a draft and will not be included in production builds.",
21
+ "page.draft": "Цей контент є чернеткою і не буде включений до виробничих збірок.",
22
22
  "404.text": "Сторінку не знайдено. Перевірте URL або спробуйте скористатися пошуком.",
23
23
  "aside.note": "Заувага",
24
24
  "aside.tip": "Порада",
25
25
  "aside.caution": "Обережно",
26
26
  "aside.danger": "Небезпечно",
27
- "fileTree.directory": "Directory",
28
- "builtWithStarlight.label": "Built with Starlight"
27
+ "fileTree.directory": "Каталог",
28
+ "builtWithStarlight.label": "Створено з Starlight"
29
29
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "Bỏ qua nội dung",
3
3
  "search.label": "Tìm kiếm",
4
- "search.shortcutLabel": "(Nhấn / để Tìm kiếm)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "Thoát",
6
6
  "search.devWarning": "Tìm kiếm không khả dụng khi đang trong môi trường lập trình. \nThử xuất bản website và tìm kiếm.",
7
7
  "themeSelect.accessibleLabel": "Chọn giao diện",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "跳转到内容",
3
3
  "search.label": "搜索",
4
- "search.shortcutLabel": "(按 / 进行搜索)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "取消",
6
6
  "search.devWarning": "搜索仅适用于生产版本。\n尝试构建并预览网站以在本地测试。",
7
7
  "themeSelect.accessibleLabel": "选择主题",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skipLink.label": "跳到內容",
3
3
  "search.label": "搜尋",
4
- "search.shortcutLabel": "(按 / 鍵開始搜尋)",
4
+ "search.ctrlKey": "Ctrl",
5
5
  "search.cancelLabel": "取消",
6
6
  "search.devWarning": "正式版本才能使用搜尋功能。\n如要在本地測試,請先建置並預覽網站。",
7
7
  "themeSelect.accessibleLabel": "選擇佈景主題",
@@ -88,7 +88,12 @@ const errorMap: z.ZodErrorMap = (baseError, ctx) => {
88
88
  expectedShape.push(relativePath);
89
89
  }
90
90
  }
91
- expectedShapes.push(`{ ${expectedShape.join('; ')} }`);
91
+ if (expectedShape.length === 1 && !expectedShape[0]?.includes(':')) {
92
+ // In this case the expected shape is not an object, but probably a literal type, e.g. `['string']`.
93
+ expectedShapes.push(expectedShape.join(''));
94
+ } else {
95
+ expectedShapes.push(`{ ${expectedShape.join('; ')} }`);
96
+ }
92
97
  }
93
98
  if (expectedShapes.length) {
94
99
  details.push('> Expected type `' + expectedShapes.join(' | ') + '`');
@@ -1,8 +1,10 @@
1
+ import { AstroError } from 'astro/errors';
1
2
  import config from 'virtual:starlight/user-config';
2
3
  import type { Badge } from '../schemas/badge';
3
4
  import type { PrevNextLinkConfig } from '../schemas/prevNextLink';
4
5
  import type {
5
6
  AutoSidebarGroup,
7
+ InternalSidebarLinkItem,
6
8
  LinkHTMLAttributes,
7
9
  SidebarItem,
8
10
  SidebarLinkItem,
@@ -11,7 +13,7 @@ import { createPathFormatter } from './createPathFormatter';
11
13
  import { formatPath } from './format-path';
12
14
  import { pickLang } from './i18n';
13
15
  import { ensureLeadingSlash, ensureTrailingSlash, stripLeadingAndTrailingSlashes } from './path';
14
- import { getLocaleRoutes, type Route } from './routing';
16
+ import { getLocaleRoutes, routes, type Route } from './routing';
15
17
  import { localeToLang, slugToPathname } from './slugs';
16
18
 
17
19
  const DirKey = Symbol('DirKey');
@@ -70,9 +72,11 @@ function configItemToEntry(
70
72
  routes: Route[]
71
73
  ): SidebarEntry {
72
74
  if ('link' in item) {
73
- return linkFromConfig(item, locale, currentPathname);
75
+ return linkFromSidebarLinkItem(item, locale, currentPathname);
74
76
  } else if ('autogenerate' in item) {
75
77
  return groupFromAutogenerateConfig(item, locale, routes, currentPathname);
78
+ } else if ('slug' in item) {
79
+ return linkFromInternalSidebarLinkItem(item, locale, currentPathname);
76
80
  } else {
77
81
  return {
78
82
  type: 'group',
@@ -113,8 +117,8 @@ function groupFromAutogenerateConfig(
113
117
  /** Check if a string starts with one of `http://` or `https://`. */
114
118
  const isAbsolute = (link: string) => /^https?:\/\//.test(link);
115
119
 
116
- /** Create a link entry from a user config object. */
117
- function linkFromConfig(
120
+ /** Create a link entry from a manual link item in user config. */
121
+ function linkFromSidebarLinkItem(
118
122
  item: SidebarLinkItem,
119
123
  locale: string | undefined,
120
124
  currentPathname: string
@@ -129,6 +133,36 @@ function linkFromConfig(
129
133
  return makeLink(href, label, currentPathname, item.badge, item.attrs);
130
134
  }
131
135
 
136
+ /** Create a link entry from an automatic internal link item in user config. */
137
+ function linkFromInternalSidebarLinkItem(
138
+ item: InternalSidebarLinkItem,
139
+ locale: string | undefined,
140
+ currentPathname: string
141
+ ) {
142
+ let slugWithLocale = locale ? locale + '/' + item.slug : item.slug;
143
+ // Astro passes root `index.[md|mdx]` entries with a slug of `index`
144
+ slugWithLocale = slugWithLocale.replace(/\/?index$/, '');
145
+ const entry = routes.find((entry) => slugWithLocale === entry.slug);
146
+ if (!entry) {
147
+ const hasExternalSlashes = item.slug.at(0) === '/' || item.slug.at(-1) === '/';
148
+ if (hasExternalSlashes) {
149
+ throw new AstroError(
150
+ `The slug \`"${item.slug}"\` specified in the Starlight sidebar config must not start or end with a slash.`,
151
+ `Please try updating \`"${item.slug}"\` to \`"${stripLeadingAndTrailingSlashes(item.slug)}"\`.`
152
+ );
153
+ } else {
154
+ throw new AstroError(
155
+ `The slug \`"${item.slug}"\` specified in the Starlight sidebar config does not exist.`,
156
+ 'Update the Starlight config to reference a valid entry slug in the docs content collection.\n' +
157
+ 'Learn more about Astro content collection slugs at https://docs.astro.build/en/reference/api-reference/#getentry'
158
+ );
159
+ }
160
+ }
161
+ const label =
162
+ pickLang(item.translations, localeToLang(locale)) || item.label || entry.entry.data.title;
163
+ return makeLink(entry.slug, label, currentPathname, item.badge, item.attrs);
164
+ }
165
+
132
166
  /** Create a link entry. */
133
167
  function makeLink(
134
168
  href: string,