starlight-theme-apple 0.6.0 → 0.9.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +47 -5
  3. package/components/Footer.astro +19 -0
  4. package/components/Header.astro +55 -0
  5. package/components/NavLink.astro +24 -0
  6. package/components/PageFrame.astro +11 -0
  7. package/components/PageTitle.astro +24 -40
  8. package/components/Pagination.astro +22 -29
  9. package/components/Sidebar.astro +27 -0
  10. package/components/SiteFooter.astro +61 -0
  11. package/components/SocialIcons.astro +22 -0
  12. package/components.ts +10 -0
  13. package/index.ts +81 -120
  14. package/libs/breadcrumbs.ts +80 -0
  15. package/libs/config.ts +54 -0
  16. package/libs/expressive-code.ts +246 -0
  17. package/libs/links.ts +38 -0
  18. package/package.json +11 -5
  19. package/styles/base.css +35 -53
  20. package/styles/code.css +7 -83
  21. package/styles/components.css +599 -0
  22. package/styles/content.css +224 -414
  23. package/styles/header.css +217 -62
  24. package/styles/reference.css +332 -0
  25. package/styles/search.css +87 -73
  26. package/styles/sidebar.css +77 -56
  27. package/styles/toc.css +55 -40
  28. package/styles/tokens.css +134 -93
  29. package/styles.css +4 -4
  30. package/translations.ts +31 -0
  31. package/user-components/Availability.astro +21 -0
  32. package/user-components/Declaration.astro +16 -0
  33. package/user-components/Figure.astro +58 -0
  34. package/user-components/Parameter.astro +14 -0
  35. package/user-components/ParameterList.astro +11 -0
  36. package/user-components/ReferenceEntry.astro +12 -0
  37. package/user-components/ReferenceMeta.astro +30 -0
  38. package/user-components/TopicLink.astro +17 -0
  39. package/user-components/TopicList.astro +5 -0
  40. package/user-components/ValueList.astro +20 -0
  41. package/virtual.d.ts +10 -0
  42. package/styles/controls.css +0 -190
  43. package/styles/responsive.css +0 -148
package/index.ts CHANGED
@@ -1,137 +1,98 @@
1
+ /// <reference path="./virtual.d.ts" />
1
2
  import type { StarlightPlugin } from "@astrojs/starlight/types";
3
+ import type { AstroIntegration } from "astro";
2
4
  import { fileURLToPath } from "node:url";
3
5
 
4
- const doccLight = {
5
- name: "apple-docc-light",
6
- type: "light" as const,
7
- colors: {
8
- "editor.background": "#f5f5f7",
9
- "editor.foreground": "#000000",
10
- },
11
- settings: [
12
- { settings: { foreground: "#000000", background: "#f5f5f7" } },
13
- {
14
- scope: ["comment", "punctuation.definition.comment"],
15
- settings: { foreground: "#707f8c" },
16
- },
17
- {
18
- scope: ["keyword", "storage", "storage.type"],
19
- settings: { foreground: "#ad3da4" },
20
- },
21
- {
22
- scope: ["string", "string.quoted", "constant.other.symbol"],
23
- settings: { foreground: "#d12f1b" },
24
- },
25
- {
26
- scope: ["constant.numeric", "constant.character"],
27
- settings: { foreground: "#272ad8" },
28
- },
29
- {
30
- scope: ["entity.name.type", "entity.name.class", "support.type"],
31
- settings: { foreground: "#703daa" },
32
- },
33
- {
34
- scope: ["entity.name.function", "support.function", "variable.function"],
35
- settings: { foreground: "#4b21b0" },
36
- },
37
- {
38
- scope: ["entity.other.attribute-name", "variable.other.constant"],
39
- settings: { foreground: "#947100" },
40
- },
41
- {
42
- scope: ["meta.preprocessor", "keyword.control.directive"],
43
- settings: { foreground: "#78492a" },
44
- },
45
- ],
6
+ import {
7
+ type StarlightThemeAppleConfig,
8
+ type StarlightThemeAppleUserConfig,
9
+ configSchema,
10
+ } from "./libs/config";
11
+ import { expressiveCodeConfig } from "./libs/expressive-code";
12
+ import { translations } from "./translations";
13
+
14
+ export type {
15
+ StarlightThemeAppleConfig,
16
+ StarlightThemeAppleLink,
17
+ StarlightThemeAppleUserConfig,
18
+ } from "./libs/config";
19
+
20
+ const component = (name: string) =>
21
+ fileURLToPath(new URL(`./components/${name}.astro`, import.meta.url));
22
+
23
+ /** Components the theme provides. Consumer and earlier-plugin overrides always win. */
24
+ const themeComponents = {
25
+ Header: component("Header"),
26
+ PageFrame: component("PageFrame"),
27
+ Sidebar: component("Sidebar"),
28
+ SocialIcons: component("SocialIcons"),
29
+ PageTitle: component("PageTitle"),
30
+ Footer: component("Footer"),
31
+ Pagination: component("Pagination"),
46
32
  };
47
33
 
48
- const doccDark = {
49
- name: "apple-docc-dark",
50
- type: "dark" as const,
51
- colors: {
52
- "editor.background": "#1c1c1e",
53
- "editor.foreground": "#ffffff",
54
- },
55
- settings: [
56
- { settings: { foreground: "#ffffff", background: "#1c1c1e" } },
57
- {
58
- scope: ["comment", "punctuation.definition.comment"],
59
- settings: { foreground: "#7f8c98" },
60
- },
61
- {
62
- scope: ["keyword", "storage", "storage.type"],
63
- settings: { foreground: "#ff7ab2" },
64
- },
65
- {
66
- scope: ["string", "string.quoted", "constant.other.symbol"],
67
- settings: { foreground: "#ff8170" },
68
- },
69
- {
70
- scope: ["constant.numeric", "constant.character"],
71
- settings: { foreground: "#d9c97c" },
72
- },
73
- {
74
- scope: ["entity.name.type", "entity.name.class", "support.type"],
75
- settings: { foreground: "#dabaff" },
76
- },
77
- {
78
- scope: ["entity.name.function", "support.function", "variable.function"],
79
- settings: { foreground: "#b281eb" },
80
- },
81
- {
82
- scope: ["entity.other.attribute-name", "variable.other.constant"],
83
- settings: { foreground: "#cc9768" },
84
- },
85
- {
86
- scope: ["meta.preprocessor", "keyword.control.directive"],
87
- settings: { foreground: "#ffa14f" },
34
+ function configIntegration(
35
+ config: StarlightThemeAppleConfig
36
+ ): AstroIntegration {
37
+ const moduleId = "virtual:starlight-theme-apple/config";
38
+ const resolvedId = `\0${moduleId}`;
39
+ return {
40
+ name: "starlight-theme-apple-config",
41
+ hooks: {
42
+ "astro:config:setup"({ updateConfig }) {
43
+ updateConfig({
44
+ vite: {
45
+ plugins: [
46
+ {
47
+ name: "vite-plugin-starlight-theme-apple-config",
48
+ resolveId: (id) => (id === moduleId ? resolvedId : undefined),
49
+ load: (id) =>
50
+ id === resolvedId
51
+ ? `export default ${JSON.stringify(config)};`
52
+ : undefined,
53
+ },
54
+ ],
55
+ },
56
+ });
57
+ },
88
58
  },
89
- ],
90
- };
59
+ };
60
+ }
61
+
62
+ export default function starlightThemeApple(
63
+ userConfig?: StarlightThemeAppleUserConfig
64
+ ): StarlightPlugin {
65
+ const parsed = configSchema.safeParse(userConfig);
66
+ if (!parsed.success) {
67
+ throw new Error(
68
+ `Invalid starlight-theme-apple configuration:\n${parsed.error.issues
69
+ .map(
70
+ (issue) => `- ${issue.path.join(".") || "(root)"}: ${issue.message}`
71
+ )
72
+ .join("\n")}`
73
+ );
74
+ }
75
+ const config = parsed.data;
91
76
 
92
- export default function starlightThemeApple(): StarlightPlugin {
93
77
  return {
94
78
  name: "starlight-theme-apple",
95
79
  hooks: {
96
- "config:setup"({ config, updateConfig }) {
97
- const userExpressiveCodeConfig =
98
- !config.expressiveCode || config.expressiveCode === true
99
- ? {}
100
- : config.expressiveCode;
101
-
80
+ "i18n:setup"({ injectTranslations }) {
81
+ injectTranslations(translations);
82
+ },
83
+ "config:setup"({
84
+ config: starlightConfig,
85
+ updateConfig,
86
+ addIntegration,
87
+ }) {
88
+ addIntegration(configIntegration(config));
102
89
  updateConfig({
103
- components: {
104
- ...config.components,
105
- PageTitle: fileURLToPath(
106
- new URL("./components/PageTitle.astro", import.meta.url)
107
- ),
108
- Pagination: fileURLToPath(
109
- new URL("./components/Pagination.astro", import.meta.url)
110
- ),
111
- },
90
+ components: { ...themeComponents, ...starlightConfig.components },
112
91
  customCss: [
113
- ...(config.customCss ?? []),
114
92
  "starlight-theme-apple/styles.css",
93
+ ...(starlightConfig.customCss ?? []),
115
94
  ],
116
- expressiveCode:
117
- config.expressiveCode === false
118
- ? false
119
- : {
120
- themes: [doccDark, doccLight],
121
- ...userExpressiveCodeConfig,
122
- styleOverrides: {
123
- borderColor: "var(--apple-hairline)",
124
- borderRadius: "var(--apple-radius-sm)",
125
- ...userExpressiveCodeConfig.styleOverrides,
126
- frames: {
127
- editorActiveTabIndicatorTopColor: "unset",
128
- editorActiveTabIndicatorBottomColor: "var(--apple-link)",
129
- editorTabBarBorderBottomColor: "var(--apple-hairline)",
130
- frameBoxShadowCssValue: "unset",
131
- ...userExpressiveCodeConfig.styleOverrides?.frames,
132
- },
133
- },
134
- },
95
+ expressiveCode: expressiveCodeConfig(starlightConfig.expressiveCode),
135
96
  });
136
97
  },
137
98
  },
@@ -0,0 +1,80 @@
1
+ interface SidebarLink {
2
+ type: "link";
3
+ label: string;
4
+ href: string;
5
+ isCurrent: boolean;
6
+ }
7
+ interface SidebarGroup {
8
+ type: "group";
9
+ label: string;
10
+ entries: SidebarEntry[];
11
+ }
12
+ type SidebarEntry = SidebarLink | SidebarGroup;
13
+
14
+ export interface Crumb {
15
+ label: string;
16
+ href?: string | undefined;
17
+ }
18
+
19
+ const isInternal = (href: string) =>
20
+ href.startsWith("/") && !href.startsWith("//");
21
+
22
+ function links(entries: SidebarEntry[]): SidebarLink[] {
23
+ return entries.flatMap((entry) =>
24
+ entry.type === "link" ? [entry] : links(entry.entries)
25
+ );
26
+ }
27
+
28
+ /**
29
+ * A group’s landing page is a direct child link whose path contains every other page in the
30
+ * group, such as an “Overview” at `/guides/` above `/guides/install/`.
31
+ */
32
+ function landingPage(group: SidebarGroup): SidebarLink | undefined {
33
+ const pages = links(group.entries).filter((link) => isInternal(link.href));
34
+ if (pages.length < 2) return undefined;
35
+ return group.entries.find(
36
+ (entry): entry is SidebarLink =>
37
+ entry.type === "link" &&
38
+ isInternal(entry.href) &&
39
+ pages.every((page) => page.href.startsWith(entry.href))
40
+ );
41
+ }
42
+
43
+ function ancestors(
44
+ entries: SidebarEntry[],
45
+ trail: SidebarGroup[] = []
46
+ ): SidebarGroup[] | undefined {
47
+ for (const entry of entries) {
48
+ if (entry.type === "link") {
49
+ if (entry.isCurrent) return trail;
50
+ } else {
51
+ const found = ancestors(entry.entries, [...trail, entry]);
52
+ if (found) return found;
53
+ }
54
+ }
55
+ return undefined;
56
+ }
57
+
58
+ /**
59
+ * Breadcrumbs from the site root through the sidebar groups containing the current page.
60
+ * Groups link to their landing page when they have one. A group whose landing page is the
61
+ * current page, or the site root itself, is omitted because it would repeat the title or root.
62
+ */
63
+ export function breadcrumbs(
64
+ sidebar: unknown,
65
+ siteTitle: string,
66
+ siteHref: string
67
+ ): Crumb[] {
68
+ const entries = sidebar as SidebarEntry[];
69
+ const trail = ancestors(entries);
70
+ const current = links(entries).find((link) => link.isCurrent);
71
+ if (!trail || current?.href === siteHref) return [];
72
+
73
+ const crumbs: Crumb[] = [{ label: siteTitle, href: siteHref }];
74
+ for (const group of trail) {
75
+ const landing = landingPage(group);
76
+ if (landing?.isCurrent || landing?.href === siteHref) continue;
77
+ crumbs.push({ label: group.label, href: landing?.href });
78
+ }
79
+ return crumbs;
80
+ }
package/libs/config.ts ADDED
@@ -0,0 +1,54 @@
1
+ import type { StarlightIcon } from "@astrojs/starlight/types";
2
+ import { z } from "astro/zod";
3
+
4
+ const attrsSchema = z
5
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean()]))
6
+ .optional();
7
+
8
+ const linkSchema = z.object({
9
+ /** Visible text for the link. */
10
+ label: z.string().min(1),
11
+ /**
12
+ * Destination. Paths starting with `/` are resolved against Astro's `base`, the same way
13
+ * Starlight resolves sidebar links. Full URLs are used as they are.
14
+ */
15
+ link: z.string().min(1),
16
+ /** Optional Starlight icon name shown before the label. */
17
+ icon: z
18
+ .custom<StarlightIcon>((value) => typeof value === "string")
19
+ .optional(),
20
+ /** Extra HTML attributes for the rendered `<a>`, e.g. analytics `data-*` attributes. */
21
+ attrs: attrsSchema,
22
+ });
23
+
24
+ export const configSchema = z
25
+ .object({
26
+ /** Primary navigation shown in the header and, on small screens, in the navigation menu. */
27
+ nav: z.array(linkSchema).default([]),
28
+ /** One emphasized header action, such as “Download”. */
29
+ action: linkSchema.optional(),
30
+ /** Extra HTML attributes for Starlight `social` links, keyed by their `icon` name. */
31
+ socialAttrs: z.record(z.string(), attrsSchema.unwrap()).default({}),
32
+ footer: z
33
+ .object({
34
+ /** Optional columns of site navigation. */
35
+ groups: z
36
+ .array(
37
+ z.object({
38
+ label: z.string().min(1),
39
+ links: z.array(linkSchema),
40
+ })
41
+ )
42
+ .default([]),
43
+ /** Supporting links shown in the site footer. */
44
+ links: z.array(linkSchema).default([]),
45
+ /** A short line of text such as a copyright notice. */
46
+ copyright: z.string().optional(),
47
+ })
48
+ .default({ groups: [], links: [] }),
49
+ })
50
+ .default({ nav: [], socialAttrs: {}, footer: { groups: [], links: [] } });
51
+
52
+ export type StarlightThemeAppleUserConfig = z.input<typeof configSchema>;
53
+ export type StarlightThemeAppleConfig = z.output<typeof configSchema>;
54
+ export type StarlightThemeAppleLink = z.output<typeof linkSchema>;
@@ -0,0 +1,246 @@
1
+ import type { StarlightUserConfig } from "@astrojs/starlight/types";
2
+
3
+ type ExpressiveCodeConfig = Exclude<
4
+ NonNullable<StarlightUserConfig["expressiveCode"]>,
5
+ boolean
6
+ >;
7
+
8
+ interface Palette {
9
+ background: string;
10
+ foreground: string;
11
+ comment: string;
12
+ keyword: string;
13
+ string: string;
14
+ number: string;
15
+ type: string;
16
+ function: string;
17
+ property: string;
18
+ attribute: string;
19
+ preprocessor: string;
20
+ }
21
+
22
+ /** Syntax colors modeled on Xcode's default light and dark themes. */
23
+ function xcodeTheme(name: string, type: "light" | "dark", p: Palette) {
24
+ return {
25
+ name,
26
+ type,
27
+ colors: {
28
+ "editor.background": p.background,
29
+ "editor.foreground": p.foreground,
30
+ },
31
+ settings: [
32
+ { settings: { foreground: p.foreground, background: p.background } },
33
+ {
34
+ scope: ["comment", "punctuation.definition.comment"],
35
+ settings: { foreground: p.comment },
36
+ },
37
+ {
38
+ scope: [
39
+ "keyword",
40
+ "storage",
41
+ "storage.type",
42
+ "storage.modifier",
43
+ "constant.language",
44
+ ],
45
+ settings: { foreground: p.keyword, fontStyle: "bold" },
46
+ },
47
+ {
48
+ scope: ["keyword.operator", "punctuation", "meta.brace"],
49
+ settings: { foreground: p.foreground, fontStyle: "" },
50
+ },
51
+ {
52
+ scope: [
53
+ "string",
54
+ "string.quoted",
55
+ "constant.other.symbol",
56
+ "markup.inline.raw",
57
+ ],
58
+ settings: { foreground: p.string },
59
+ },
60
+ {
61
+ scope: [
62
+ "constant.numeric",
63
+ "constant.character",
64
+ "constant.other.boolean",
65
+ ],
66
+ settings: { foreground: p.number },
67
+ },
68
+ {
69
+ scope: [
70
+ "entity.name.type",
71
+ "entity.name.class",
72
+ "support.type",
73
+ "support.class",
74
+ "entity.name.tag",
75
+ ],
76
+ settings: { foreground: p.type },
77
+ },
78
+ {
79
+ scope: [
80
+ "entity.name.function",
81
+ "support.function",
82
+ "variable.function",
83
+ "meta.function-call",
84
+ ],
85
+ settings: { foreground: p.function },
86
+ },
87
+ {
88
+ scope: [
89
+ "support.type.property-name",
90
+ "meta.object-literal.key",
91
+ "variable.other.property",
92
+ "variable.other.key",
93
+ "entity.name.section",
94
+ "variable.other.constant",
95
+ ],
96
+ settings: { foreground: p.property },
97
+ },
98
+ {
99
+ scope: [
100
+ "entity.other.attribute-name",
101
+ "meta.attribute",
102
+ "variable.parameter",
103
+ ],
104
+ settings: { foreground: p.attribute },
105
+ },
106
+ {
107
+ scope: [
108
+ "meta.preprocessor",
109
+ "keyword.control.directive",
110
+ "entity.name.function.preprocessor",
111
+ ],
112
+ settings: { foreground: p.preprocessor },
113
+ },
114
+ {
115
+ scope: ["markup.heading", "markup.bold"],
116
+ settings: { fontStyle: "bold" },
117
+ },
118
+ { scope: ["markup.italic"], settings: { fontStyle: "italic" } },
119
+ { scope: ["markup.deleted"], settings: { foreground: p.string } },
120
+ { scope: ["markup.inserted"], settings: { foreground: p.property } },
121
+ ],
122
+ };
123
+ }
124
+
125
+ export const xcodeLight = xcodeTheme("apple-xcode-light", "light", {
126
+ background: "#f5f5f7",
127
+ foreground: "#1d1d1f",
128
+ comment: "#5d6c79",
129
+ keyword: "#9b2393",
130
+ string: "#c41a16",
131
+ number: "#1c00cf",
132
+ type: "#703daa",
133
+ function: "#3900a0",
134
+ property: "#326d74",
135
+ attribute: "#815f03",
136
+ preprocessor: "#643820",
137
+ });
138
+
139
+ export const xcodeDark = xcodeTheme("apple-xcode-dark", "dark", {
140
+ background: "#1d1d1f",
141
+ foreground: "#f5f5f7",
142
+ comment: "#7f8c98",
143
+ keyword: "#ff7ab2",
144
+ string: "#ff8170",
145
+ number: "#d9c97c",
146
+ type: "#dabaff",
147
+ function: "#b281eb",
148
+ property: "#78c2b3",
149
+ attribute: "#cc9768",
150
+ preprocessor: "#ffa14f",
151
+ });
152
+
153
+ /**
154
+ * Apple code listings are flat, borderless panels. Colors come from theme tokens so code
155
+ * follows the page palette and any token a consumer overrides.
156
+ */
157
+ const styleOverrides = {
158
+ borderWidth: "0px",
159
+ borderRadius: "var(--apple-radius-panel)",
160
+ borderColor: "var(--apple-hairline)",
161
+ codeBackground: "var(--apple-code-background)",
162
+ codeFontFamily: "var(--apple-font-mono)",
163
+ codeFontSize: "0.875rem",
164
+ codeLineHeight: "1.65",
165
+ codePaddingBlock: "0.875rem",
166
+ codePaddingInline: "1.125rem",
167
+ uiFontFamily: "var(--apple-font-sans)",
168
+ uiFontSize: "0.8125rem",
169
+ focusBorder: "var(--apple-link)",
170
+ scrollbarThumbColor: "var(--apple-hairline)",
171
+ textMarkers: {
172
+ lineMarkerAccentWidth: "0.1875rem",
173
+ markBackground: "color-mix(in srgb, var(--apple-link) 12%, transparent)",
174
+ markBorderColor: "color-mix(in srgb, var(--apple-link) 55%, transparent)",
175
+ insBackground: "color-mix(in srgb, var(--apple-success) 13%, transparent)",
176
+ insBorderColor: "color-mix(in srgb, var(--apple-success) 60%, transparent)",
177
+ insDiffIndicatorColor: "var(--apple-success)",
178
+ delBackground: "color-mix(in srgb, var(--apple-danger) 11%, transparent)",
179
+ delBorderColor: "color-mix(in srgb, var(--apple-danger) 55%, transparent)",
180
+ delDiffIndicatorColor: "var(--apple-danger)",
181
+ },
182
+ frames: {
183
+ shadowColor: "transparent",
184
+ frameBoxShadowCssValue: "none",
185
+ editorBackground: "var(--apple-code-background)",
186
+ editorTabBarBackground: "var(--apple-code-background)",
187
+ editorTabBarBorderColor: "transparent",
188
+ editorTabBarBorderBottomColor: "var(--apple-hairline)",
189
+ editorActiveTabBackground: "var(--apple-code-background)",
190
+ editorActiveTabForeground: "var(--apple-secondary-foreground)",
191
+ editorActiveTabBorderColor: "transparent",
192
+ editorActiveTabIndicatorHeight: "0px",
193
+ editorActiveTabIndicatorTopColor: "transparent",
194
+ editorActiveTabIndicatorBottomColor: "transparent",
195
+ editorTabBorderRadius: "0px",
196
+ editorTabsMarginInlineStart: "0.375rem",
197
+ terminalBackground: "var(--apple-code-background)",
198
+ terminalTitlebarBackground: "var(--apple-code-background)",
199
+ terminalTitlebarForeground: "var(--apple-secondary-foreground)",
200
+ terminalTitlebarBorderBottomColor: "var(--apple-hairline)",
201
+ terminalTitlebarDotsForeground: "var(--apple-tertiary-foreground)",
202
+ terminalTitlebarDotsOpacity: "0.35",
203
+ inlineButtonBackground: "var(--apple-background)",
204
+ inlineButtonForeground: "var(--apple-secondary-foreground)",
205
+ inlineButtonBorder: "var(--apple-hairline)",
206
+ inlineButtonBorderOpacity: "1",
207
+ inlineButtonBackgroundIdleOpacity: "0.85",
208
+ inlineButtonBackgroundHoverOrFocusOpacity: "1",
209
+ inlineButtonBackgroundActiveOpacity: "1",
210
+ tooltipSuccessBackground: "var(--apple-success)",
211
+ tooltipSuccessForeground: "#fff",
212
+ },
213
+ };
214
+
215
+ /** Merge the theme's Expressive Code defaults beneath the consumer's own configuration. */
216
+ export function expressiveCodeConfig(
217
+ user: StarlightUserConfig["expressiveCode"]
218
+ ): StarlightUserConfig["expressiveCode"] {
219
+ if (user === false) return false;
220
+ const config: ExpressiveCodeConfig = user === true || !user ? {} : user;
221
+ const userStyles = (config.styleOverrides ?? {}) as Record<
222
+ string,
223
+ unknown
224
+ > & {
225
+ frames?: Record<string, unknown>;
226
+ textMarkers?: Record<string, unknown>;
227
+ };
228
+ return {
229
+ themes: [xcodeDark, xcodeLight],
230
+ // Shell snippets render as plain code listings rather than empty terminal windows.
231
+ // Use `frame="terminal"` on a block, or set `defaultProps`, to restore the window chrome.
232
+ defaultProps: {
233
+ overridesByLang: {
234
+ "ansi,bash,sh,shell,shellscript,shellsession,zsh,fish,powershell,ps,ps1":
235
+ { frame: "code" },
236
+ },
237
+ },
238
+ ...config,
239
+ styleOverrides: {
240
+ ...styleOverrides,
241
+ ...userStyles,
242
+ frames: { ...styleOverrides.frames, ...userStyles.frames },
243
+ textMarkers: { ...styleOverrides.textMarkers, ...userStyles.textMarkers },
244
+ },
245
+ } as ExpressiveCodeConfig;
246
+ }
package/libs/links.ts ADDED
@@ -0,0 +1,38 @@
1
+ import type { StarlightThemeAppleLink } from "./config";
2
+
3
+ const base = import.meta.env.BASE_URL.replace(/\/$/, "");
4
+ const hasProtocol = (link: string) => /^[a-z][a-z\d+.-]*:/i.test(link);
5
+
6
+ /** Resolve a configured link against the site `base`, as Starlight does for sidebar links. */
7
+ export function resolveHref(link: string): string {
8
+ if (hasProtocol(link) || link.startsWith("//") || link.startsWith("#"))
9
+ return link;
10
+ const path = link.startsWith("/") ? link : `/${link}`;
11
+ return `${base}${path}`;
12
+ }
13
+
14
+ const normalize = (path: string) => path.replace(/\/?(index\.html)?$/, "/");
15
+
16
+ /**
17
+ * A header link is current when the page is the link target or lives below it.
18
+ * The site root only matches itself so it does not mark every page as current.
19
+ */
20
+ export function isCurrentSection(href: string, pathname: string): boolean {
21
+ if (hasProtocol(href) || href.startsWith("//") || href.startsWith("#"))
22
+ return false;
23
+ const target = normalize(href.split(/[?#]/)[0] ?? href);
24
+ const current = normalize(pathname);
25
+ if (target === normalize(`${base}/`)) return current === target;
26
+ return current.startsWith(target);
27
+ }
28
+
29
+ export function linkProps(item: StarlightThemeAppleLink, pathname: string) {
30
+ const href = resolveHref(item.link);
31
+ const external = hasProtocol(href) || href.startsWith("//");
32
+ return {
33
+ href,
34
+ current: isCurrentSection(href, pathname),
35
+ external,
36
+ attrs: item.attrs ?? {},
37
+ };
38
+ }
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "starlight-theme-apple",
3
- "version": "0.6.0",
3
+ "version": "0.9.0",
4
4
  "description": "An Apple Developer Documentation-inspired theme for Astro Starlight.",
5
5
  "keywords": [
6
6
  "starlight",
7
+ "starlight-plugin",
8
+ "starlight-theme",
7
9
  "theme",
10
+ "apple",
8
11
  "apple-developer-documentation",
9
- "docs",
10
- "black",
11
- "dark"
12
+ "docs"
12
13
  ],
13
14
  "homepage": "https://acsandmann.github.io/starlight-theme-apple/",
14
15
  "bugs": {
@@ -24,15 +25,20 @@
24
25
  "type": "module",
25
26
  "exports": {
26
27
  ".": "./index.ts",
28
+ "./components": "./components.ts",
27
29
  "./styles.css": "./styles.css",
28
30
  "./styles/*": "./styles/*"
29
31
  },
32
+ "scripts": {
33
+ "test": "bun test"
34
+ },
30
35
  "devDependencies": {
31
36
  "@astrojs/starlight": "^0.41.7",
32
37
  "astro": "^7.0.2"
33
38
  },
34
39
  "peerDependencies": {
35
- "@astrojs/starlight": ">=0.38"
40
+ "@astrojs/starlight": ">=0.41.0",
41
+ "astro": ">=7.0.0"
36
42
  },
37
43
  "engines": {
38
44
  "node": ">=22.23.2"