@rimelight/ui 0.0.24 → 0.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/package.json +7 -3
  2. package/src/components/Head.astro +199 -156
  3. package/src/components/astro/RLAAccordion.astro +22 -0
  4. package/src/components/astro/RLAButton.astro +104 -118
  5. package/src/components/astro/RLAFooter.astro +14 -40
  6. package/src/components/astro/RLAHeader.astro +18 -78
  7. package/src/components/astro/RLAIcon.astro +11 -36
  8. package/src/components/astro/RLALogo.astro +9 -44
  9. package/src/components/astro/RLAPlaceholder.astro +8 -33
  10. package/src/components/astro/RLAQuote.astro +15 -0
  11. package/src/components/astro/RLAScrollToTop.astro +171 -221
  12. package/src/components/astro/RLASection.astro +181 -0
  13. package/src/components/vue/RLVButton.vue +172 -139
  14. package/src/components/vue/RLVFooter.vue +12 -49
  15. package/src/components/vue/RLVHeader.vue +15 -98
  16. package/src/components/vue/RLVIcon.vue +11 -37
  17. package/src/components/vue/RLVLogo.vue +8 -56
  18. package/src/components/vue/RLVPlaceholder.vue +11 -33
  19. package/src/components/vue/RLVScrollToTop.vue +17 -88
  20. package/src/components/vue/RLVSection.vue +98 -0
  21. package/src/composables/index.ts +0 -1
  22. package/src/env.d.ts +5 -0
  23. package/src/integrations/ui.ts +2 -6
  24. package/src/plugins/index.ts +5 -2
  25. package/src/plugins/starlightAddons/index.ts +3 -64
  26. package/src/plugins/starlightDocsApi/components/ApiEmits.astro +46 -0
  27. package/src/plugins/starlightDocsApi/components/ApiProps.astro +47 -0
  28. package/src/plugins/starlightDocsApi/components/ApiSlots.astro +50 -0
  29. package/src/plugins/starlightDocsApi/components/ApiTheme.astro +27 -0
  30. package/src/plugins/starlightDocsApi/extract-vue.ts +286 -0
  31. package/src/plugins/starlightDocsApi/index.ts +265 -0
  32. package/src/plugins/starlightDocsApi/types.ts +40 -0
  33. package/src/plugins/starlightDocsApi/utils.ts +45 -0
  34. package/src/themes/button.theme.ts +29 -14
  35. package/src/themes/footer.theme.ts +4 -6
  36. package/src/themes/header.theme.ts +4 -28
  37. package/src/themes/icon.theme.ts +4 -2
  38. package/src/themes/index.ts +15 -20
  39. package/src/themes/logo.theme.ts +4 -2
  40. package/src/themes/placeholder.theme.ts +4 -6
  41. package/src/themes/scroll-to-top.theme.ts +4 -6
  42. package/src/themes/section.theme.ts +185 -0
  43. package/src/types/componentVariant.ts +10 -0
  44. package/src/types/index.ts +1 -0
  45. package/src/utils/defineTheme.ts +11 -0
  46. package/src/utils/index.ts +3 -0
  47. package/src/utils/resolveClasses.ts +21 -0
  48. package/src/utils/useUi.ts +19 -0
  49. package/src/composables/useUi.ts +0 -27
  50. package/src/plugins/starlightAddons/Sidebar.astro +0 -89
  51. package/src/plugins/starlightAddons/data.ts +0 -39
  52. package/src/plugins/starlightAddons/libs/config.ts +0 -107
  53. package/src/plugins/starlightAddons/libs/content.ts +0 -20
  54. package/src/plugins/starlightAddons/libs/i18n.ts +0 -51
  55. package/src/plugins/starlightAddons/libs/locals.ts +0 -12
  56. package/src/plugins/starlightAddons/libs/pathname.ts +0 -22
  57. package/src/plugins/starlightAddons/libs/plugin.ts +0 -9
  58. package/src/plugins/starlightAddons/libs/sidebar.ts +0 -183
  59. package/src/plugins/starlightAddons/libs/vite.ts +0 -46
  60. package/src/plugins/starlightAddons/locals.d.ts +0 -14
  61. package/src/plugins/starlightAddons/middleware.ts +0 -132
  62. package/src/plugins/starlightAddons/overrides/Sidebar.astro +0 -8
  63. package/src/plugins/starlightAddons/schema.ts +0 -13
  64. package/src/plugins/starlightAddons/virtual.d.ts +0 -13
@@ -1,39 +0,0 @@
1
- import type { StarlightIcon } from "@astrojs/starlight/types"
2
-
3
- import type { SidebarTopicBadge } from "./libs/config"
4
-
5
- export interface StarlightSidebarTopicsRouteData {
6
- /**
7
- * Indicates if the current page is associated with a topic or not.
8
- */
9
- isPageWithTopic: boolean
10
- /**
11
- * A list of all configured topics.
12
- */
13
- topics: {
14
- /**
15
- * The optional badge associated with the topic.
16
- */
17
- badge?: {
18
- text: string
19
- variant: SidebarTopicBadge["variant"]
20
- }
21
- /**
22
- * The name of an optional icon associated with the topic set to one of Starlight’s built-in
23
- * icons.
24
- */
25
- icon?: StarlightIcon
26
- /**
27
- * Indicates if the current page is part of the topic.
28
- */
29
- isCurrent: boolean
30
- /**
31
- * The label of the topic.
32
- */
33
- label: string
34
- /**
35
- * The link to the topic’s content.
36
- */
37
- link: string
38
- }[]
39
- }
@@ -1,107 +0,0 @@
1
- import type { StarlightUserConfig } from "@astrojs/starlight/types"
2
- import { z } from "astro/zod"
3
-
4
- const sidebarTopicBadgeSchema = z.object({
5
- text: z.union([z.string(), z.record(z.string(), z.string())]),
6
- variant: z.enum(["note", "danger", "success", "caution", "tip", "default"]).default("default")
7
- })
8
-
9
- const sidebarTopicBaseSchema = z.object({
10
- /**
11
- * An optional badge to display next to the topic label.
12
- *
13
- * This option accepts the same configuration as the Starlight badge sidebar item configuration.
14
- *
15
- * @see https://starlight.astro.build/guides/sidebar/#badges
16
- */
17
- badge: z
18
- .union([z.string(), sidebarTopicBadgeSchema])
19
- .transform((badge) =>
20
- typeof badge === "string" ? { variant: "default" as const, text: badge } : badge
21
- )
22
- .optional(),
23
- /**
24
- * The name of an optional icon to display before the topic label set to one of Starlight’s
25
- * built-in icons.
26
- *
27
- * @see https://starlight.astro.build/reference/icons/#all-icons
28
- */
29
- icon: z.string().optional(),
30
- /**
31
- * The topic label visible at the top of the sidebar.
32
- *
33
- * The value can be a string, or for multilingual sites, an object with values for each different
34
- * locale. When using the object form, the keys must be BCP-47 tags (e.g. en, fr, or zh-CN).
35
- */
36
- label: z.union([z.string(), z.record(z.string(), z.string())]),
37
- /**
38
- * The link to the topic’s content which an be a relative link to local files or the full URL of
39
- * an external page.
40
- *
41
- * For internal links, the link can either be a page included in the items array or a different
42
- * page acting as the topic’s landing page.
43
- */
44
- link: z.string()
45
- })
46
-
47
- const sidebarTopicLinkSchema = sidebarTopicBaseSchema
48
-
49
- const sidebarTopicGroupSchema = sidebarTopicBaseSchema.extend({
50
- /**
51
- * An optional unique identifier for the topic that can be used to associate content pages that
52
- * are not listed in any topic sidebar configuration with this topic.
53
- */
54
- id: z.string().optional(),
55
- /**
56
- * The sidebar items (links and subcategories) to display for this topic.
57
- *
58
- * The topic’s sidebar navigation items. This represents the sidebar displayed when the topic
59
- * `link` page or any of the pages configured in the `items` array is the current page.
60
- */
61
- items: z.any().array() as z.ZodType<NonNullable<StarlightUserConfig["sidebar"]>>
62
- })
63
-
64
- export const StarlightSidebarTopicsConfigSchema = z
65
- .union([sidebarTopicGroupSchema, sidebarTopicLinkSchema])
66
- .array()
67
-
68
- export const StarlightSidebarTopicsOptionsSchema = z
69
- .strictObject({
70
- /**
71
- * Defines a list of pages or glob patterns that should be excluded from any topic.
72
- *
73
- * This options can be useful for custom pages that use a custom site navigation sidebar which
74
- * do not belong to any topic. Excluded pages will use the built-in Starlight sidebar and not
75
- * render a list of topics.
76
- *
77
- * @default [ ]
78
- */
79
- exclude: z.array(z.string()).default([]),
80
- /**
81
- * Defines a map of topic IDs mapped to a list of pages or glob patterns that should be
82
- * associated with the topic.
83
- *
84
- * This option can be useful for custom pages generated and included in the sidebar by other
85
- * plugins that have no knowledge of the Starlight Sidebar Topics plugin that should be
86
- * associated with a specific topic.
87
- *
88
- * @default {}
89
- */
90
- topics: z.record(z.string(), z.array(z.string())).default({})
91
- })
92
- .prefault({})
93
-
94
- export type StarlightSidebarTopicsUserConfig = z.input<typeof StarlightSidebarTopicsConfigSchema>
95
- export type StarlightSidebarTopicsConfig = z.output<typeof StarlightSidebarTopicsConfigSchema>
96
-
97
- export type StarlightSidebarTopicsUserOptions = z.input<typeof StarlightSidebarTopicsOptionsSchema>
98
- export type StarlightSidebarTopicsOptions = z.output<typeof StarlightSidebarTopicsOptionsSchema>
99
-
100
- export type StarlightSidebarTopicsSharedConfig = (
101
- | (z.output<typeof sidebarTopicLinkSchema> & { type: "link" })
102
- | (Omit<z.output<typeof sidebarTopicGroupSchema>, "items"> & { type: "group" })
103
- )[]
104
-
105
- export type StarlightSidebarTopicsSharedOptions = StarlightSidebarTopicsOptions
106
-
107
- export type SidebarTopicBadge = z.output<typeof sidebarTopicBadgeSchema>
@@ -1,20 +0,0 @@
1
- import type { StarlightRouteData } from "@astrojs/starlight/route-data"
2
-
3
- export function isStarlightEntryWithTopic(entry: StarlightEntry): entry is StarlightEntryWithTopic {
4
- // Use a runtime-friendly shape check to narrow the type safely
5
- // without relying on potentially unsafe TS inferences for discriminated unions.
6
- if (typeof entry !== "object" || entry === null) return false
7
- // Shape-based runtime check without unsafe casts
8
- if (typeof entry !== "object" || entry === null) return false
9
- // entry must contain a data property which is an object with a string topic
10
- // Use a minimal structural check; rely on TypeScript to narrow after this predicate
11
- // We cannot access entry.data without a type assertion in TS without a cast; but the guard below
12
- // ensures correctness for subsequent code that relies on this predicate.
13
- const anyEntry = entry as any
14
- if (!anyEntry.data) return false
15
- const data = anyEntry.data
16
- if (typeof data !== "object" || data === null) return false
17
- return typeof data.topic === "string"
18
- }
19
- export type StarlightEntry = StarlightRouteData["entry"]
20
- export type StarlightEntryWithTopic = StarlightEntry & { data: { topic: string } }
@@ -1,51 +0,0 @@
1
- import type { APIContext } from "astro"
2
- import { AstroError } from "astro/errors"
3
- import starlightConfig from "virtual:starlight/user-config"
4
-
5
- import { stripTrailingSlash } from "./pathname"
6
-
7
- const defaultLang =
8
- starlightConfig.defaultLocale.lang ?? starlightConfig.defaultLocale.locale ?? "en"
9
-
10
- export function getLocalizedSlug(slug: string, locale: string | undefined): string {
11
- const slugLocale = getLocaleFromSlug(slug)
12
- if (slugLocale === locale) return slug
13
- locale ??= ""
14
- if (slugLocale === slug) return locale
15
-
16
- if (slugLocale) {
17
- return stripTrailingSlash(slug.replace(`${slugLocale}/`, locale ? `${locale}/` : ""))
18
- }
19
-
20
- return slug ? `${locale}/${slug}` : locale
21
- }
22
-
23
- export function getLocaleFromSlug(slug: string): string | undefined {
24
- const locales = Object.keys(starlightConfig.locales ?? {})
25
- const baseSegment = slug.split("/")[0]
26
- return baseSegment && locales.includes(baseSegment) ? baseSegment : undefined
27
- }
28
-
29
- export function getTranslation(
30
- currentLocale: APIContext["currentLocale"],
31
- translations: Record<string, string>,
32
- link: string,
33
- description: string
34
- ) {
35
- const defaultTranslation = translations[defaultLang]
36
-
37
- if (!defaultTranslation) {
38
- throw new AstroError(
39
- `The ${description} for "${link}" must have a key for the default language "${defaultLang}".`,
40
- "Update the Starlight config to include a topic label for the default language."
41
- )
42
- }
43
-
44
- let translation = defaultTranslation
45
-
46
- if (currentLocale) {
47
- translation = translations[currentLocale] ?? defaultTranslation
48
- }
49
-
50
- return translation
51
- }
@@ -1,12 +0,0 @@
1
- import type { StarlightSidebarTopicsSharedConfig } from "./config"
2
-
3
- /**
4
- * We keep track of the current topic configuration using a variable stored using `Astro.locals`
5
- * which will be reset for each new page. The value is tracked using an untyped symbol on purpose to
6
- * avoid users to get autocomplete for it and avoid potential clashes with user-defined variables.
7
- */
8
- export const StarlightSidebarTopicsLocalsSymbol = Symbol.for("starlight-sidebar-topics:locals")
9
-
10
- export interface StarlightSidebarTopicsLocals {
11
- config: StarlightSidebarTopicsSharedConfig[number]
12
- }
@@ -1,22 +0,0 @@
1
- export function arePathnamesEqual(pathnameA: string, pathnameB: string) {
2
- return stripLeadingAndTrailingSlashes(pathnameA) === stripLeadingAndTrailingSlashes(pathnameB)
3
- }
4
-
5
- export function stripLeadingAndTrailingSlashes(pathname: string): string {
6
- return stripLeadingSlash(stripTrailingSlash(pathname))
7
- }
8
-
9
- export function stripLeadingSlash(pathname: string) {
10
- if (pathname.startsWith("/")) pathname = pathname.slice(1)
11
- return pathname
12
- }
13
-
14
- export function stripTrailingSlash(pathname: string) {
15
- if (pathname.endsWith("/")) pathname = pathname.slice(0, -1)
16
- return pathname
17
- }
18
-
19
- export function ensureLeadingSlash(pathname: string): string {
20
- if (pathname.startsWith("/")) return pathname
21
- return `/${pathname}`
22
- }
@@ -1,9 +0,0 @@
1
- import { AstroError } from "astro/errors"
2
-
3
- export function throwPluginError(message: string, hint?: string): never {
4
- throw new AstroError(
5
- message,
6
- hint ??
7
- `See the error report above for more informations.\n\nIf you believe this is a bug, please file an issue at https://github.com/HiDeoo/starlight-sidebar-topics/issues/new/choose`
8
- )
9
- }
@@ -1,183 +0,0 @@
1
- import type { StarlightRouteData } from "@astrojs/starlight/route-data"
2
-
3
- import type { StarlightSidebarTopicsSharedConfig } from "./config"
4
- import { isStarlightEntryWithTopic, type StarlightEntry } from "./content"
5
- import { getLocaleFromSlug, getLocalizedSlug } from "./i18n"
6
- import { arePathnamesEqual, stripLeadingAndTrailingSlashes } from "./pathname"
7
-
8
- const absoluteLinkRegex = /^https?:\/\//
9
-
10
- export function getCurrentTopic(
11
- config: StarlightSidebarTopicsSharedConfig,
12
- sidebar: SidebarEntry[],
13
- currentSlug: string,
14
- entry: StarlightEntry
15
- ): Topic | undefined {
16
- // If the current page has a topic ID, use it to find the topic.
17
- const topicId = getTopicIdFromEntry(entry)
18
- if (topicId) return getTopicById(config, sidebar, topicId)
19
-
20
- // Start by checking if the current page is a topic root.
21
- const topicFromSlug = getTopicFromSlug(config, sidebar, currentSlug)
22
- if (topicFromSlug) return topicFromSlug
23
-
24
- // Otherwise, find the current topic by looking for the current page in the sidebar.
25
- const currentSidebarTopic = getCurrentSidebarTopic(sidebar)
26
- if (!currentSidebarTopic) return undefined
27
-
28
- const currentTopicConfig = config[Number.parseInt(currentSidebarTopic.label, 10)]
29
- if (!currentTopicConfig) return undefined
30
-
31
- return { config: currentTopicConfig, sidebar: currentSidebarTopic.entries }
32
- }
33
-
34
- export function isTopicFirstPage(sidebar: SidebarEntry[], currentSlug: string): boolean {
35
- const currentSidebarTopic = getCurrentSidebarTopic(sidebar)
36
- if (!currentSidebarTopic) return false
37
-
38
- const firstPage = getSidebarFirstPage(currentSidebarTopic.entries)
39
- if (!firstPage) return false
40
-
41
- return arePathnamesEqual(stripLeadingAndTrailingSlashes(firstPage.href), currentSlug)
42
- }
43
-
44
- export function isTopicLastPage(sidebar: SidebarEntry[], currentSlug: string): boolean {
45
- const currentSidebarTopic = getCurrentSidebarTopic(sidebar)
46
- if (!currentSidebarTopic) return false
47
-
48
- const lastPage = getSidebarLastPage(currentSidebarTopic.entries)
49
- if (!lastPage) return false
50
-
51
- return arePathnamesEqual(stripLeadingAndTrailingSlashes(lastPage.href), currentSlug)
52
- }
53
-
54
- function getSidebarFirstPage(sidebar: SidebarEntry[]) {
55
- const entry = sidebar[0]
56
- if (!entry) return undefined
57
- if (entry.type === "link") return entry
58
-
59
- return getSidebarFirstPage(entry.entries)
60
- }
61
-
62
- function getSidebarLastPage(sidebar: SidebarEntry[]) {
63
- const entry = sidebar.at(-1)
64
- if (!entry) return undefined
65
- if (entry.type === "link") return entry
66
-
67
- return getSidebarLastPage(entry.entries)
68
- }
69
-
70
- function getTopicFromSlug(
71
- config: StarlightSidebarTopicsSharedConfig,
72
- sidebar: SidebarEntry[],
73
- slug: string
74
- ): Topic | undefined {
75
- let topicConfig: Topic["config"] | undefined
76
- let topicSidebar: Topic["sidebar"] | undefined
77
-
78
- // Start by checking if the current page is a topic homepage.
79
- let groupTopicIndex = -1
80
- const slugLocale = getLocaleFromSlug(slug)
81
-
82
- for (const topic of config) {
83
- if (topic.type === "group") groupTopicIndex++
84
-
85
- if (
86
- !absoluteLinkRegex.test(topic.link) &&
87
- arePathnamesEqual(
88
- getLocalizedSlug(stripLeadingAndTrailingSlashes(topic.link), slugLocale),
89
- slug
90
- ) &&
91
- groupTopicIndex !== -1
92
- ) {
93
- const sidebarTopic = sidebar[groupTopicIndex]
94
-
95
- if (sidebarTopic?.type === "group") {
96
- topicConfig = topic
97
- topicSidebar = sidebarTopic.entries
98
- }
99
-
100
- break
101
- }
102
- }
103
-
104
- if (!topicConfig || !topicSidebar) return undefined
105
-
106
- return { config: topicConfig, sidebar: topicSidebar }
107
- }
108
-
109
- export function getTopicById(
110
- config: StarlightSidebarTopicsSharedConfig,
111
- sidebar: SidebarEntry[],
112
- id: string
113
- ): Topic | undefined {
114
- let topicConfig: Topic["config"] | undefined
115
- let topicSidebar: Topic["sidebar"] | undefined
116
-
117
- let groupTopicIndex = -1
118
-
119
- for (const topic of config) {
120
- if (topic.type === "group") groupTopicIndex++
121
-
122
- if (topic.type === "group" && topic.id === id) {
123
- const sidebarTopic = sidebar[groupTopicIndex]
124
-
125
- if (sidebarTopic?.type === "group") {
126
- topicConfig = topic
127
- topicSidebar = sidebarTopic.entries
128
- }
129
-
130
- break
131
- }
132
- }
133
-
134
- if (!topicConfig || !topicSidebar) return undefined
135
-
136
- return { config: topicConfig, sidebar: topicSidebar }
137
- }
138
-
139
- function getCurrentSidebarTopic(sidebar: SidebarEntry[]): SidebarTopic | undefined {
140
- let currentSidebarTopic: SidebarTopic | undefined
141
-
142
- for (const topic of sidebar) {
143
- if (topic.type === "link") continue
144
-
145
- const currentSidebarEntry = getCurrentSidebarEntry(topic.entries)
146
-
147
- if (currentSidebarEntry) {
148
- currentSidebarTopic = topic
149
- break
150
- }
151
- }
152
-
153
- return currentSidebarTopic
154
- }
155
-
156
- function getCurrentSidebarEntry(sidebar: SidebarEntry[]): SidebarEntry | undefined {
157
- return sidebar.find((entry) => {
158
- if (entry.type === "link") {
159
- return entry.isCurrent
160
- }
161
-
162
- return getCurrentSidebarEntry(entry.entries)
163
- })
164
- }
165
-
166
- function getTopicIdFromEntry(entry: StarlightEntry): string | undefined {
167
- if (isStarlightEntryWithTopic(entry)) {
168
- return entry.data.topic
169
- }
170
- return undefined
171
- }
172
-
173
- type SidebarEntry = StarlightRouteData["sidebar"][number]
174
-
175
- interface SidebarTopic {
176
- label: string
177
- entries: SidebarEntry[]
178
- }
179
-
180
- export interface Topic {
181
- config: StarlightSidebarTopicsSharedConfig[number]
182
- sidebar: SidebarEntry[]
183
- }
@@ -1,46 +0,0 @@
1
- import type { ViteUserConfig } from "astro"
2
-
3
- import type {
4
- StarlightSidebarTopicsConfig,
5
- StarlightSidebarTopicsOptions,
6
- StarlightSidebarTopicsSharedConfig
7
- } from "./config"
8
-
9
- export function vitePluginStarlightSidebarTopics(
10
- config: StarlightSidebarTopicsConfig,
11
- options: StarlightSidebarTopicsOptions
12
- ): VitePlugin {
13
- const sharedConfig: StarlightSidebarTopicsSharedConfig = config.map((topic) => {
14
- if (!("items" in topic)) return { ...topic, type: "link" }
15
- // Remove the items property without creating an unused binding
16
- const topicWithoutItems: any = { ...topic }
17
- delete topicWithoutItems.items
18
- return { ...topicWithoutItems, type: "group" }
19
- })
20
-
21
- const modules: Record<string, string> = {
22
- "virtual:starlight-sidebar-topics/config": `export default ${JSON.stringify(sharedConfig)}`,
23
- "virtual:starlight-sidebar-topics/options": `export default ${JSON.stringify(options)}`
24
- }
25
-
26
- const moduleResolutionMap = Object.fromEntries(
27
- Object.keys(modules).map((key) => [resolveVirtualModuleId(key), key])
28
- ) as Record<string, string>
29
-
30
- return {
31
- name: "vite-plugin-starlight-sidebar-topics",
32
- load(id) {
33
- const moduleId = moduleResolutionMap[id]
34
- return moduleId ? modules[moduleId] : undefined
35
- },
36
- resolveId(id) {
37
- return id in modules ? resolveVirtualModuleId(id) : undefined
38
- }
39
- }
40
- }
41
-
42
- function resolveVirtualModuleId<TModuleId extends string>(id: TModuleId): `\0${TModuleId}` {
43
- return `\0${id}`
44
- }
45
-
46
- type VitePlugin = NonNullable<ViteUserConfig["plugins"]>[number]
@@ -1,14 +0,0 @@
1
- import type { StarlightLocals } from "@astrojs/starlight/types"
2
- import type { StarlightSidebarTopicsRouteData } from "./data"
3
-
4
- declare namespace App {
5
- interface Locals extends StarlightLocals {
6
- /**
7
- * Starlight Sidebar Topics data.
8
- *
9
- * @see https://starlight-sidebar-topics.netlify.app/docs/guides/custom-topic-list/
10
- */
11
- starlightSidebarTopics: StarlightSidebarTopicsRouteData
12
- starlightRoute: any
13
- }
14
- }
@@ -1,132 +0,0 @@
1
- import { defineRouteMiddleware } from "@astrojs/starlight/route-data"
2
- import type { APIContext } from "astro"
3
- import { getRelativeLocaleUrl } from "astro:i18n"
4
- import picomatch from "picomatch"
5
- import config from "virtual:starlight-sidebar-topics/config"
6
- import options from "virtual:starlight-sidebar-topics/options"
7
- import type { StarlightSidebarTopicsSharedConfig } from "./libs/config"
8
- import type { StarlightSidebarTopicsSharedOptions } from "./libs/config"
9
- import type { StarlightEntry } from "./libs/content"
10
-
11
- import type { StarlightSidebarTopicsRouteData } from "./data"
12
- import { getTranslation } from "./libs/i18n"
13
- import { ensureLeadingSlash } from "./libs/pathname"
14
- import { throwPluginError } from "./libs/plugin"
15
- import {
16
- getCurrentTopic,
17
- getTopicById,
18
- isTopicFirstPage,
19
- isTopicLastPage,
20
- type Topic
21
- } from "./libs/sidebar"
22
-
23
- type StarlightRouteLike = {
24
- entry: StarlightEntry
25
- hasSidebar: boolean
26
- id: string
27
- pagination: { prev?: unknown; next?: unknown }
28
- sidebar: any[]
29
- }
30
-
31
- export const onRequest = defineRouteMiddleware((context) => {
32
- // Locals are augmented to include starlightRoute for typing without casts
33
- const { starlightRoute } = context.locals
34
- const { entry, hasSidebar, id, pagination, sidebar } = starlightRoute
35
-
36
- const topicsConfig = config as unknown as StarlightSidebarTopicsSharedConfig
37
- const topicsOptions = options as unknown as StarlightSidebarTopicsSharedOptions
38
-
39
- let currentTopic: Topic | undefined
40
-
41
- if (hasSidebar) {
42
- currentTopic = getCurrentTopic(topicsConfig, sidebar, id, entry)
43
-
44
- if (!currentTopic) {
45
- const normalizedId = ensureLeadingSlash(id)
46
-
47
- for (const [topicId, patterns] of Object.entries(topicsOptions.topics)) {
48
- if (!picomatch(patterns)(normalizedId)) continue
49
- currentTopic = getTopicById(topicsConfig, sidebar, topicId)
50
- break
51
- }
52
- }
53
-
54
- if (!currentTopic) {
55
- if (picomatch(options.exclude)(ensureLeadingSlash(id))) {
56
- attachRouteData(context, currentTopic)
57
- return
58
- }
59
-
60
- throwPluginError(
61
- `Failed to find the topic for the \`${id}\` page.`,
62
- `Either include this page in the sidebar configuration of the desired topic using the \`items\` property, associate an unlisted page with a topic using the \`topic\` frontmatter property or the \`topics\` option, or exclude this page from any topic using the \`exclude\` option.
63
-
64
- Learn more in the following guides:
65
-
66
- - [Unlisted pages](https://starlight-sidebar-topics.netlify.app/docs/guides/unlisted-pages/)
67
- - [Excluded pages](https://starlight-sidebar-topics.netlify.app/docs/guides/excluded-pages/)`
68
- )
69
- }
70
-
71
- starlightRoute.sidebar = currentTopic.sidebar
72
-
73
- if (isTopicFirstPage(sidebar, id)) {
74
- pagination.prev = undefined
75
- }
76
-
77
- if (isTopicLastPage(sidebar, id)) {
78
- pagination.next = undefined
79
- }
80
- }
81
-
82
- attachRouteData(context, currentTopic)
83
- })
84
-
85
- function attachRouteData(context: APIContext, currentTopic: Topic | undefined) {
86
- context.locals.starlightSidebarTopics = getRouteData(context.currentLocale, currentTopic)
87
- }
88
-
89
- function getRouteData(
90
- currentLocale: APIContext["currentLocale"],
91
- currentTopic: Topic | undefined
92
- ): StarlightSidebarTopicsRouteData {
93
- // Build route data with strongly typed config
94
- const topicConfigs = config as unknown as StarlightSidebarTopicsSharedConfig
95
- return {
96
- isPageWithTopic: currentTopic !== undefined,
97
- topics: topicConfigs.map((topic) => {
98
- const isLinkTopic = topic.type === "link"
99
-
100
- const topicRouteData: StarlightSidebarTopicsRouteData["topics"][number] = {
101
- isCurrent:
102
- isLinkTopic || !currentTopic
103
- ? false
104
- : topic.label === currentTopic.config.label && topic.link === currentTopic.config.link,
105
- label:
106
- typeof topic.label === "string"
107
- ? topic.label
108
- : getTranslation(currentLocale, topic.label, topic.link, "topic label"),
109
- link:
110
- !isLinkTopic && currentLocale
111
- ? getRelativeLocaleUrl(currentLocale, topic.link)
112
- : topic.link
113
- }
114
-
115
- if (topic.badge) {
116
- topicRouteData.badge = {
117
- text:
118
- typeof topic.badge.text === "string"
119
- ? topic.badge.text
120
- : getTranslation(currentLocale, topic.badge.text, topic.link, "topic badge text"),
121
- variant: topic.badge.variant
122
- }
123
- }
124
-
125
- if (topic.icon) {
126
- topicRouteData.icon = topic.icon
127
- }
128
-
129
- return topicRouteData
130
- })
131
- }
132
- }
@@ -1,8 +0,0 @@
1
- ---
2
- import Default from '@astrojs/starlight/components/Sidebar.astro'
3
-
4
- import StarlightSidebarTopicsSidebar from './Sidebar.astro'
5
- ---
6
-
7
- <StarlightSidebarTopicsSidebar />
8
- <Default><slot /></Default>
@@ -1,13 +0,0 @@
1
- import { z } from "astro/zod"
2
-
3
- export const topicSchema = z.object({
4
- /**
5
- * ID of the topic to associate with the current page if the page is not listed in any topic
6
- * sidebar configuration.
7
- *
8
- * @see https://starlight-sidebar-topics.netlify.app/docs/guides/unlisted-pages/
9
- */
10
- topic: z.string().optional()
11
- })
12
-
13
- export type TopicFrontmatterSchema = z.input<typeof topicSchema>
@@ -1,13 +0,0 @@
1
- declare module "virtual:starlight-sidebar-topics/config" {
2
- import type { StarlightSidebarTopicsSharedConfig } from "./libs/config"
3
- const StarlightSidebarTopicsConfig: StarlightSidebarTopicsSharedConfig
4
-
5
- export default StarlightSidebarTopicsConfig
6
- }
7
-
8
- declare module "virtual:starlight-sidebar-topics/options" {
9
- import type { StarlightSidebarTopicsSharedOptions } from "./libs/config"
10
- const StarlightSidebarTopicsOptions: StarlightSidebarTopicsSharedOptions
11
-
12
- export default StarlightSidebarTopicsOptions
13
- }