radiant-docs 0.1.47 → 0.1.49

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.
@@ -6,6 +6,12 @@ import { oas } from "@stoplight/spectral-rulesets";
6
6
  import { compile } from "@mdx-js/mdx";
7
7
  import yaml from "yaml";
8
8
  import { docsSchema } from "./frontmatter-schema";
9
+ import {
10
+ DEFAULT_SHIKI_DARK_THEME,
11
+ DEFAULT_SHIKI_LIGHT_THEME,
12
+ SHIKI_BUNDLED_THEME_NAMES,
13
+ isBundledShikiThemeName,
14
+ } from "./code/shiki-theme-config";
9
15
 
10
16
  // --- Configuration Constants ---
11
17
  const CWD = process.cwd();
@@ -226,10 +232,20 @@ export type CardTheme = {
226
232
  cover?: CardCoverTheme;
227
233
  button?: CardButtonTheme;
228
234
  };
235
+ export type CodeSyntaxThemeConfig =
236
+ | string
237
+ | {
238
+ light?: string;
239
+ dark?: string;
240
+ };
241
+ export type CodeTheme = {
242
+ syntaxTheme?: CodeSyntaxThemeConfig;
243
+ };
229
244
  export type DocsTheme = {
230
245
  baseColor?: BaseColorOption | BaseColorByMode;
231
246
  themeColor?: string | ThemeColorByMode;
232
247
  card?: CardTheme;
248
+ code?: CodeTheme;
233
249
  };
234
250
  export type AssistantIcon = {
235
251
  src?: string;
@@ -1718,6 +1734,127 @@ function validateTheme(theme: DocsConfig["theme"]): void {
1718
1734
  }
1719
1735
  }
1720
1736
 
1737
+ const normalizeShikiThemeName = (
1738
+ value: unknown,
1739
+ currentPath: Path,
1740
+ label: string,
1741
+ ): string => {
1742
+ checkType(value, "string", currentPath, label);
1743
+ if (typeof value !== "string") {
1744
+ throwConfigError(`${label} must be a string.`, currentPath);
1745
+ }
1746
+
1747
+ const normalizedThemeName = value.trim().toLowerCase();
1748
+ if (normalizedThemeName.length === 0) {
1749
+ throwConfigError(`${label} cannot be empty.`, currentPath);
1750
+ }
1751
+
1752
+ if (!isBundledShikiThemeName(normalizedThemeName)) {
1753
+ throwConfigError(
1754
+ `${label} must be a bundled Shiki theme name. Supported themes include: ${SHIKI_BUNDLED_THEME_NAMES.join(", ")}.`,
1755
+ currentPath,
1756
+ );
1757
+ }
1758
+
1759
+ return normalizedThemeName;
1760
+ };
1761
+
1762
+ if (theme.code !== undefined) {
1763
+ checkType(theme.code, "object", ["theme", "code"], "Theme code");
1764
+ if (
1765
+ typeof theme.code !== "object" ||
1766
+ theme.code === null ||
1767
+ Array.isArray(theme.code)
1768
+ ) {
1769
+ throwConfigError("Theme code must be an object.", ["theme", "code"]);
1770
+ }
1771
+
1772
+ const codeTheme = theme.code as CodeTheme & Record<string, unknown>;
1773
+ const allowedCodeKeys = new Set(["syntaxTheme"]);
1774
+ for (const key of Object.keys(codeTheme)) {
1775
+ if (!allowedCodeKeys.has(key)) {
1776
+ throwConfigError(
1777
+ "Theme code configuration only supports 'syntaxTheme'.",
1778
+ ["theme", "code", key],
1779
+ );
1780
+ }
1781
+ }
1782
+
1783
+ if (codeTheme.syntaxTheme !== undefined) {
1784
+ if (typeof codeTheme.syntaxTheme === "string") {
1785
+ const themeName = normalizeShikiThemeName(
1786
+ codeTheme.syntaxTheme,
1787
+ ["theme", "code", "syntaxTheme"],
1788
+ "Theme code syntax theme",
1789
+ );
1790
+ codeTheme.syntaxTheme = {
1791
+ light: themeName,
1792
+ dark: themeName,
1793
+ };
1794
+ } else {
1795
+ checkType(
1796
+ codeTheme.syntaxTheme,
1797
+ "object",
1798
+ ["theme", "code", "syntaxTheme"],
1799
+ "Theme code syntax theme",
1800
+ );
1801
+ if (
1802
+ typeof codeTheme.syntaxTheme !== "object" ||
1803
+ codeTheme.syntaxTheme === null ||
1804
+ Array.isArray(codeTheme.syntaxTheme)
1805
+ ) {
1806
+ throwConfigError(
1807
+ "Theme code syntax theme must be a string or an object with light/dark values.",
1808
+ ["theme", "code", "syntaxTheme"],
1809
+ );
1810
+ }
1811
+
1812
+ const syntaxThemeByMode = codeTheme.syntaxTheme as Record<
1813
+ string,
1814
+ unknown
1815
+ >;
1816
+ const allowedSyntaxThemeKeys = new Set(["light", "dark"]);
1817
+ for (const key of Object.keys(syntaxThemeByMode)) {
1818
+ if (!allowedSyntaxThemeKeys.has(key)) {
1819
+ throwConfigError(
1820
+ "Theme code syntax theme object only supports 'light' and 'dark'.",
1821
+ ["theme", "code", "syntaxTheme", key],
1822
+ );
1823
+ }
1824
+ }
1825
+
1826
+ const light =
1827
+ syntaxThemeByMode.light !== undefined
1828
+ ? normalizeShikiThemeName(
1829
+ syntaxThemeByMode.light,
1830
+ ["theme", "code", "syntaxTheme", "light"],
1831
+ "Theme code syntax theme light",
1832
+ )
1833
+ : undefined;
1834
+ const dark =
1835
+ syntaxThemeByMode.dark !== undefined
1836
+ ? normalizeShikiThemeName(
1837
+ syntaxThemeByMode.dark,
1838
+ ["theme", "code", "syntaxTheme", "dark"],
1839
+ "Theme code syntax theme dark",
1840
+ )
1841
+ : undefined;
1842
+
1843
+ if (!light && !dark) {
1844
+ throwConfigError(
1845
+ "Theme code syntax theme object must include 'light', 'dark', or both.",
1846
+ ["theme", "code", "syntaxTheme"],
1847
+ );
1848
+ }
1849
+
1850
+ codeTheme.syntaxTheme = {
1851
+ light: light ?? DEFAULT_SHIKI_LIGHT_THEME,
1852
+ dark: dark ?? DEFAULT_SHIKI_DARK_THEME,
1853
+ };
1854
+ }
1855
+ }
1856
+ }
1857
+
1721
1858
  if (theme.card !== undefined) {
1722
1859
  checkType(theme.card, "object", ["theme", "card"], "Theme card");
1723
1860
  if (
@@ -55,9 +55,20 @@
55
55
  --border-light: var(--color-neutral-100);
56
56
  --input: oklch(0.922 0 0);
57
57
  --ring: oklch(0.708 0 0);
58
- --rd-code-surface: #ffffff;
58
+ --rd-panel-transition-duration-ms: 400;
59
+ --rd-panel-transition-duration: 400ms;
60
+ --rd-panel-transition-easing: cubic-bezier(0.22, 1, 0.36, 1);
61
+ --rd-code-surface: color-mix(
62
+ in srgb,
63
+ var(--color-neutral-100) 60%,
64
+ var(--background) 40%
65
+ );
59
66
  --rd-code-tab-edge-bg: var(--rd-code-surface);
60
- --rd-code-tab-edge-border: var(--color-neutral-200);
67
+ --rd-code-tab-edge-border: color-mix(
68
+ in oklab,
69
+ var(--color-neutral-900) 4%,
70
+ var(--color-white) 96%
71
+ );
61
72
  }
62
73
 
63
74
  /* 3. Dark Mode */
@@ -86,7 +97,11 @@
86
97
  var(--color-neutral-900) 45%
87
98
  );
88
99
  --rd-code-tab-edge-bg: var(--rd-code-surface);
89
- --rd-code-tab-edge-border: var(--color-neutral-800);
100
+ --rd-code-tab-edge-border: color-mix(
101
+ in oklab,
102
+ white 4%,
103
+ var(--rd-code-tab-edge-bg) 96%
104
+ );
90
105
  }
91
106
 
92
107
  @variant dark (&:where(.dark, .dark *));
@@ -197,6 +212,16 @@
197
212
  }
198
213
 
199
214
  @layer base {
215
+ .prose-rules > :first-child {
216
+ margin-block-start: 0 !important;
217
+ margin-top: 0 !important;
218
+ }
219
+
220
+ .prose-rules > :last-child {
221
+ margin-block-end: 0 !important;
222
+ margin-bottom: 0 !important;
223
+ }
224
+
200
225
  .prose-rules > .rd-prose-block:first-child,
201
226
  .prose-rules > .react-renderer:first-child > .rd-prose-block,
202
227
  .prose-rules > [data-node-view-content-react]:first-child > :first-child,
@@ -326,6 +351,26 @@
326
351
  background-color: var(--rd-token-bg, transparent);
327
352
  }
328
353
 
354
+ [data-rd-code-block-root] {
355
+ --rd-code-line-highlight-bg: color-mix(
356
+ in srgb,
357
+ var(--rd-code-line-highlight-theme-bg-light) 90%,
358
+ var(--color-neutral-400) 10%
359
+ );
360
+ }
361
+
362
+ .dark [data-rd-code-block-root] {
363
+ --rd-code-line-highlight-bg: color-mix(
364
+ in srgb,
365
+ var(--rd-code-line-highlight-theme-bg-dark) 80%,
366
+ var(--color-neutral-700) 20%
367
+ );
368
+ }
369
+
370
+ [data-rd-code-line-highlighted="true"] {
371
+ background-color: var(--rd-code-line-highlight-bg);
372
+ }
373
+
329
374
  .dark [data-rd-code-theme] [data-rd-token] {
330
375
  color: var(--rd-token-color-dark, var(--rd-token-color, currentColor));
331
376
  background-color: var(--rd-token-bg-dark, var(--rd-token-bg, transparent));