@specglass/theme-default 0.0.2 → 0.0.4

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 (115) hide show
  1. package/dist/__tests__/api-endpoint-fallback.test.d.ts +1 -0
  2. package/dist/__tests__/api-endpoint-fallback.test.js +153 -0
  3. package/dist/__tests__/code-tabs.test.d.ts +0 -1
  4. package/dist/__tests__/code-tabs.test.js +0 -1
  5. package/dist/__tests__/copy-button.test.d.ts +0 -1
  6. package/dist/__tests__/copy-button.test.js +0 -1
  7. package/dist/__tests__/search-palette.test.d.ts +0 -1
  8. package/dist/__tests__/search-palette.test.js +0 -1
  9. package/dist/__tests__/shiki.test.d.ts +0 -1
  10. package/dist/__tests__/shiki.test.js +0 -1
  11. package/dist/__tests__/theme-css.test.d.ts +0 -1
  12. package/dist/__tests__/theme-css.test.js +0 -1
  13. package/dist/__tests__/theme-helpers.test.d.ts +0 -1
  14. package/dist/__tests__/theme-helpers.test.js +0 -1
  15. package/dist/index.d.ts +0 -1
  16. package/dist/index.js +0 -1
  17. package/dist/islands/CodeTabs.d.ts +0 -1
  18. package/dist/islands/CodeTabs.js +0 -1
  19. package/dist/islands/CopyButton.d.ts +0 -1
  20. package/dist/islands/CopyButton.js +0 -1
  21. package/dist/islands/SearchPalette.d.ts +0 -1
  22. package/dist/islands/SearchPalette.js +0 -1
  23. package/dist/islands/SearchResults.d.ts +0 -1
  24. package/dist/islands/SearchResults.js +0 -1
  25. package/dist/islands/ThemeToggle.d.ts +0 -1
  26. package/dist/islands/ThemeToggle.js +0 -1
  27. package/dist/layouts/DocPage.test.d.ts +0 -1
  28. package/dist/layouts/DocPage.test.js +0 -1
  29. package/dist/lib/utils.d.ts +0 -1
  30. package/dist/lib/utils.js +0 -1
  31. package/dist/scripts/code-block-enhancer.d.ts +0 -1
  32. package/dist/scripts/code-block-enhancer.js +0 -1
  33. package/dist/ui/command.d.ts +0 -1
  34. package/dist/ui/command.js +0 -1
  35. package/dist/ui/dialog.d.ts +0 -1
  36. package/dist/ui/dialog.js +0 -1
  37. package/dist/utils/parse-highlight-range.d.ts +0 -1
  38. package/dist/utils/parse-highlight-range.js +0 -1
  39. package/dist/utils/parse-highlight-range.test.d.ts +0 -1
  40. package/dist/utils/parse-highlight-range.test.js +0 -1
  41. package/dist/utils/schema-renderer.d.ts +0 -1
  42. package/dist/utils/schema-renderer.js +0 -1
  43. package/dist/utils/schema-renderer.test.d.ts +0 -1
  44. package/dist/utils/schema-renderer.test.js +0 -1
  45. package/dist/utils/shiki.d.ts +0 -1
  46. package/dist/utils/shiki.js +0 -1
  47. package/dist/utils/sidebar-helpers.d.ts +0 -1
  48. package/dist/utils/sidebar-helpers.js +0 -1
  49. package/dist/utils/theme-css.d.ts +0 -1
  50. package/dist/utils/theme-css.js +0 -1
  51. package/dist/utils/theme-helpers.d.ts +0 -1
  52. package/dist/utils/theme-helpers.js +0 -1
  53. package/dist/utils/toc-helpers.d.ts +0 -1
  54. package/dist/utils/toc-helpers.js +0 -1
  55. package/package.json +7 -3
  56. package/src/components/ApiEndpointFallback.astro +102 -0
  57. package/src/components/ApiExampleRequest.astro +192 -0
  58. package/src/components/ApiExampleResponse.astro +145 -0
  59. package/src/components/ApiNavigation.astro +70 -3
  60. package/src/layouts/ApiReferencePage.astro +75 -38
  61. package/src/scripts/code-block-enhancer.ts +59 -0
  62. package/src/ui/command.tsx +183 -0
  63. package/src/ui/dialog.tsx +133 -0
  64. package/dist/__tests__/code-tabs.test.d.ts.map +0 -1
  65. package/dist/__tests__/code-tabs.test.js.map +0 -1
  66. package/dist/__tests__/copy-button.test.d.ts.map +0 -1
  67. package/dist/__tests__/copy-button.test.js.map +0 -1
  68. package/dist/__tests__/search-palette.test.d.ts.map +0 -1
  69. package/dist/__tests__/search-palette.test.js.map +0 -1
  70. package/dist/__tests__/shiki.test.d.ts.map +0 -1
  71. package/dist/__tests__/shiki.test.js.map +0 -1
  72. package/dist/__tests__/theme-css.test.d.ts.map +0 -1
  73. package/dist/__tests__/theme-css.test.js.map +0 -1
  74. package/dist/__tests__/theme-helpers.test.d.ts.map +0 -1
  75. package/dist/__tests__/theme-helpers.test.js.map +0 -1
  76. package/dist/index.d.ts.map +0 -1
  77. package/dist/index.js.map +0 -1
  78. package/dist/islands/CodeTabs.d.ts.map +0 -1
  79. package/dist/islands/CodeTabs.js.map +0 -1
  80. package/dist/islands/CopyButton.d.ts.map +0 -1
  81. package/dist/islands/CopyButton.js.map +0 -1
  82. package/dist/islands/SearchPalette.d.ts.map +0 -1
  83. package/dist/islands/SearchPalette.js.map +0 -1
  84. package/dist/islands/SearchResults.d.ts.map +0 -1
  85. package/dist/islands/SearchResults.js.map +0 -1
  86. package/dist/islands/ThemeToggle.d.ts.map +0 -1
  87. package/dist/islands/ThemeToggle.js.map +0 -1
  88. package/dist/layouts/DocPage.test.d.ts.map +0 -1
  89. package/dist/layouts/DocPage.test.js.map +0 -1
  90. package/dist/lib/utils.d.ts.map +0 -1
  91. package/dist/lib/utils.js.map +0 -1
  92. package/dist/scripts/code-block-enhancer.d.ts.map +0 -1
  93. package/dist/scripts/code-block-enhancer.js.map +0 -1
  94. package/dist/ui/command.d.ts.map +0 -1
  95. package/dist/ui/command.js.map +0 -1
  96. package/dist/ui/dialog.d.ts.map +0 -1
  97. package/dist/ui/dialog.js.map +0 -1
  98. package/dist/utils/parse-highlight-range.d.ts.map +0 -1
  99. package/dist/utils/parse-highlight-range.js.map +0 -1
  100. package/dist/utils/parse-highlight-range.test.d.ts.map +0 -1
  101. package/dist/utils/parse-highlight-range.test.js.map +0 -1
  102. package/dist/utils/schema-renderer.d.ts.map +0 -1
  103. package/dist/utils/schema-renderer.js.map +0 -1
  104. package/dist/utils/schema-renderer.test.d.ts.map +0 -1
  105. package/dist/utils/schema-renderer.test.js.map +0 -1
  106. package/dist/utils/shiki.d.ts.map +0 -1
  107. package/dist/utils/shiki.js.map +0 -1
  108. package/dist/utils/sidebar-helpers.d.ts.map +0 -1
  109. package/dist/utils/sidebar-helpers.js.map +0 -1
  110. package/dist/utils/theme-css.d.ts.map +0 -1
  111. package/dist/utils/theme-css.js.map +0 -1
  112. package/dist/utils/theme-helpers.d.ts.map +0 -1
  113. package/dist/utils/theme-helpers.js.map +0 -1
  114. package/dist/utils/toc-helpers.d.ts.map +0 -1
  115. package/dist/utils/toc-helpers.js.map +0 -1
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Tests for error endpoint slug generation, data contract, and
3
+ * the shared buildErrorEndpointSlug utility from @specglass/core.
4
+ */
5
+ import { describe, it, expect } from "vitest";
6
+ import { buildEndpointSlug, buildErrorEndpointSlug, buildEndpointId } from "@specglass/core";
7
+ describe("buildErrorEndpointSlug", () => {
8
+ it("generates correct slug for a simple errored endpoint", () => {
9
+ const error = {
10
+ path: "/pets",
11
+ method: "get",
12
+ reason: "Complex polymorphism not supported",
13
+ rawSpec: { get: { summary: "List pets" } },
14
+ };
15
+ expect(buildErrorEndpointSlug(error)).toBe("_unsupported/get-pets");
16
+ });
17
+ it("generates correct slug for nested path with parameters", () => {
18
+ const error = {
19
+ path: "/users/{userId}/orders/{orderId}",
20
+ method: "patch",
21
+ reason: "Unsupported allOf/oneOf",
22
+ rawSpec: {},
23
+ };
24
+ expect(buildErrorEndpointSlug(error)).toBe("_unsupported/patch-users-userId-orders-orderId");
25
+ });
26
+ it("generates correct slug for root path", () => {
27
+ const error = {
28
+ path: "/",
29
+ method: "get",
30
+ reason: "Unsupported extension",
31
+ rawSpec: {},
32
+ };
33
+ expect(buildErrorEndpointSlug(error)).toBe("_unsupported/get-");
34
+ });
35
+ it("uses _unsupported prefix to avoid collision with valid endpoint slugs", () => {
36
+ // An errored endpoint at /pets GET should NOT collide with a valid
37
+ // endpoint at /pets GET (which gets slug "default/get-pets")
38
+ const error = {
39
+ path: "/pets",
40
+ method: "get",
41
+ reason: "test",
42
+ rawSpec: {},
43
+ };
44
+ const validEndpoint = {
45
+ path: "/pets",
46
+ method: "get",
47
+ tags: [],
48
+ summary: "",
49
+ description: "",
50
+ operationId: "",
51
+ deprecated: false,
52
+ parameters: [],
53
+ responses: [],
54
+ security: [],
55
+ requestBody: undefined,
56
+ };
57
+ const errorSlug = buildErrorEndpointSlug(error);
58
+ const validSlug = buildEndpointSlug(validEndpoint);
59
+ expect(errorSlug).not.toBe(validSlug);
60
+ expect(errorSlug).toContain("_unsupported/");
61
+ expect(validSlug).toContain("default/");
62
+ });
63
+ it("generates unique IDs for errored endpoints", () => {
64
+ const error = {
65
+ path: "/pets/{petId}",
66
+ method: "delete",
67
+ reason: "test",
68
+ rawSpec: {},
69
+ };
70
+ const errId = `${error.method}-${error.path}`;
71
+ expect(errId).toBe("delete-/pets/{petId}");
72
+ // This matches buildEndpointId behavior
73
+ const validEndpoint = {
74
+ path: "/pets/{petId}",
75
+ method: "delete",
76
+ tags: [],
77
+ summary: "",
78
+ description: "",
79
+ operationId: "",
80
+ deprecated: false,
81
+ parameters: [],
82
+ responses: [],
83
+ security: [],
84
+ requestBody: undefined,
85
+ };
86
+ expect(errId).toBe(buildEndpointId(validEndpoint));
87
+ });
88
+ });
89
+ describe("ApiEndpointError data contract", () => {
90
+ it("has all required fields for rendering", () => {
91
+ const error = {
92
+ path: "/complex-endpoint",
93
+ method: "post",
94
+ reason: "Complex polymorphism (allOf with discriminator) is not supported",
95
+ rawSpec: {
96
+ post: {
97
+ summary: "Create complex resource",
98
+ requestBody: {
99
+ content: {
100
+ "application/json": {
101
+ schema: {
102
+ allOf: [{ $ref: "#/components/schemas/Base" }],
103
+ discriminator: { propertyName: "type" },
104
+ },
105
+ },
106
+ },
107
+ },
108
+ },
109
+ },
110
+ };
111
+ // Verify all fields exist and are the correct types
112
+ expect(typeof error.path).toBe("string");
113
+ expect(typeof error.method).toBe("string");
114
+ expect(typeof error.reason).toBe("string");
115
+ expect(error.rawSpec).toBeDefined();
116
+ expect(typeof error.rawSpec).toBe("object");
117
+ });
118
+ it("rawSpec serializes to valid JSON for display", () => {
119
+ const error = {
120
+ path: "/test",
121
+ method: "get",
122
+ reason: "test",
123
+ rawSpec: {
124
+ nested: { deeply: { value: [1, 2, 3] } },
125
+ special: 'chars "in" strings',
126
+ },
127
+ };
128
+ const json = JSON.stringify(error.rawSpec, null, 2);
129
+ expect(() => JSON.parse(json)).not.toThrow();
130
+ expect(json).toContain("nested");
131
+ expect(json).toContain("deeply");
132
+ });
133
+ it("handles empty rawSpec gracefully", () => {
134
+ const error = {
135
+ path: "/test",
136
+ method: "get",
137
+ reason: "test",
138
+ rawSpec: {},
139
+ };
140
+ const json = JSON.stringify(error.rawSpec, null, 2);
141
+ expect(json).toBe("{}");
142
+ });
143
+ it("handles null rawSpec gracefully", () => {
144
+ const error = {
145
+ path: "/test",
146
+ method: "get",
147
+ reason: "test",
148
+ rawSpec: null,
149
+ };
150
+ const json = JSON.stringify(error.rawSpec, null, 2);
151
+ expect(json).toBe("null");
152
+ });
153
+ });
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=code-tabs.test.d.ts.map
@@ -216,4 +216,3 @@ describe("CodeTabs", () => {
216
216
  });
217
217
  });
218
218
  });
219
- //# sourceMappingURL=code-tabs.test.js.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=copy-button.test.d.ts.map
@@ -113,4 +113,3 @@ describe("CopyButton logic", () => {
113
113
  expect(result.current.copied).toBe(false);
114
114
  });
115
115
  });
116
- //# sourceMappingURL=copy-button.test.js.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=search-palette.test.d.ts.map
@@ -68,4 +68,3 @@ describe("ui/dialog", () => {
68
68
  expect(mod.DialogDescription).toBeDefined();
69
69
  });
70
70
  });
71
- //# sourceMappingURL=search-palette.test.js.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=shiki.test.d.ts.map
@@ -34,4 +34,3 @@ describe("highlight utility", () => {
34
34
  expect(html2).toContain("--shiki-light");
35
35
  });
36
36
  });
37
- //# sourceMappingURL=shiki.test.js.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=theme-css.test.d.ts.map
@@ -121,4 +121,3 @@ describe("generateThemeCSS", () => {
121
121
  expect(css).not.toContain("--color-surface:");
122
122
  });
123
123
  });
124
- //# sourceMappingURL=theme-css.test.js.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=theme-helpers.test.d.ts.map
@@ -78,4 +78,3 @@ describe("theme-helpers", () => {
78
78
  });
79
79
  });
80
80
  });
81
- //# sourceMappingURL=theme-helpers.test.js.map
package/dist/index.d.ts CHANGED
@@ -60,4 +60,3 @@ export interface TableOfContentsProps {
60
60
  text: string;
61
61
  }>;
62
62
  }
63
- //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -10,4 +10,3 @@ export { getStoredTheme, getEffectiveTheme, applyTheme, THEME_STORAGE_KEY, DEFAU
10
10
  export { ThemeToggle } from "./islands/ThemeToggle.js";
11
11
  export { CopyButton } from "./islands/CopyButton.js";
12
12
  export { CodeTabs } from "./islands/CodeTabs.js";
13
- //# sourceMappingURL=index.js.map
@@ -18,4 +18,3 @@ interface CodeTabsProps {
18
18
  declare function CodeTabs({ tabs, syncKey, ariaLabel }: CodeTabsProps): import("react/jsx-runtime").JSX.Element | null;
19
19
  export { CodeTabs };
20
20
  export type { CodeTab, CodeTabsProps };
21
- //# sourceMappingURL=CodeTabs.d.ts.map
@@ -122,4 +122,3 @@ function CodeTabs({ tabs, syncKey = "sdk-language", ariaLabel = "SDK language ta
122
122
  return (_jsxs("div", { className: "my-6 border border-border rounded-lg overflow-hidden", children: [_jsx("div", { role: "tablist", "aria-label": ariaLabel, className: "flex border-b border-border bg-surface-raised", onKeyDown: handleKeyDown, children: tabs.map((tab, i) => (_jsx("button", { id: `${groupId}-tab-${i}`, role: "tab", "aria-selected": i === activeIndex, "aria-controls": `${groupId}-panel-${i}`, tabIndex: i === activeIndex ? 0 : -1, className: cn("px-4 py-2 border-b-2 border-transparent text-[0.8125rem] font-medium font-mono", "text-text-muted cursor-pointer whitespace-nowrap bg-transparent", "transition-colors duration-150 hover:text-text", "focus-visible:outline-2 focus-visible:outline-primary focus-visible:-outline-offset-2", i === activeIndex && "text-text border-b-primary"), onClick: () => setTab(i), children: tab.label }, tab.language))) }), tabs.map((tab, i) => (_jsx("div", { id: `${groupId}-panel-${i}`, role: "tabpanel", "aria-labelledby": `${groupId}-tab-${i}`, tabIndex: 0, className: "code-tabs-panel", style: { display: i === activeIndex ? undefined : "none" }, dangerouslySetInnerHTML: { __html: tab.content } }, tab.language)))] }));
123
123
  }
124
124
  export { CodeTabs };
125
- //# sourceMappingURL=CodeTabs.js.map
@@ -13,4 +13,3 @@ export interface CopyButtonProps {
13
13
  code: string;
14
14
  }
15
15
  export declare function CopyButton({ code }: CopyButtonProps): import("react/jsx-runtime").JSX.Element;
16
- //# sourceMappingURL=CopyButton.d.ts.map
@@ -51,4 +51,3 @@ export function CopyButton({ code }) {
51
51
  /* Copy icon */
52
52
  _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }), _jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })] })) }));
53
53
  }
54
- //# sourceMappingURL=CopyButton.js.map
@@ -1,2 +1 @@
1
1
  export declare function SearchPalette(): import("react/jsx-runtime").JSX.Element;
2
- //# sourceMappingURL=SearchPalette.d.ts.map
@@ -106,4 +106,3 @@ export function SearchPalette() {
106
106
  __html: result.sub_results?.[0]?.excerpt ?? result.excerpt ?? "",
107
107
  } })] }, result.id)))] }), (results.length > 0 || query.trim().length > 0) && !isUnavailable && (_jsx("div", { className: "flex items-center justify-between border-t border-border px-3 py-2 text-xs text-text-muted", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("span", { className: "inline-flex items-center gap-1", children: [_jsx("kbd", { className: "rounded border border-border bg-surface-raised px-1 py-0.5 font-mono text-[10px]", children: "\u2191\u2193" }), "navigate"] }), _jsxs("span", { className: "inline-flex items-center gap-1", children: [_jsx("kbd", { className: "rounded border border-border bg-surface-raised px-1 py-0.5 font-mono text-[10px]", children: "\u21B5" }), "open"] }), _jsxs("span", { className: "inline-flex items-center gap-1", children: [_jsx("kbd", { className: "rounded border border-border bg-surface-raised px-1 py-0.5 font-mono text-[10px]", children: "esc" }), "close"] })] }) }))] })] }));
108
108
  }
109
- //# sourceMappingURL=SearchPalette.js.map
@@ -1,2 +1 @@
1
1
  export declare function SearchResults(): import("react/jsx-runtime").JSX.Element;
2
- //# sourceMappingURL=SearchResults.d.ts.map
@@ -127,4 +127,3 @@ export function SearchResults() {
127
127
  __html: result.sub_results?.[0]?.excerpt ?? result.excerpt ?? "",
128
128
  } })] }, result.id)))] }))] }));
129
129
  }
130
- //# sourceMappingURL=SearchResults.js.map
@@ -9,4 +9,3 @@
9
9
  * Imports shared utilities from theme-helpers.ts for consistency.
10
10
  */
11
11
  export declare function ThemeToggle(): import("react/jsx-runtime").JSX.Element;
12
- //# sourceMappingURL=ThemeToggle.d.ts.map
@@ -40,4 +40,3 @@ export function ThemeToggle() {
40
40
  }, []);
41
41
  return (_jsx("button", { type: "button", onClick: toggleTheme, "aria-label": isDark ? "Switch to light mode" : "Switch to dark mode", className: "inline-flex items-center justify-center rounded-md p-2 text-text-muted hover:text-(--color-text) hover:bg-hover-bg focus:outline-none focus-visible:ring-2 focus-visible:ring-primary transition-colors", children: _jsxs("span", { className: "relative inline-flex items-center justify-center w-5 h-5", children: [_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", className: `absolute inset-0 transition-transform duration-300 ${isDark ? "rotate-0 scale-100" : "-rotate-90 scale-0"}`, children: [_jsx("circle", { cx: "12", cy: "12", r: "4" }), _jsx("path", { d: "M12 2v2" }), _jsx("path", { d: "M12 20v2" }), _jsx("path", { d: "m4.93 4.93 1.41 1.41" }), _jsx("path", { d: "m17.66 17.66 1.41 1.41" }), _jsx("path", { d: "M2 12h2" }), _jsx("path", { d: "M20 12h2" }), _jsx("path", { d: "m6.34 17.66-1.41 1.41" }), _jsx("path", { d: "m19.07 4.93-1.41 1.41" })] }), _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", className: `absolute inset-0 transition-transform duration-300 ${!isDark ? "rotate-0 scale-100" : "rotate-90 scale-0"}`, children: _jsx("path", { d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" }) })] }) }));
42
42
  }
43
- //# sourceMappingURL=ThemeToggle.js.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=DocPage.test.d.ts.map
@@ -162,4 +162,3 @@ describe("DocPage props contract", () => {
162
162
  });
163
163
  });
164
164
  });
165
- //# sourceMappingURL=DocPage.test.js.map
@@ -7,4 +7,3 @@ import { type ClassValue } from "clsx";
7
7
  * Will be used by UI primitives in upcoming stories (e.g., search, dialogs).
8
8
  */
9
9
  export declare function cn(...inputs: ClassValue[]): string;
10
- //# sourceMappingURL=utils.d.ts.map
package/dist/lib/utils.js CHANGED
@@ -10,4 +10,3 @@ import { twMerge } from "tailwind-merge";
10
10
  export function cn(...inputs) {
11
11
  return twMerge(clsx(inputs));
12
12
  }
13
- //# sourceMappingURL=utils.js.map
@@ -13,4 +13,3 @@
13
13
  * until the user actually clicks copy.
14
14
  */
15
15
  declare function initCopyButtons(): void;
16
- //# sourceMappingURL=code-block-enhancer.d.ts.map
@@ -52,4 +52,3 @@ function initCopyButtons() {
52
52
  document.addEventListener("DOMContentLoaded", initCopyButtons);
53
53
  // Re-run after Astro ViewTransitions page swaps
54
54
  document.addEventListener("astro:after-swap", initCopyButtons);
55
- //# sourceMappingURL=code-block-enhancer.js.map
@@ -84,4 +84,3 @@ declare const CommandShortcut: {
84
84
  displayName: string;
85
85
  };
86
86
  export { Command, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandShortcut, CommandSeparator, };
87
- //# sourceMappingURL=command.d.ts.map
@@ -25,4 +25,3 @@ const CommandShortcut = ({ className, ...props }) => {
25
25
  };
26
26
  CommandShortcut.displayName = "CommandShortcut";
27
27
  export { Command, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandShortcut, CommandSeparator, };
28
- //# sourceMappingURL=command.js.map
@@ -17,4 +17,3 @@ declare const DialogFooter: {
17
17
  declare const DialogTitle: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogTitleProps & React.RefAttributes<HTMLHeadingElement>, "ref"> & React.RefAttributes<HTMLHeadingElement>>;
18
18
  declare const DialogDescription: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogDescriptionProps & React.RefAttributes<HTMLParagraphElement>, "ref"> & React.RefAttributes<HTMLParagraphElement>>;
19
19
  export { Dialog, DialogPortal, DialogOverlay, DialogClose, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, };
20
- //# sourceMappingURL=dialog.d.ts.map
package/dist/ui/dialog.js CHANGED
@@ -19,4 +19,3 @@ DialogTitle.displayName = DialogPrimitive.Title.displayName;
19
19
  const DialogDescription = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Description, { ref: ref, className: cn("text-sm text-text-muted", className), ...props })));
20
20
  DialogDescription.displayName = DialogPrimitive.Description.displayName;
21
21
  export { Dialog, DialogPortal, DialogOverlay, DialogClose, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, };
22
- //# sourceMappingURL=dialog.js.map
@@ -9,4 +9,3 @@
9
9
  * parseHighlightRange("") // → []
10
10
  */
11
11
  export declare function parseHighlightRange(range: string): number[];
12
- //# sourceMappingURL=parse-highlight-range.d.ts.map
@@ -37,4 +37,3 @@ export function parseHighlightRange(range) {
37
37
  }
38
38
  return result;
39
39
  }
40
- //# sourceMappingURL=parse-highlight-range.js.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=parse-highlight-range.test.d.ts.map
@@ -29,4 +29,3 @@ describe("parseHighlightRange", () => {
29
29
  expect(parseHighlightRange("1,abc,3")).toEqual([1, 3]);
30
30
  });
31
31
  });
32
- //# sourceMappingURL=parse-highlight-range.test.js.map
@@ -35,4 +35,3 @@ export declare function renderTypeString(schema: ApiSchema | undefined): string;
35
35
  * suitable for rendering in a table. Only expands 1 level deep for MVP.
36
36
  */
37
37
  export declare function flattenSchemaProperties(schema: ApiSchema | undefined, requiredFields?: string[], maxDepth?: number): SchemaProperty[];
38
- //# sourceMappingURL=schema-renderer.d.ts.map
@@ -112,4 +112,3 @@ export function flattenSchemaProperties(schema, requiredFields, maxDepth = 1) {
112
112
  }
113
113
  return properties;
114
114
  }
115
- //# sourceMappingURL=schema-renderer.js.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=schema-renderer.test.d.ts.map
@@ -216,4 +216,3 @@ describe("flattenSchemaProperties", () => {
216
216
  expect(props[0].name).toBe("nested");
217
217
  });
218
218
  });
219
- //# sourceMappingURL=schema-renderer.test.js.map
@@ -17,4 +17,3 @@ export interface HighlightOptions {
17
17
  * Returns raw HTML string with CSS variable tokens — ready for `set:html`.
18
18
  */
19
19
  export declare function highlight(code: string, lang: string, options?: HighlightOptions): Promise<string>;
20
- //# sourceMappingURL=shiki.d.ts.map
@@ -81,4 +81,3 @@ export async function highlight(code, lang, options) {
81
81
  ...(decorations?.length ? { decorations } : {}),
82
82
  });
83
83
  }
84
- //# sourceMappingURL=shiki.js.map
@@ -7,4 +7,3 @@ import type { NavItem } from "@specglass/core";
7
7
  export declare function filterVisibleItems(items: NavItem[]): NavItem[];
8
8
  /** Check if this item or any descendant is the current page. */
9
9
  export declare function isActiveOrAncestor(item: NavItem, currentSlug: string): boolean;
10
- //# sourceMappingURL=sidebar-helpers.d.ts.map
@@ -11,4 +11,3 @@ export function isActiveOrAncestor(item, currentSlug) {
11
11
  }
12
12
  return false;
13
13
  }
14
- //# sourceMappingURL=sidebar-helpers.js.map
@@ -18,4 +18,3 @@ import type { SpecglassConfig } from "@specglass/core";
18
18
  * ```
19
19
  */
20
20
  export declare function generateThemeCSS(config: SpecglassConfig): string;
21
- //# sourceMappingURL=theme-css.d.ts.map
@@ -74,4 +74,3 @@ export function generateThemeCSS(config) {
74
74
  return "";
75
75
  return `:root {\n${overrides.join("\n")}\n}`;
76
76
  }
77
- //# sourceMappingURL=theme-css.js.map
@@ -25,4 +25,3 @@ export declare function getEffectiveTheme(stored: Theme | null): Theme;
25
25
  * Also persists the preference to localStorage.
26
26
  */
27
27
  export declare function applyTheme(theme: Theme): void;
28
- //# sourceMappingURL=theme-helpers.d.ts.map
@@ -52,4 +52,3 @@ export function applyTheme(theme) {
52
52
  // localStorage unavailable — graceful degradation
53
53
  }
54
54
  }
55
- //# sourceMappingURL=theme-helpers.js.map
@@ -9,4 +9,3 @@ export type Heading = {
9
9
  };
10
10
  /** Filter headings to include only h2 and h3 for ToC display. */
11
11
  export declare function filterTocHeadings(headings: Heading[]): Heading[];
12
- //# sourceMappingURL=toc-helpers.d.ts.map
@@ -6,4 +6,3 @@
6
6
  export function filterTocHeadings(headings) {
7
7
  return headings.filter((h) => h.depth >= 2 && h.depth <= 3);
8
8
  }
9
- //# sourceMappingURL=toc-helpers.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specglass/theme-default",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Default theme for Specglass — layouts, components, React islands, and styles",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,7 +23,9 @@
23
23
  "./layouts/*": "./src/layouts/*",
24
24
  "./lib/*": "./src/lib/*",
25
25
  "./styles/*": "./src/styles/*",
26
- "./utils/*": "./src/utils/*"
26
+ "./utils/*": "./src/utils/*",
27
+ "./ui/*": "./src/ui/*",
28
+ "./scripts/*": "./src/scripts/*"
27
29
  },
28
30
  "files": [
29
31
  "dist",
@@ -32,7 +34,9 @@
32
34
  "src/layouts",
33
35
  "src/lib",
34
36
  "src/styles",
35
- "src/utils"
37
+ "src/utils",
38
+ "src/ui",
39
+ "src/scripts"
36
40
  ],
37
41
  "scripts": {
38
42
  "build": "tsc",
@@ -0,0 +1,102 @@
1
+ ---
2
+ /**
3
+ * ApiEndpointFallback.astro — Renders a "Manual Documentation Required" placeholder
4
+ * for endpoints where the OpenAPI parser encountered unsupported spec patterns (FR40).
5
+ *
6
+ * Shows the error reason and raw spec data in a collapsible, syntax-highlighted block.
7
+ */
8
+ import type { ApiEndpointError } from "@specglass/core";
9
+ import { CopyButton } from "../islands/CopyButton";
10
+ import { highlight } from "../utils/shiki";
11
+
12
+ export interface Props {
13
+ error: ApiEndpointError;
14
+ }
15
+
16
+ const { error } = Astro.props;
17
+
18
+ const methodColors: Record<string, string> = {
19
+ get: "bg-emerald-500/15 text-emerald-700 dark:text-emerald-400 border-emerald-500/30",
20
+ post: "bg-blue-500/15 text-blue-700 dark:text-blue-400 border-blue-500/30",
21
+ put: "bg-amber-500/15 text-amber-700 dark:text-amber-400 border-amber-500/30",
22
+ patch: "bg-yellow-500/15 text-yellow-700 dark:text-yellow-400 border-yellow-500/30",
23
+ delete: "bg-red-500/15 text-red-700 dark:text-red-400 border-red-500/30",
24
+ options: "bg-purple-500/15 text-purple-700 dark:text-purple-400 border-purple-500/30",
25
+ head: "bg-gray-500/15 text-gray-700 dark:text-gray-400 border-gray-500/30",
26
+ };
27
+
28
+ const methodColor = methodColors[error.method.toLowerCase()] ?? methodColors.get;
29
+ const rawSpecJson = JSON.stringify(error.rawSpec, null, 2);
30
+ const highlightedSpec = await highlight(rawSpecJson, "json");
31
+ ---
32
+
33
+ <div id={`endpoint-${error.method}-${error.path.replace(/[{}\\\/]/g, "-")}`} class="mb-8">
34
+ {/* Method + Path header */}
35
+ <div class="flex items-center gap-3 mb-4">
36
+ <span
37
+ class:list={[
38
+ "inline-flex items-center px-3 py-1 rounded-full text-xs font-bold uppercase tracking-wider border",
39
+ methodColor,
40
+ ]}
41
+ >
42
+ {error.method.toUpperCase()}
43
+ </span>
44
+ <code class="text-lg font-mono text-text font-semibold break-all">
45
+ {error.path}
46
+ </code>
47
+ </div>
48
+
49
+ {/* Warning alert */}
50
+ <div
51
+ role="alert"
52
+ class="rounded-lg border border-amber-500/30 bg-amber-50 dark:bg-amber-950/20 px-5 py-4 mb-6"
53
+ >
54
+ <div class="flex items-start gap-3">
55
+ <span class="text-amber-600 dark:text-amber-400 text-xl mt-0.5" aria-hidden="true">⚠️</span>
56
+ <div>
57
+ <h3 class="text-base font-semibold text-amber-800 dark:text-amber-200 mt-0 mb-1">
58
+ Manual Documentation Required
59
+ </h3>
60
+ <p class="text-sm text-amber-700 dark:text-amber-300 leading-relaxed m-0">
61
+ This endpoint uses an OpenAPI spec pattern that couldn't be fully parsed.
62
+ {
63
+ error.reason && (
64
+ <span class="block mt-1 text-amber-600 dark:text-amber-400 font-medium">
65
+ {error.reason}
66
+ </span>
67
+ )
68
+ }
69
+ </p>
70
+ </div>
71
+ </div>
72
+ </div>
73
+
74
+ {/* Raw spec data — collapsed by default */}
75
+ <details class="rounded-lg border border-border bg-surface-code overflow-hidden">
76
+ <summary
77
+ class="cursor-pointer px-4 py-3 text-sm font-medium text-text-muted hover:text-text transition-colors select-none flex items-center gap-2"
78
+ >
79
+ <svg
80
+ class="w-4 h-4 transition-transform open:rotate-90"
81
+ viewBox="0 0 20 20"
82
+ fill="currentColor"
83
+ aria-hidden="true"
84
+ >
85
+ <path
86
+ fill-rule="evenodd"
87
+ d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z"
88
+ clip-rule="evenodd"></path>
89
+ </svg>
90
+ View raw OpenAPI spec data
91
+ </summary>
92
+ <div class="relative group border-t border-border">
93
+ <div
94
+ class="[&_pre]:overflow-x-auto [&_pre]:p-4 [&_pre]:text-sm [&_pre]:leading-relaxed [&_pre]:m-0"
95
+ set:html={highlightedSpec}
96
+ />
97
+ <div class="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity">
98
+ <CopyButton client:idle code={rawSpecJson} />
99
+ </div>
100
+ </div>
101
+ </details>
102
+ </div>