@grove-dev/starlight 0.6.1 → 0.8.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 (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -78
  3. package/components/custom/ContainerSection.astro +1 -1
  4. package/components/custom/LinkButton.astro +134 -89
  5. package/components/custom/dropdown/Dropdown.astro +5 -5
  6. package/components/custom/dropdown/DropdownContent.astro +66 -66
  7. package/components/custom/dropdown/DropdownItem.astro +14 -9
  8. package/components/custom/dropdown/DropdownLabel.astro +4 -4
  9. package/components/custom/dropdown/DropdownTrigger.astro +13 -13
  10. package/components/custom/dropdown/index.ts +14 -14
  11. package/components/overrides/Footer.astro +3 -3
  12. package/components/overrides/Header.astro +5 -6
  13. package/components/overrides/Hero.astro +20 -12
  14. package/components/overrides/PageFrame.astro +25 -9
  15. package/components/overrides/PageSidebar.astro +1 -0
  16. package/components/overrides/PageTitle.astro +3 -3
  17. package/components/overrides/Search.astro +6 -6
  18. package/components/overrides/Sidebar.astro +38 -1
  19. package/components/overrides/SocialIcons.astro +3 -1
  20. package/components/overrides/ThemeSelect.astro +4 -4
  21. package/components/overrides/TwoColumnContent.astro +1 -1
  22. package/components/overrides/parts/Drawer.astro +13 -7
  23. package/components/overrides/parts/NavBar.astro +12 -31
  24. package/components/overrides/parts/SidebarSublist.astro +75 -4
  25. package/components/overrides/parts/toc/TableOfContentsList.astro +4 -4
  26. package/components/overrides/parts/toc/starlight-toc.ts +96 -98
  27. package/core/config/docs-schema.ts +58 -0
  28. package/core/config/expresive-code.ts +49 -51
  29. package/core/config/override.ts +37 -33
  30. package/core/config/schemas.ts +62 -41
  31. package/core/config/vite.ts +14 -14
  32. package/core/i18n.ts +175 -0
  33. package/core/plugin.ts +45 -43
  34. package/core/sidebar.ts +22 -0
  35. package/global.d.ts +3 -3
  36. package/package.json +3 -19
  37. package/schema.ts +44 -13
  38. package/styles/base.css +883 -896
  39. package/styles/layers.css +1 -1
  40. package/styles/theme.css +62 -62
  41. package/user-components.ts +1 -2
  42. package/virtual.d.ts +28 -28
  43. package/THIRD_PARTY_LICENSES.md +0 -68
  44. package/components/custom/Card.astro +0 -118
@@ -1,36 +1,19 @@
1
1
  ---
2
- import { AstroError } from 'astro/errors';
3
- import userConfig from 'virtual:lucode-starlight-config';
4
- import starlightConfig from 'virtual:starlight/user-config';
5
2
  import { getRelativeLocaleUrl } from 'astro:i18n';
3
+ import userConfig from 'virtual:grove-starlight-config';
4
+ import starlightConfig from 'virtual:starlight/user-config';
5
+ import { createLocaleLookup, resolveNavLabel } from '../../../core/i18n';
6
6
 
7
7
  const currentPath = Astro.url.pathname;
8
8
 
9
- const defaultLang =
10
- starlightConfig.defaultLocale?.lang || starlightConfig.defaultLocale?.locale || 'en';
11
-
12
- export function getTranslation(
13
- translations: Record<string, string>,
14
- link: string,
15
- description: string
16
- ) {
17
- const defaultTranslation = translations[defaultLang];
18
-
19
- if (!defaultTranslation) {
20
- throw new AstroError(
21
- `The ${description} for "${link}" must have a key for the default language "${defaultLang}".`,
22
- 'Update the Starlight config to include a topic label for the default language.'
23
- );
24
- }
9
+ const { lang, locale } = Astro.locals.starlightRoute;
25
10
 
26
- let translation = defaultTranslation;
27
-
28
- if (Astro.currentLocale) {
29
- translation = translations[Astro.currentLocale] ?? defaultTranslation;
30
- }
31
-
32
- return translation;
33
- }
11
+ const localeKeys = createLocaleLookup({
12
+ lang,
13
+ locale: Astro.currentLocale ?? locale,
14
+ defaultLang: starlightConfig.defaultLocale?.lang,
15
+ defaultLocale: starlightConfig.defaultLocale?.locale,
16
+ });
34
17
  ---
35
18
 
36
19
  <nav class="nav-bar">
@@ -42,10 +25,8 @@ export function getTranslation(
42
25
  !absoluteLinkRegex.test(nav.link) && Astro.currentLocale
43
26
  ? getRelativeLocaleUrl(Astro.currentLocale, nav.link)
44
27
  : nav.link;
45
- const label =
46
- typeof nav.label === 'string'
47
- ? nav.label
48
- : getTranslation(nav.label, nav.link, 'label');
28
+
29
+ const label = resolveNavLabel(nav.label, nav.translations, localeKeys);
49
30
 
50
31
  return (
51
32
  <a class:list={[{ active: currentPath === link }]} href={link} {...nav.attrs}>
@@ -1,12 +1,15 @@
1
1
  ---
2
+ import { Icon } from '@astrojs/starlight/components';
3
+ import SidebarRestorePoint from '@astrojs/starlight/components/SidebarRestorePoint.astro';
2
4
  import type { StarlightRouteData } from '@astrojs/starlight/route-data';
5
+ import { isSidebarGroupOpen } from '../../../core/sidebar';
3
6
 
4
7
  interface Props {
5
- sublist: StarlightRouteData['sidebar'];
6
- nested?: boolean;
8
+ sublist: StarlightRouteData['sidebar'];
9
+ nested?: boolean;
7
10
  }
8
11
 
9
- const { sublist } = Astro.props;
12
+ const { sublist, nested = false } = Astro.props;
10
13
  ---
11
14
 
12
15
  {
@@ -14,7 +17,7 @@ const { sublist } = Astro.props;
14
17
  entry.type === 'link' ? (
15
18
  <a
16
19
  href={entry.href}
17
- aria-current={entry.isCurrent && 'page'}
20
+ aria-current={entry.isCurrent ? 'page' : undefined}
18
21
  class:list={['entry-link', entry.attrs.class]}
19
22
  {...entry.attrs}
20
23
  >
@@ -23,6 +26,20 @@ const { sublist } = Astro.props;
23
26
  {entry.badge && <span class="entry-badge">{entry.badge.text}</span>}
24
27
  </span>
25
28
  </a>
29
+ ) : nested ? (
30
+ <details class="entry-group" open={isSidebarGroupOpen(entry)}>
31
+ <summary class="entry-group-summary">
32
+ <span class="entry-link-inner">
33
+ {entry.label}
34
+ {entry.badge && <span class="entry-badge">{entry.badge.text}</span>}
35
+ </span>
36
+ <Icon name="right-caret" class="entry-group-caret" size="1rem" />
37
+ </summary>
38
+ <SidebarRestorePoint />
39
+ <div class="container-group-link nested">
40
+ <Astro.self sublist={entry.entries} nested />
41
+ </div>
42
+ </details>
26
43
  ) : (
27
44
  <div class="container-sidebar-entry">
28
45
  <h4 class="entry-title">{entry.label}</h4>
@@ -62,6 +79,60 @@ const { sublist } = Astro.props;
62
79
  display: grid;
63
80
  }
64
81
 
82
+ /*
83
+ * Nested levels are indented with a guide line rather than plain padding, so that a reader can
84
+ * follow which group a link belongs to once the tree is more than one level deep.
85
+ */
86
+ .container-group-link.nested {
87
+ margin-left: calc(var(--spacing) * 2);
88
+ padding-left: calc(var(--spacing) * 2);
89
+ border-left: 1px solid var(--border);
90
+ }
91
+
92
+ .entry-group {
93
+ display: flex;
94
+ flex-direction: column;
95
+ gap: 2px;
96
+ }
97
+
98
+ .entry-group-summary {
99
+ display: flex;
100
+ align-items: center;
101
+ justify-content: space-between;
102
+ gap: calc(var(--spacing) * 1);
103
+ height: 1.875rem;
104
+ cursor: pointer;
105
+ list-style: none;
106
+ color: var(--foreground);
107
+ font-size: 0.8rem;
108
+ line-height: 1.125rem;
109
+ border-radius: calc(var(--radius) - 2px);
110
+ transition:
111
+ color 0.15s,
112
+ background-color 0.15s;
113
+ }
114
+
115
+ /* Safari still paints its own marker without this. */
116
+ .entry-group-summary::-webkit-details-marker {
117
+ display: none;
118
+ }
119
+
120
+ .entry-group-summary:hover {
121
+ background-color: var(--secondary);
122
+ color: var(--secondary-foreground);
123
+ }
124
+
125
+ .entry-group-caret {
126
+ flex-shrink: 0;
127
+ margin-right: calc(var(--spacing) * 2);
128
+ color: var(--muted-foreground);
129
+ transition: transform 0.15s;
130
+ }
131
+
132
+ .entry-group[open] > .entry-group-summary .entry-group-caret {
133
+ transform: rotate(90deg);
134
+ }
135
+
65
136
  .entry-link {
66
137
  color: var(--foreground);
67
138
  font-weight: 400;
@@ -2,13 +2,13 @@
2
2
  import type { MarkdownHeading } from 'astro';
3
3
 
4
4
  interface TocItem extends MarkdownHeading {
5
- children: TocItem[];
5
+ children: TocItem[];
6
6
  }
7
7
 
8
8
  interface Props {
9
- toc: TocItem[];
10
- depth?: number;
11
- isMobile?: boolean;
9
+ toc: TocItem[];
10
+ depth?: number;
11
+ isMobile?: boolean;
12
12
  }
13
13
 
14
14
  const { toc, depth = 0 } = Astro.props;
@@ -5,115 +5,113 @@
5
5
  import { PAGE_TITLE_ID } from '../../../../core/config/constants';
6
6
 
7
7
  export class StarlightTOC extends HTMLElement {
8
- private _current = this.querySelector<HTMLAnchorElement>('a[aria-current="true"]');
9
- private minH = Number.parseInt(this.dataset.minH || '2', 10);
10
- private maxH = Number.parseInt(this.dataset.maxH || '3', 10);
8
+ private _current = this.querySelector<HTMLAnchorElement>('a[aria-current="true"]');
9
+ private minH = Number.parseInt(this.dataset.minH || '2', 10);
10
+ private maxH = Number.parseInt(this.dataset.maxH || '3', 10);
11
11
 
12
- protected set current(link: HTMLAnchorElement) {
13
- if (link === this._current) return;
14
- if (this._current) this._current.removeAttribute('aria-current');
15
- link.setAttribute('aria-current', 'true');
16
- this._current = link;
17
- }
12
+ protected set current(link: HTMLAnchorElement) {
13
+ if (link === this._current) return;
14
+ if (this._current) this._current.removeAttribute('aria-current');
15
+ link.setAttribute('aria-current', 'true');
16
+ this._current = link;
17
+ }
18
18
 
19
- private onIdle = (cb: IdleRequestCallback) =>
20
- (window.requestIdleCallback || ((cb) => setTimeout(cb, 1)))(cb);
19
+ private onIdle = (cb: IdleRequestCallback) =>
20
+ (window.requestIdleCallback || ((cb) => setTimeout(cb, 1)))(cb);
21
21
 
22
- constructor() {
23
- super();
24
- this.onIdle(() => this.init());
25
- }
22
+ constructor() {
23
+ super();
24
+ this.onIdle(() => this.init());
25
+ }
26
26
 
27
- private init = (): void => {
28
- /** All the links in the table of contents. */
29
- const links = [...this.querySelectorAll('a')];
27
+ private init = (): void => {
28
+ /** All the links in the table of contents. */
29
+ const links = [...this.querySelectorAll('a')];
30
30
 
31
- /** Test if an element is a table-of-contents heading. */
32
- const isHeading = (el: Element): el is HTMLHeadingElement => {
33
- if (el instanceof HTMLHeadingElement) {
34
- // Special case for page title h1
35
- if (el.id === PAGE_TITLE_ID) return true;
36
- // Check the heading level is within the user-configured limits for the ToC
37
- const level = el.tagName[1];
38
- if (level) {
39
- const int = Number.parseInt(level, 10);
40
- if (int >= this.minH && int <= this.maxH) return true;
41
- }
42
- }
43
- return false;
44
- };
45
-
46
- /** Walk up the DOM to find the nearest heading. */
47
- const getElementHeading = (el: Element | null): HTMLHeadingElement | null => {
48
- if (!el) return null;
49
- const origin = el;
50
- while (el) {
51
- if (isHeading(el)) return el;
52
- // Assign the previous sibling’s last, most deeply nested child to el.
53
- el = el.previousElementSibling;
54
- while (el?.lastElementChild) {
55
- el = el.lastElementChild;
56
- }
57
- // Look for headings amongst siblings.
58
- const h = getElementHeading(el);
59
- if (h) return h;
60
- }
61
- // Walk back up the parent.
62
- return getElementHeading(origin.parentElement);
63
- };
31
+ /** Test if an element is a table-of-contents heading. */
32
+ const isHeading = (el: Element): el is HTMLHeadingElement => {
33
+ if (el instanceof HTMLHeadingElement) {
34
+ // Special case for page title h1
35
+ if (el.id === PAGE_TITLE_ID) return true;
36
+ // Check the heading level is within the user-configured limits for the ToC
37
+ const level = el.tagName[1];
38
+ if (level) {
39
+ const int = Number.parseInt(level, 10);
40
+ if (int >= this.minH && int <= this.maxH) return true;
41
+ }
42
+ }
43
+ return false;
44
+ };
64
45
 
65
- /** Handle intersections and set the current link to the heading for the current intersection. */
66
- const setCurrent: IntersectionObserverCallback = (entries) => {
67
- for (const { isIntersecting, target } of entries) {
68
- if (!isIntersecting) continue;
69
- const heading = getElementHeading(target);
70
- if (!heading) continue;
71
- const link = links.find(
72
- (link) => link.hash === `#${encodeURIComponent(heading.id)}`
73
- );
74
- if (link) {
75
- this.current = link;
76
- break;
77
- }
78
- }
79
- };
46
+ /** Walk up the DOM to find the nearest heading. */
47
+ const getElementHeading = (el: Element | null): HTMLHeadingElement | null => {
48
+ if (!el) return null;
49
+ const origin = el;
50
+ while (el) {
51
+ if (isHeading(el)) return el;
52
+ // Assign the previous sibling’s last, most deeply nested child to el.
53
+ el = el.previousElementSibling;
54
+ while (el?.lastElementChild) {
55
+ el = el.lastElementChild;
56
+ }
57
+ // Look for headings amongst siblings.
58
+ const h = getElementHeading(el);
59
+ if (h) return h;
60
+ }
61
+ // Walk back up the parent.
62
+ return getElementHeading(origin.parentElement);
63
+ };
80
64
 
81
- // Observe elements with an `id` (most likely headings) and their siblings.
82
- // Also observe direct children of `.content` to include elements before
83
- // the first heading.
84
- const toObserve = document.querySelectorAll('main [id], main [id] ~ *, main .content > *');
65
+ /** Handle intersections and set the current link to the heading for the current intersection. */
66
+ const setCurrent: IntersectionObserverCallback = (entries) => {
67
+ for (const { isIntersecting, target } of entries) {
68
+ if (!isIntersecting) continue;
69
+ const heading = getElementHeading(target);
70
+ if (!heading) continue;
71
+ const link = links.find((link) => link.hash === `#${encodeURIComponent(heading.id)}`);
72
+ if (link) {
73
+ this.current = link;
74
+ break;
75
+ }
76
+ }
77
+ };
85
78
 
86
- let observer: IntersectionObserver | undefined;
87
- const observe = () => {
88
- if (observer) return;
89
- observer = new IntersectionObserver(setCurrent, { rootMargin: this.getRootMargin() });
90
- toObserve.forEach((h) => observer!.observe(h));
91
- };
92
- observe();
79
+ // Observe elements with an `id` (most likely headings) and their siblings.
80
+ // Also observe direct children of `.content` to include elements before
81
+ // the first heading.
82
+ const toObserve = document.querySelectorAll('main [id], main [id] ~ *, main .content > *');
93
83
 
94
- let timeout: NodeJS.Timeout;
95
- window.addEventListener('resize', () => {
96
- // Disable intersection observer while window is resizing.
97
- if (observer) {
98
- observer.disconnect();
99
- observer = undefined;
100
- }
101
- clearTimeout(timeout);
102
- timeout = setTimeout(() => this.onIdle(observe), 200);
103
- });
84
+ let observer: IntersectionObserver | undefined;
85
+ const observe = () => {
86
+ if (observer) return;
87
+ observer = new IntersectionObserver(setCurrent, { rootMargin: this.getRootMargin() });
88
+ for (const h of toObserve) observer.observe(h);
104
89
  };
90
+ observe();
91
+
92
+ let timeout: NodeJS.Timeout;
93
+ window.addEventListener('resize', () => {
94
+ // Disable intersection observer while window is resizing.
95
+ if (observer) {
96
+ observer.disconnect();
97
+ observer = undefined;
98
+ }
99
+ clearTimeout(timeout);
100
+ timeout = setTimeout(() => this.onIdle(observe), 200);
101
+ });
102
+ };
105
103
 
106
- private getRootMargin(): `-${number}px 0% ${number}px` {
107
- const navBarHeight = document.querySelector('header')?.getBoundingClientRect().height || 0;
108
- // `<summary>` only exists in mobile ToC, so will fall back to 0 in large viewport component.
109
- const mobileTocHeight = this.querySelector('summary')?.getBoundingClientRect().height || 0;
110
- /** Start intersections at nav height + 2rem padding. */
111
- const top = navBarHeight + mobileTocHeight + 32;
112
- /** End intersections `53px` later. This is slightly more than the maximum `margin-top` in Markdown content. */
113
- const bottom = top + 53;
114
- const height = document.documentElement.clientHeight;
115
- return `-${top}px 0% ${bottom - height}px`;
116
- }
104
+ private getRootMargin(): `-${number}px 0% ${number}px` {
105
+ const navBarHeight = document.querySelector('header')?.getBoundingClientRect().height || 0;
106
+ // `<summary>` only exists in mobile ToC, so will fall back to 0 in large viewport component.
107
+ const mobileTocHeight = this.querySelector('summary')?.getBoundingClientRect().height || 0;
108
+ /** Start intersections at nav height + 2rem padding. */
109
+ const top = navBarHeight + mobileTocHeight + 32;
110
+ /** End intersections `53px` later. This is slightly more than the maximum `margin-top` in Markdown content. */
111
+ const bottom = top + 53;
112
+ const height = document.documentElement.clientHeight;
113
+ return `-${top}px 0% ${bottom - height}px`;
114
+ }
117
115
  }
118
116
 
119
117
  customElements.define('starlight-toc', StarlightTOC);
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Starlight validates frontmatter with `z.object`, which strips keys it does not know about. When
3
+ * `docsSchema()` is called without `extend: ExtendDocsSchema`, the theme's own `hero` fields are
4
+ * therefore dropped without any error, and splash pages silently fall back to the default layout.
5
+ *
6
+ * Detecting that from the parsed frontmatter is unreliable: Starlight builds some entries in code
7
+ * rather than through the schema — its fallback 404 route hardcodes `hero` — so those look stripped
8
+ * even on a correctly configured site. Instead, `@grove-dev/starlight/schema` records that it was
9
+ * imported, which answers the actual question: did the user wire the extension up at all?
10
+ */
11
+ const LOADED = Symbol.for('@grove-dev/starlight.docs-schema-loaded');
12
+
13
+ /** Called by `@grove-dev/starlight/schema` on import. */
14
+ export function markDocsSchemaLoaded(): void {
15
+ (globalThis as Record<symbol, unknown>)[LOADED] = true;
16
+ }
17
+
18
+ export function isDocsSchemaExtended(): boolean {
19
+ return (globalThis as Record<symbol, unknown>)[LOADED] === true;
20
+ }
21
+
22
+ let warned = false;
23
+
24
+ /**
25
+ * Emits {@link missingDocsSchemaWarning} the first time a page with a hero is rendered on a site
26
+ * that never imported the schema. Returns whether it warned; later calls are no-ops, so a site with
27
+ * several splash pages does not repeat the same advice once per page.
28
+ */
29
+ export function warnAboutMissingDocsSchemaOnce(
30
+ hero: unknown,
31
+ warn: (message: string) => void = console.warn,
32
+ ): boolean {
33
+ if (warned || hero == null || isDocsSchemaExtended()) return false;
34
+
35
+ warned = true;
36
+ warn(missingDocsSchemaWarning());
37
+ return true;
38
+ }
39
+
40
+ /** Test seam: clears the once-per-process guard used by {@link warnAboutMissingDocsSchemaOnce}. */
41
+ export function resetDocsSchemaWarning(): void {
42
+ warned = false;
43
+ }
44
+
45
+ /** The advice printed when the schema extension is missing. */
46
+ export function missingDocsSchemaWarning(): string {
47
+ return [
48
+ '[@grove-dev/starlight] This page sets `hero` frontmatter, but the docs schema is not extended,',
49
+ "so Starlight is dropping the theme's hero fields. `hero.layout`, `hero.announcement` and the",
50
+ 'extra `hero.actions[].variant` values have no effect until you extend it:',
51
+ '',
52
+ " import { ExtendDocsSchema } from '@grove-dev/starlight/schema';",
53
+ '',
54
+ ' schema: docsSchema({ extend: ExtendDocsSchema }),',
55
+ '',
56
+ 'See https://withgrove.dev/reference/plugin-api/#frontmatter-extension',
57
+ ].join('\n');
58
+ }
@@ -2,62 +2,60 @@ import type { StarlightExpressiveCodeOptions } from '@astrojs/starlight/expressi
2
2
  import type { StarlightUserConfig } from '@astrojs/starlight/types';
3
3
 
4
4
  const createInlineSvgUrl = (svgContents: string): string => {
5
- const inlineSvg = svgContents.replace(
6
- /^(\s*<svg)\s+([^>]+)\s*(\/?>)/,
7
- (_match, tagStart: string, attributes: string, tagEnd: string) => {
8
- const sanitizedAttributes = attributes.replaceAll(
9
- /(?:width|height)\s*=\s*(?:(["'])[^"']*\1|\d+)\s*/g,
10
- ''
11
- );
5
+ const inlineSvg = svgContents.replace(
6
+ /^(\s*<svg)\s+([^>]+)\s*(\/?>)/,
7
+ (_match, tagStart: string, attributes: string, tagEnd: string) => {
8
+ const sanitizedAttributes = attributes.replaceAll(
9
+ /(?:width|height)\s*=\s*(?:(["'])[^"']*\1|\d+)\s*/g,
10
+ '',
11
+ );
12
12
 
13
- return `${tagStart} ${sanitizedAttributes.trim()}${tagEnd}`;
14
- }
15
- );
13
+ return `${tagStart} ${sanitizedAttributes.trim()}${tagEnd}`;
14
+ },
15
+ );
16
16
 
17
- return `url("data:image/svg+xml,${encodeURIComponent(inlineSvg)}")`;
17
+ return `url("data:image/svg+xml,${encodeURIComponent(inlineSvg)}")`;
18
18
  };
19
19
 
20
20
  export const expressiveCode = (
21
- config: StarlightUserConfig
21
+ config: StarlightUserConfig,
22
22
  ): boolean | StarlightExpressiveCodeOptions => {
23
- const userExpressiveCodeConfig =
24
- config.expressiveCode === false || config.expressiveCode === true
25
- ? {}
26
- : config.expressiveCode;
23
+ const userExpressiveCodeConfig =
24
+ config.expressiveCode === false || config.expressiveCode === true ? {} : config.expressiveCode;
27
25
 
28
- return config.expressiveCode === false
29
- ? false
30
- : {
31
- themes: ['github-dark-default', 'github-light-default'],
32
- ...userExpressiveCodeConfig,
33
- styleOverrides: {
34
- codeBackground: 'var(--code-background)',
35
- borderWidth: '0px',
36
- borderRadius: 'calc(var(--radius) + 4px)',
37
- gutterBorderWidth: '0px',
38
- ...userExpressiveCodeConfig?.styleOverrides,
39
- frames: {
40
- editorBackground: 'var(--code-background)',
41
- editorActiveTabBackground: 'var(--gray-5)',
42
- editorActiveTabForeground: 'var(--foreground)',
43
- editorTabBarBackground: 'var(--gray-6)',
44
- editorTabBarBorderColor: 'var(--border)',
45
- editorTabBarBorderBottomColor: 'var(--border)',
46
- terminalBackground: 'var(--code-background)',
47
- terminalTitlebarBackground: 'var(--gray-6)',
48
- terminalTitlebarBorderBottomColor: 'var(--border)',
49
- terminalTitlebarForeground: 'var(--muted-foreground)',
50
- shadowColor: 'transparent',
51
- copyIcon: createInlineSvgUrl(
52
- `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-clipboard"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"></rect><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path></svg>`
53
- ),
54
- ...userExpressiveCodeConfig?.styleOverrides?.frames,
55
- },
56
- textMarkers: {
57
- markBackground: 'var(--mark-background)',
58
- markBorderColor: 'var(--border)',
59
- ...userExpressiveCodeConfig?.styleOverrides?.textMarkers,
60
- },
61
- },
62
- };
26
+ return config.expressiveCode === false
27
+ ? false
28
+ : {
29
+ themes: ['github-dark-default', 'github-light-default'],
30
+ ...userExpressiveCodeConfig,
31
+ styleOverrides: {
32
+ codeBackground: 'var(--code-background)',
33
+ borderWidth: '0px',
34
+ borderRadius: 'calc(var(--radius) + 4px)',
35
+ gutterBorderWidth: '0px',
36
+ ...userExpressiveCodeConfig?.styleOverrides,
37
+ frames: {
38
+ editorBackground: 'var(--code-background)',
39
+ editorActiveTabBackground: 'var(--gray-5)',
40
+ editorActiveTabForeground: 'var(--foreground)',
41
+ editorTabBarBackground: 'var(--gray-6)',
42
+ editorTabBarBorderColor: 'var(--border)',
43
+ editorTabBarBorderBottomColor: 'var(--border)',
44
+ terminalBackground: 'var(--code-background)',
45
+ terminalTitlebarBackground: 'var(--gray-6)',
46
+ terminalTitlebarBorderBottomColor: 'var(--border)',
47
+ terminalTitlebarForeground: 'var(--muted-foreground)',
48
+ shadowColor: 'transparent',
49
+ copyIcon: createInlineSvgUrl(
50
+ `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-clipboard"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"></rect><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path></svg>`,
51
+ ),
52
+ ...userExpressiveCodeConfig?.styleOverrides?.frames,
53
+ },
54
+ textMarkers: {
55
+ markBackground: 'var(--mark-background)',
56
+ markBorderColor: 'var(--border)',
57
+ ...userExpressiveCodeConfig?.styleOverrides?.textMarkers,
58
+ },
59
+ },
60
+ };
63
61
  };