@specglass/theme-default 0.0.2

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 (141) hide show
  1. package/dist/__tests__/code-tabs.test.d.ts +2 -0
  2. package/dist/__tests__/code-tabs.test.d.ts.map +1 -0
  3. package/dist/__tests__/code-tabs.test.js +219 -0
  4. package/dist/__tests__/code-tabs.test.js.map +1 -0
  5. package/dist/__tests__/copy-button.test.d.ts +2 -0
  6. package/dist/__tests__/copy-button.test.d.ts.map +1 -0
  7. package/dist/__tests__/copy-button.test.js +116 -0
  8. package/dist/__tests__/copy-button.test.js.map +1 -0
  9. package/dist/__tests__/search-palette.test.d.ts +2 -0
  10. package/dist/__tests__/search-palette.test.d.ts.map +1 -0
  11. package/dist/__tests__/search-palette.test.js +71 -0
  12. package/dist/__tests__/search-palette.test.js.map +1 -0
  13. package/dist/__tests__/shiki.test.d.ts +2 -0
  14. package/dist/__tests__/shiki.test.d.ts.map +1 -0
  15. package/dist/__tests__/shiki.test.js +37 -0
  16. package/dist/__tests__/shiki.test.js.map +1 -0
  17. package/dist/__tests__/theme-css.test.d.ts +2 -0
  18. package/dist/__tests__/theme-css.test.d.ts.map +1 -0
  19. package/dist/__tests__/theme-css.test.js +124 -0
  20. package/dist/__tests__/theme-css.test.js.map +1 -0
  21. package/dist/__tests__/theme-helpers.test.d.ts +2 -0
  22. package/dist/__tests__/theme-helpers.test.d.ts.map +1 -0
  23. package/dist/__tests__/theme-helpers.test.js +81 -0
  24. package/dist/__tests__/theme-helpers.test.js.map +1 -0
  25. package/dist/index.d.ts +63 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +13 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/islands/CodeTabs.d.ts +21 -0
  30. package/dist/islands/CodeTabs.d.ts.map +1 -0
  31. package/dist/islands/CodeTabs.js +125 -0
  32. package/dist/islands/CodeTabs.js.map +1 -0
  33. package/dist/islands/CopyButton.d.ts +16 -0
  34. package/dist/islands/CopyButton.d.ts.map +1 -0
  35. package/dist/islands/CopyButton.js +54 -0
  36. package/dist/islands/CopyButton.js.map +1 -0
  37. package/dist/islands/SearchPalette.d.ts +2 -0
  38. package/dist/islands/SearchPalette.d.ts.map +1 -0
  39. package/dist/islands/SearchPalette.js +109 -0
  40. package/dist/islands/SearchPalette.js.map +1 -0
  41. package/dist/islands/SearchResults.d.ts +2 -0
  42. package/dist/islands/SearchResults.d.ts.map +1 -0
  43. package/dist/islands/SearchResults.js +130 -0
  44. package/dist/islands/SearchResults.js.map +1 -0
  45. package/dist/islands/ThemeToggle.d.ts +12 -0
  46. package/dist/islands/ThemeToggle.d.ts.map +1 -0
  47. package/dist/islands/ThemeToggle.js +43 -0
  48. package/dist/islands/ThemeToggle.js.map +1 -0
  49. package/dist/layouts/DocPage.test.d.ts +2 -0
  50. package/dist/layouts/DocPage.test.d.ts.map +1 -0
  51. package/dist/layouts/DocPage.test.js +165 -0
  52. package/dist/layouts/DocPage.test.js.map +1 -0
  53. package/dist/lib/utils.d.ts +10 -0
  54. package/dist/lib/utils.d.ts.map +1 -0
  55. package/dist/lib/utils.js +13 -0
  56. package/dist/lib/utils.js.map +1 -0
  57. package/dist/scripts/code-block-enhancer.d.ts +16 -0
  58. package/dist/scripts/code-block-enhancer.d.ts.map +1 -0
  59. package/dist/scripts/code-block-enhancer.js +55 -0
  60. package/dist/scripts/code-block-enhancer.js.map +1 -0
  61. package/dist/ui/command.d.ts +87 -0
  62. package/dist/ui/command.d.ts.map +1 -0
  63. package/dist/ui/command.js +28 -0
  64. package/dist/ui/command.js.map +1 -0
  65. package/dist/ui/dialog.d.ts +20 -0
  66. package/dist/ui/dialog.d.ts.map +1 -0
  67. package/dist/ui/dialog.js +22 -0
  68. package/dist/ui/dialog.js.map +1 -0
  69. package/dist/utils/parse-highlight-range.d.ts +12 -0
  70. package/dist/utils/parse-highlight-range.d.ts.map +1 -0
  71. package/dist/utils/parse-highlight-range.js +40 -0
  72. package/dist/utils/parse-highlight-range.js.map +1 -0
  73. package/dist/utils/parse-highlight-range.test.d.ts +2 -0
  74. package/dist/utils/parse-highlight-range.test.d.ts.map +1 -0
  75. package/dist/utils/parse-highlight-range.test.js +32 -0
  76. package/dist/utils/parse-highlight-range.test.js.map +1 -0
  77. package/dist/utils/schema-renderer.d.ts +38 -0
  78. package/dist/utils/schema-renderer.d.ts.map +1 -0
  79. package/dist/utils/schema-renderer.js +115 -0
  80. package/dist/utils/schema-renderer.js.map +1 -0
  81. package/dist/utils/schema-renderer.test.d.ts +2 -0
  82. package/dist/utils/schema-renderer.test.d.ts.map +1 -0
  83. package/dist/utils/schema-renderer.test.js +219 -0
  84. package/dist/utils/schema-renderer.test.js.map +1 -0
  85. package/dist/utils/shiki.d.ts +20 -0
  86. package/dist/utils/shiki.d.ts.map +1 -0
  87. package/dist/utils/shiki.js +84 -0
  88. package/dist/utils/shiki.js.map +1 -0
  89. package/dist/utils/sidebar-helpers.d.ts +10 -0
  90. package/dist/utils/sidebar-helpers.d.ts.map +1 -0
  91. package/dist/utils/sidebar-helpers.js +14 -0
  92. package/dist/utils/sidebar-helpers.js.map +1 -0
  93. package/dist/utils/theme-css.d.ts +21 -0
  94. package/dist/utils/theme-css.d.ts.map +1 -0
  95. package/dist/utils/theme-css.js +77 -0
  96. package/dist/utils/theme-css.js.map +1 -0
  97. package/dist/utils/theme-helpers.d.ts +28 -0
  98. package/dist/utils/theme-helpers.d.ts.map +1 -0
  99. package/dist/utils/theme-helpers.js +55 -0
  100. package/dist/utils/theme-helpers.js.map +1 -0
  101. package/dist/utils/toc-helpers.d.ts +12 -0
  102. package/dist/utils/toc-helpers.d.ts.map +1 -0
  103. package/dist/utils/toc-helpers.js +9 -0
  104. package/dist/utils/toc-helpers.js.map +1 -0
  105. package/package.json +68 -0
  106. package/src/components/ApiAuth.astro +116 -0
  107. package/src/components/ApiEndpoint.astro +75 -0
  108. package/src/components/ApiNavigation.astro +110 -0
  109. package/src/components/ApiParameters.astro +204 -0
  110. package/src/components/ApiResponse.astro +144 -0
  111. package/src/components/Callout.astro +54 -0
  112. package/src/components/Card.astro +46 -0
  113. package/src/components/CodeBlock.astro +142 -0
  114. package/src/components/CodeBlockGroup.astro +196 -0
  115. package/src/components/CodeTabs.astro +53 -0
  116. package/src/components/Footer.astro +41 -0
  117. package/src/components/Header.astro +80 -0
  118. package/src/components/Sidebar.astro +117 -0
  119. package/src/components/TabItem.astro +24 -0
  120. package/src/components/TableOfContents.astro +111 -0
  121. package/src/components/Tabs.astro +185 -0
  122. package/src/islands/CodeTabs.tsx +212 -0
  123. package/src/islands/CopyButton.tsx +101 -0
  124. package/src/islands/SearchPalette.tsx +307 -0
  125. package/src/islands/SearchResults.tsx +301 -0
  126. package/src/islands/ThemeToggle.tsx +107 -0
  127. package/src/layouts/ApiReferencePage.astro +239 -0
  128. package/src/layouts/DocPage.astro +199 -0
  129. package/src/layouts/DocPage.test.ts +183 -0
  130. package/src/layouts/LandingPage.astro +143 -0
  131. package/src/lib/utils.ts +13 -0
  132. package/src/styles/global.css +241 -0
  133. package/src/utils/parse-highlight-range.test.ts +40 -0
  134. package/src/utils/parse-highlight-range.ts +41 -0
  135. package/src/utils/schema-renderer.test.ts +269 -0
  136. package/src/utils/schema-renderer.ts +152 -0
  137. package/src/utils/shiki.ts +99 -0
  138. package/src/utils/sidebar-helpers.ts +24 -0
  139. package/src/utils/theme-css.ts +101 -0
  140. package/src/utils/theme-helpers.ts +59 -0
  141. package/src/utils/toc-helpers.ts +11 -0
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Shared Shiki highlighter utility for CodeBlock components.
3
+ *
4
+ * Uses a module-level cached highlighter instance so we don't
5
+ * pay the startup cost on every render. Common languages are
6
+ * pre-loaded at initialization; additional languages are loaded
7
+ * on demand. Output uses `defaultColor: false` which emits CSS
8
+ * variables (--shiki-light, --shiki-dark) instead of hardcoded
9
+ * inline colors — matching the fenced code block rendering path.
10
+ */
11
+ export interface HighlightOptions {
12
+ /** Line numbers to highlight (1-indexed) */
13
+ highlightLines?: number[];
14
+ }
15
+ /**
16
+ * Syntax-highlight `code` using Shiki dual-theme (github-light / github-dark).
17
+ * Returns raw HTML string with CSS variable tokens — ready for `set:html`.
18
+ */
19
+ export declare function highlight(code: string, lang: string, options?: HighlightOptions): Promise<string>;
20
+ //# sourceMappingURL=shiki.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shiki.d.ts","sourceRoot":"","sources":["../../src/utils/shiki.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,MAAM,WAAW,gBAAgB;IAC/B,4CAA4C;IAC5C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAiBD;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,MAAM,CAAC,CAuDjB"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Shared Shiki highlighter utility for CodeBlock components.
3
+ *
4
+ * Uses a module-level cached highlighter instance so we don't
5
+ * pay the startup cost on every render. Common languages are
6
+ * pre-loaded at initialization; additional languages are loaded
7
+ * on demand. Output uses `defaultColor: false` which emits CSS
8
+ * variables (--shiki-light, --shiki-dark) instead of hardcoded
9
+ * inline colors — matching the fenced code block rendering path.
10
+ */
11
+ import { createHighlighter } from "shiki";
12
+ let highlighter = null;
13
+ /**
14
+ * Common language shorthand aliases that Shiki may not auto-resolve.
15
+ */
16
+ const LANG_ALIASES = {
17
+ ts: "typescript",
18
+ js: "javascript",
19
+ py: "python",
20
+ rb: "ruby",
21
+ sh: "bash",
22
+ shell: "bash",
23
+ yml: "yaml",
24
+ md: "markdown",
25
+ mdx: "markdown",
26
+ };
27
+ /**
28
+ * Syntax-highlight `code` using Shiki dual-theme (github-light / github-dark).
29
+ * Returns raw HTML string with CSS variable tokens — ready for `set:html`.
30
+ */
31
+ export async function highlight(code, lang, options) {
32
+ if (!highlighter) {
33
+ highlighter = await createHighlighter({
34
+ themes: ["github-light", "github-dark"],
35
+ langs: [
36
+ "typescript",
37
+ "javascript",
38
+ "python",
39
+ "bash",
40
+ "json",
41
+ "yaml",
42
+ "html",
43
+ "css",
44
+ "markdown",
45
+ "tsx",
46
+ "jsx",
47
+ "ruby",
48
+ "go",
49
+ "rust",
50
+ "sql",
51
+ "graphql",
52
+ "diff",
53
+ "text",
54
+ ],
55
+ });
56
+ }
57
+ // Normalize common aliases
58
+ lang = LANG_ALIASES[lang] ?? lang;
59
+ // Load language on demand if not already loaded
60
+ const loaded = highlighter.getLoadedLanguages();
61
+ if (!loaded.includes(lang)) {
62
+ try {
63
+ await highlighter.loadLanguage(lang);
64
+ }
65
+ catch {
66
+ // Unknown language — fall back to plaintext
67
+ lang = "text";
68
+ }
69
+ }
70
+ // Build decorations for line highlighting
71
+ const lines = code.split("\n");
72
+ const decorations = options?.highlightLines?.map((line) => ({
73
+ start: { line: line - 1, character: 0 },
74
+ end: { line: line - 1, character: lines[line - 1]?.length ?? 0 },
75
+ properties: { class: "highlighted" },
76
+ }));
77
+ return highlighter.codeToHtml(code, {
78
+ lang,
79
+ themes: { light: "github-light", dark: "github-dark" },
80
+ defaultColor: false,
81
+ ...(decorations?.length ? { decorations } : {}),
82
+ });
83
+ }
84
+ //# sourceMappingURL=shiki.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shiki.js","sourceRoot":"","sources":["../../src/utils/shiki.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,iBAAiB,EAAoB,MAAM,OAAO,CAAC;AAE5D,IAAI,WAAW,GAAuB,IAAI,CAAC;AAO3C;;GAEG;AACH,MAAM,YAAY,GAA2B;IAC3C,EAAE,EAAE,YAAY;IAChB,EAAE,EAAE,YAAY;IAChB,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,MAAM;IACV,EAAE,EAAE,MAAM;IACV,KAAK,EAAE,MAAM;IACb,GAAG,EAAE,MAAM;IACX,EAAE,EAAE,UAAU;IACd,GAAG,EAAE,UAAU;CAChB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,IAAY,EACZ,IAAY,EACZ,OAA0B;IAE1B,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,WAAW,GAAG,MAAM,iBAAiB,CAAC;YACpC,MAAM,EAAE,CAAC,cAAc,EAAE,aAAa,CAAC;YACvC,KAAK,EAAE;gBACL,YAAY;gBACZ,YAAY;gBACZ,QAAQ;gBACR,MAAM;gBACN,MAAM;gBACN,MAAM;gBACN,MAAM;gBACN,KAAK;gBACL,UAAU;gBACV,KAAK;gBACL,KAAK;gBACL,MAAM;gBACN,IAAI;gBACJ,MAAM;gBACN,KAAK;gBACL,SAAS;gBACT,MAAM;gBACN,MAAM;aACP;SACF,CAAC,CAAC;IACL,CAAC;IAED,2BAA2B;IAC3B,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;IAElC,gDAAgD;IAChD,MAAM,MAAM,GAAG,WAAW,CAAC,kBAAkB,EAAE,CAAC;IAChD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAa,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,WAAW,CAAC,YAAY,CAAC,IAAa,CAAC,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACP,4CAA4C;YAC5C,IAAI,GAAG,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAED,0CAA0C;IAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,WAAW,GAAG,OAAO,EAAE,cAAc,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC1D,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE;QACvC,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,EAAE;QAChE,UAAU,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE;KACrC,CAAC,CAAC,CAAC;IAEJ,OAAO,WAAW,CAAC,UAAU,CAAC,IAAI,EAAE;QAClC,IAAI;QACJ,MAAM,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,EAAE;QACtD,YAAY,EAAE,KAAK;QACnB,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChD,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Sidebar navigation helpers — shared logic used by both
3
+ * `Sidebar.astro` and its unit tests.
4
+ */
5
+ import type { NavItem } from "@specglass/core";
6
+ /** Filter out hidden items from a navigation list. */
7
+ export declare function filterVisibleItems(items: NavItem[]): NavItem[];
8
+ /** Check if this item or any descendant is the current page. */
9
+ export declare function isActiveOrAncestor(item: NavItem, currentSlug: string): boolean;
10
+ //# sourceMappingURL=sidebar-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidebar-helpers.d.ts","sourceRoot":"","sources":["../../src/utils/sidebar-helpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C,sDAAsD;AACtD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAE9D;AAED,gEAAgE;AAChE,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,OAAO,EACb,WAAW,EAAE,MAAM,GAClB,OAAO,CAQT"}
@@ -0,0 +1,14 @@
1
+ /** Filter out hidden items from a navigation list. */
2
+ export function filterVisibleItems(items) {
3
+ return items.filter((item) => !item.hidden);
4
+ }
5
+ /** Check if this item or any descendant is the current page. */
6
+ export function isActiveOrAncestor(item, currentSlug) {
7
+ if (item.type === "page" && item.slug === currentSlug)
8
+ return true;
9
+ if (item.children) {
10
+ return item.children.some((child) => isActiveOrAncestor(child, currentSlug));
11
+ }
12
+ return false;
13
+ }
14
+ //# sourceMappingURL=sidebar-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidebar-helpers.js","sourceRoot":"","sources":["../../src/utils/sidebar-helpers.ts"],"names":[],"mappings":"AAMA,sDAAsD;AACtD,MAAM,UAAU,kBAAkB,CAAC,KAAgB;IACjD,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC9C,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,kBAAkB,CAChC,IAAa,EACb,WAAmB;IAEnB,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IACnE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAClC,kBAAkB,CAAC,KAAK,EAAE,WAAW,CAAC,CACvC,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { SpecglassConfig } from "@specglass/core";
2
+ /**
3
+ * Generates a CSS string with custom property overrides from the theme config.
4
+ *
5
+ * Brand tokens (primaryColor, fonts) are set at `:root` and apply to both modes.
6
+ * Surface colors support two formats:
7
+ * - `"#0a0a0f"` (string) → sets `-dark` variant only (backward compat)
8
+ * - `{ light: "#fff", dark: "#000" }` → sets both base and `-dark` variants
9
+ *
10
+ * Returns an empty string if no theme config values are set,
11
+ * allowing the @theme defaults to apply.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const css = generateThemeCSS(config);
16
+ * // String value: ":root { --color-surface-dark: #0a0a0f; }"
17
+ * // Pair value: ":root { --color-surface: #fff; --color-surface-dark: #0a0a0f; }"
18
+ * ```
19
+ */
20
+ export declare function generateThemeCSS(config: SpecglassConfig): string;
21
+ //# sourceMappingURL=theme-css.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-css.d.ts","sourceRoot":"","sources":["../../src/utils/theme-css.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AA8CvD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAoChE"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * CSS custom property mapping from theme config.
3
+ *
4
+ * Maps config values to CSS custom properties that override
5
+ * the Tailwind @theme design tokens. This implements the config-level
6
+ * tier of the three-tier customization system.
7
+ *
8
+ * The generated CSS is injected as a <style> block in the layout <head>,
9
+ * ensuring config values take precedence over @theme defaults.
10
+ */
11
+ /** Properties that apply globally (brand colors, fonts) — always :root */
12
+ const ROOT_CSS_MAP = [
13
+ { configPath: (t) => t.primaryColor ?? t.colors?.primary, cssProperty: "--color-primary" },
14
+ { configPath: (t) => t.accentColor ?? t.colors?.accent, cssProperty: "--color-primary-light" },
15
+ { configPath: (t) => t.fontFamily, cssProperty: "--font-sans" },
16
+ { configPath: (t) => t.codeFontFamily, cssProperty: "--font-mono" },
17
+ ];
18
+ const COLOR_PAIR_MAP = [
19
+ { configPath: (t) => t.colors?.background, lightProperty: "--color-surface", darkProperty: "--color-surface-dark" },
20
+ { configPath: (t) => t.colors?.foreground, lightProperty: "--color-text", darkProperty: "--color-text-dark" },
21
+ { configPath: (t) => t.colors?.muted, lightProperty: "--color-text-muted", darkProperty: "--color-text-muted-dark" },
22
+ { configPath: (t) => t.colors?.border, lightProperty: "--color-border", darkProperty: "--color-border-dark" },
23
+ ];
24
+ /**
25
+ * Generates a CSS string with custom property overrides from the theme config.
26
+ *
27
+ * Brand tokens (primaryColor, fonts) are set at `:root` and apply to both modes.
28
+ * Surface colors support two formats:
29
+ * - `"#0a0a0f"` (string) → sets `-dark` variant only (backward compat)
30
+ * - `{ light: "#fff", dark: "#000" }` → sets both base and `-dark` variants
31
+ *
32
+ * Returns an empty string if no theme config values are set,
33
+ * allowing the @theme defaults to apply.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const css = generateThemeCSS(config);
38
+ * // String value: ":root { --color-surface-dark: #0a0a0f; }"
39
+ * // Pair value: ":root { --color-surface: #fff; --color-surface-dark: #0a0a0f; }"
40
+ * ```
41
+ */
42
+ export function generateThemeCSS(config) {
43
+ const theme = config.theme;
44
+ if (!theme)
45
+ return "";
46
+ const overrides = [];
47
+ // Brand / font overrides → always :root
48
+ for (const mapping of ROOT_CSS_MAP) {
49
+ const value = mapping.configPath(theme);
50
+ if (value !== undefined) {
51
+ overrides.push(` ${mapping.cssProperty}: ${value};`);
52
+ }
53
+ }
54
+ // Surface color overrides → light/dark pair or dark-only
55
+ for (const mapping of COLOR_PAIR_MAP) {
56
+ const value = mapping.configPath(theme);
57
+ if (value === undefined)
58
+ continue;
59
+ if (typeof value === "string") {
60
+ // Backward compat: string = dark-only override
61
+ overrides.push(` ${mapping.darkProperty}: ${value};`);
62
+ }
63
+ else {
64
+ // Explicit pair
65
+ if (value.light) {
66
+ overrides.push(` ${mapping.lightProperty}: ${value.light};`);
67
+ }
68
+ if (value.dark) {
69
+ overrides.push(` ${mapping.darkProperty}: ${value.dark};`);
70
+ }
71
+ }
72
+ }
73
+ if (overrides.length === 0)
74
+ return "";
75
+ return `:root {\n${overrides.join("\n")}\n}`;
76
+ }
77
+ //# sourceMappingURL=theme-css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-css.js","sourceRoot":"","sources":["../../src/utils/theme-css.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Theme utilities — pure functions for theme storage and application.
3
+ *
4
+ * Extracted to a shared utility for testability (Story 3.1 pattern).
5
+ * Used by ThemeToggle.tsx. The FOUC-prevention inline script reimplements
6
+ * the same logic inline because `is:inline` scripts cannot import modules.
7
+ */
8
+ export type Theme = "dark" | "light";
9
+ /** localStorage key for theme preference */
10
+ export declare const THEME_STORAGE_KEY = "specglass-theme";
11
+ /** Default theme when no preference is stored */
12
+ export declare const DEFAULT_THEME: Theme;
13
+ /**
14
+ * Read the stored theme preference from localStorage.
15
+ * Returns null if no preference is stored or localStorage is unavailable.
16
+ */
17
+ export declare function getStoredTheme(): Theme | null;
18
+ /**
19
+ * Determine the effective theme given a stored preference.
20
+ * Falls back to DEFAULT_THEME ("dark") when no preference exists.
21
+ */
22
+ export declare function getEffectiveTheme(stored: Theme | null): Theme;
23
+ /**
24
+ * Apply a theme by toggling the .dark class on <html>.
25
+ * Also persists the preference to localStorage.
26
+ */
27
+ export declare function applyTheme(theme: Theme): void;
28
+ //# sourceMappingURL=theme-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-helpers.d.ts","sourceRoot":"","sources":["../../src/utils/theme-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;AAErC,4CAA4C;AAC5C,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAEnD,iDAAiD;AACjD,eAAO,MAAM,aAAa,EAAE,KAAc,CAAC;AAE3C;;;GAGG;AACH,wBAAgB,cAAc,IAAI,KAAK,GAAG,IAAI,CAW7C;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,KAAK,GAAG,IAAI,GAAG,KAAK,CAE7D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAa7C"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Theme utilities — pure functions for theme storage and application.
3
+ *
4
+ * Extracted to a shared utility for testability (Story 3.1 pattern).
5
+ * Used by ThemeToggle.tsx. The FOUC-prevention inline script reimplements
6
+ * the same logic inline because `is:inline` scripts cannot import modules.
7
+ */
8
+ /** localStorage key for theme preference */
9
+ export const THEME_STORAGE_KEY = "specglass-theme";
10
+ /** Default theme when no preference is stored */
11
+ export const DEFAULT_THEME = "dark";
12
+ /**
13
+ * Read the stored theme preference from localStorage.
14
+ * Returns null if no preference is stored or localStorage is unavailable.
15
+ */
16
+ export function getStoredTheme() {
17
+ try {
18
+ const stored = localStorage.getItem(THEME_STORAGE_KEY);
19
+ if (stored === "dark" || stored === "light") {
20
+ return stored;
21
+ }
22
+ return null;
23
+ }
24
+ catch {
25
+ // localStorage unavailable (SSR, privacy mode, etc.)
26
+ return null;
27
+ }
28
+ }
29
+ /**
30
+ * Determine the effective theme given a stored preference.
31
+ * Falls back to DEFAULT_THEME ("dark") when no preference exists.
32
+ */
33
+ export function getEffectiveTheme(stored) {
34
+ return stored ?? DEFAULT_THEME;
35
+ }
36
+ /**
37
+ * Apply a theme by toggling the .dark class on <html>.
38
+ * Also persists the preference to localStorage.
39
+ */
40
+ export function applyTheme(theme) {
41
+ const root = document.documentElement;
42
+ if (theme === "dark") {
43
+ root.classList.add("dark");
44
+ }
45
+ else {
46
+ root.classList.remove("dark");
47
+ }
48
+ try {
49
+ localStorage.setItem(THEME_STORAGE_KEY, theme);
50
+ }
51
+ catch {
52
+ // localStorage unavailable — graceful degradation
53
+ }
54
+ }
55
+ //# sourceMappingURL=theme-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-helpers.js","sourceRoot":"","sources":["../../src/utils/theme-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,4CAA4C;AAC5C,MAAM,CAAC,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;AAEnD,iDAAiD;AACjD,MAAM,CAAC,MAAM,aAAa,GAAU,MAAM,CAAC;AAE3C;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACvD,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;YAC5C,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,qDAAqD;QACrD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAoB;IACpD,OAAO,MAAM,IAAI,aAAa,CAAC;AACjC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,KAAY;IACrC,MAAM,IAAI,GAAG,QAAQ,CAAC,eAAe,CAAC;IACtC,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QACrB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IAED,IAAI,CAAC;QACH,YAAY,CAAC,OAAO,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,kDAAkD;IACpD,CAAC;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Table of Contents helpers — shared logic used by both
3
+ * `TableOfContents.astro` and its unit tests.
4
+ */
5
+ export type Heading = {
6
+ depth: number;
7
+ slug: string;
8
+ text: string;
9
+ };
10
+ /** Filter headings to include only h2 and h3 for ToC display. */
11
+ export declare function filterTocHeadings(headings: Heading[]): Heading[];
12
+ //# sourceMappingURL=toc-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toc-helpers.d.ts","sourceRoot":"","sources":["../../src/utils/toc-helpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,OAAO,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE,iEAAiE;AACjE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAEhE"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Table of Contents helpers — shared logic used by both
3
+ * `TableOfContents.astro` and its unit tests.
4
+ */
5
+ /** Filter headings to include only h2 and h3 for ToC display. */
6
+ export function filterTocHeadings(headings) {
7
+ return headings.filter((h) => h.depth >= 2 && h.depth <= 3);
8
+ }
9
+ //# sourceMappingURL=toc-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toc-helpers.js","sourceRoot":"","sources":["../../src/utils/toc-helpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,iEAAiE;AACjE,MAAM,UAAU,iBAAiB,CAAC,QAAmB;IACnD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;AAC9D,CAAC"}
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@specglass/theme-default",
3
+ "version": "0.0.2",
4
+ "description": "Default theme for Specglass — layouts, components, React islands, and styles",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/Not-Diamond/ndocs.git",
10
+ "directory": "packages/theme-default"
11
+ },
12
+ "homepage": "https://github.com/Not-Diamond/ndocs#readme",
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js"
20
+ },
21
+ "./components/*": "./src/components/*",
22
+ "./islands/*": "./src/islands/*",
23
+ "./layouts/*": "./src/layouts/*",
24
+ "./lib/*": "./src/lib/*",
25
+ "./styles/*": "./src/styles/*",
26
+ "./utils/*": "./src/utils/*"
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "src/components",
31
+ "src/islands",
32
+ "src/layouts",
33
+ "src/lib",
34
+ "src/styles",
35
+ "src/utils"
36
+ ],
37
+ "scripts": {
38
+ "build": "tsc",
39
+ "test": "vitest run",
40
+ "lint": "eslint src/"
41
+ },
42
+ "peerDependencies": {
43
+ "astro": "^5.17",
44
+ "shiki": "^3.0"
45
+ },
46
+ "dependencies": {
47
+ "@astrojs/react": "^4.4.2",
48
+ "@radix-ui/react-dialog": "^1.1.15",
49
+ "@specglass/core": "*",
50
+ "@tailwindcss/vite": "^4.1.18",
51
+ "clsx": "^2.1.1",
52
+ "cmdk": "^1.1.1",
53
+ "react": "^19.2.4",
54
+ "react-dom": "^19.2.4",
55
+ "tailwind-merge": "^3.4.0",
56
+ "tailwindcss": "^4.1.18",
57
+ "tailwindcss-animate": "^1.0.7"
58
+ },
59
+ "devDependencies": {
60
+ "@testing-library/react": "^16.3.2",
61
+ "@types/react": "^19.2.14",
62
+ "@types/react-dom": "^19.2.3",
63
+ "astro": "^5.17",
64
+ "jsdom": "^28.0.0",
65
+ "typescript": "^5.5",
66
+ "vitest": "^3.0"
67
+ }
68
+ }
@@ -0,0 +1,116 @@
1
+ ---
2
+ /**
3
+ * ApiAuth.astro — Displays authentication/security requirements for an endpoint.
4
+ * Shows both global security schemes and per-endpoint security from the parsed OpenAPI spec.
5
+ */
6
+ import type { ApiSecurityRequirement } from "@specglass/core";
7
+
8
+ export interface Props {
9
+ security: ApiSecurityRequirement[];
10
+ securitySchemes?: Record<string, ApiSecurityRequirement>;
11
+ }
12
+
13
+ const { security, securitySchemes } = Astro.props;
14
+
15
+ const schemeTypeLabels: Record<string, string> = {
16
+ apiKey: "API Key",
17
+ http: "HTTP",
18
+ oauth2: "OAuth 2.0",
19
+ openIdConnect: "OpenID Connect",
20
+ };
21
+
22
+ const hasAuth = security.length > 0;
23
+ const hasGlobalSchemes = securitySchemes && Object.keys(securitySchemes).length > 0;
24
+ ---
25
+
26
+ {
27
+ (hasAuth || hasGlobalSchemes) && (
28
+ <div class="mb-8">
29
+ <h3 class="text-sm font-semibold text-text-muted uppercase tracking-wider mb-3">
30
+ <span class="mr-1.5">🔒</span> Authentication
31
+ </h3>
32
+
33
+ {/* Per-endpoint security requirements */}
34
+ {hasAuth && (
35
+ <div class="border border-border rounded-lg overflow-hidden mb-4">
36
+ <div class="px-4 py-2 bg-surface-raised border-b border-border/50 text-xs font-medium text-text-muted">
37
+ Required for this endpoint
38
+ </div>
39
+ {security.map((req, idx) => (
40
+ <div
41
+ class:list={[
42
+ "px-4 py-3 flex items-start gap-3",
43
+ idx > 0 && "border-t border-border/50",
44
+ ]}
45
+ >
46
+ {/* Scheme type badge */}
47
+ <span class="inline-flex shrink-0 items-center px-2 py-0.5 rounded text-xs font-medium bg-purple-500/15 text-purple-700 dark:text-purple-400 border border-purple-500/30 mt-0.5">
48
+ {schemeTypeLabels[req.type] ?? req.type}
49
+ </span>
50
+
51
+ <div class="min-w-0">
52
+ <div class="text-sm text-text font-medium">{req.schemeName}</div>
53
+
54
+ {/* API Key specifics */}
55
+ {req.type === "apiKey" && req.in && req.name && (
56
+ <p class="text-xs text-text-muted/60 mt-1 font-mono">
57
+ {req.in}: <code>{req.name}</code>
58
+ </p>
59
+ )}
60
+
61
+ {/* HTTP auth specifics */}
62
+ {req.type === "http" && req.scheme && (
63
+ <p class="text-xs text-text-muted/60 mt-1 font-mono">Scheme: {req.scheme}</p>
64
+ )}
65
+
66
+ {/* Scopes */}
67
+ {req.scopes.length > 0 && (
68
+ <div class="mt-1.5 flex flex-wrap gap-1">
69
+ <span class="text-xs text-text-muted mr-1">Scopes:</span>
70
+ {req.scopes.map((scope) => (
71
+ <span class="inline-block px-1.5 py-0.5 text-xs rounded bg-surface-raised text-text-muted font-mono border border-border/50">
72
+ {scope}
73
+ </span>
74
+ ))}
75
+ </div>
76
+ )}
77
+ </div>
78
+ </div>
79
+ ))}
80
+ </div>
81
+ )}
82
+
83
+ {/* Global security schemes */}
84
+ {hasGlobalSchemes && (
85
+ <div class="border border-border rounded-lg overflow-hidden">
86
+ <div class="px-4 py-2 bg-surface-raised border-b border-border/50 text-xs font-medium text-text-muted">
87
+ Available security schemes
88
+ </div>
89
+ {Object.entries(securitySchemes!).map(([name, scheme], idx) => (
90
+ <div
91
+ class:list={[
92
+ "px-4 py-3 flex items-start gap-3",
93
+ idx > 0 && "border-t border-border/50",
94
+ ]}
95
+ >
96
+ <span class="inline-flex shrink-0 items-center px-2 py-0.5 rounded text-xs font-medium bg-purple-500/15 text-purple-700 dark:text-purple-400 border border-purple-500/30 mt-0.5">
97
+ {schemeTypeLabels[scheme.type] ?? scheme.type}
98
+ </span>
99
+ <div class="min-w-0">
100
+ <div class="text-sm text-text font-medium">{name}</div>
101
+ {scheme.type === "apiKey" && scheme.in && scheme.name && (
102
+ <p class="text-xs text-text-muted/60 mt-1 font-mono">
103
+ {scheme.in}: <code>{scheme.name}</code>
104
+ </p>
105
+ )}
106
+ {scheme.type === "http" && scheme.scheme && (
107
+ <p class="text-xs text-text-muted/60 mt-1 font-mono">Scheme: {scheme.scheme}</p>
108
+ )}
109
+ </div>
110
+ </div>
111
+ ))}
112
+ </div>
113
+ )}
114
+ </div>
115
+ )
116
+ }
@@ -0,0 +1,75 @@
1
+ ---
2
+ /**
3
+ * ApiEndpoint.astro — Renders the header section of a single API endpoint.
4
+ * Shows HTTP method badge, path, summary, description, operation ID, and deprecated warning.
5
+ */
6
+ import type { ApiEndpoint } from "@specglass/core";
7
+
8
+ export interface Props {
9
+ endpoint: ApiEndpoint;
10
+ }
11
+
12
+ const { endpoint } = Astro.props;
13
+
14
+ const methodColors: Record<string, string> = {
15
+ get: "bg-emerald-500/15 text-emerald-700 dark:text-emerald-400 border-emerald-500/30",
16
+ post: "bg-blue-500/15 text-blue-700 dark:text-blue-400 border-blue-500/30",
17
+ put: "bg-amber-500/15 text-amber-700 dark:text-amber-400 border-amber-500/30",
18
+ patch: "bg-yellow-500/15 text-yellow-700 dark:text-yellow-400 border-yellow-500/30",
19
+ delete: "bg-red-500/15 text-red-700 dark:text-red-400 border-red-500/30",
20
+ options: "bg-purple-500/15 text-purple-700 dark:text-purple-400 border-purple-500/30",
21
+ head: "bg-gray-500/15 text-gray-700 dark:text-gray-400 border-gray-500/30",
22
+ };
23
+
24
+ const methodColor = methodColors[endpoint.method.toLowerCase()] ?? methodColors.get;
25
+ ---
26
+
27
+ <div id={`endpoint-${endpoint.method}-${endpoint.path.replace(/[{}\\/]/g, "-")}`} class="mb-8">
28
+ {/* Deprecated banner */}
29
+ {
30
+ endpoint.deprecated && (
31
+ <div class="mb-4 rounded-lg border border-amber-500/30 bg-amber-500/10 px-4 py-3 text-amber-700 dark:text-amber-300 text-sm">
32
+ <span class="font-semibold">⚠ Deprecated</span> — This endpoint is deprecated and may be
33
+ removed in a future version.
34
+ </div>
35
+ )
36
+ }
37
+
38
+ {/* Method + Path header */}
39
+ <div class="flex items-center gap-3 mb-4">
40
+ <span
41
+ class:list={[
42
+ "inline-flex items-center px-3 py-1 rounded-full text-xs font-bold uppercase tracking-wider border",
43
+ methodColor,
44
+ ]}
45
+ >
46
+ {endpoint.method.toUpperCase()}
47
+ </span>
48
+ <code class="text-lg font-mono text-text font-semibold break-all">
49
+ {endpoint.path}
50
+ </code>
51
+ </div>
52
+
53
+ {/* Summary */}
54
+ {
55
+ endpoint.summary && (
56
+ <h1 class="text-xl font-semibold text-text mb-2 mt-0">{endpoint.summary}</h1>
57
+ )
58
+ }
59
+
60
+ {/* Description */}
61
+ {
62
+ endpoint.description && (
63
+ <p class="text-text-muted text-sm leading-relaxed mb-4">{endpoint.description}</p>
64
+ )
65
+ }
66
+
67
+ {/* Operation ID */}
68
+ {
69
+ endpoint.operationId && (
70
+ <p class="text-xs text-text-muted/60 font-mono mb-4">
71
+ Operation ID: <code class="text-text-muted/80">{endpoint.operationId}</code>
72
+ </p>
73
+ )
74
+ }
75
+ </div>