specra 0.1.13 → 0.2.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 (276) hide show
  1. package/LICENSE.MD +25 -4
  2. package/README.md +67 -58
  3. package/config/specra.config.schema.json +16 -0
  4. package/config/svelte-config.js +63 -0
  5. package/dist/api-parser.types.d.ts +59 -0
  6. package/dist/api-parser.types.js +5 -0
  7. package/dist/api.types.d.ts +137 -0
  8. package/dist/api.types.js +5 -0
  9. package/dist/category.d.ts +21 -0
  10. package/dist/category.js +48 -0
  11. package/dist/components/ConfigProvider.svelte +13 -0
  12. package/dist/components/ConfigProvider.svelte.d.ts +31 -0
  13. package/dist/components/docs/Accordion.svelte +18 -0
  14. package/dist/components/docs/Accordion.svelte.d.ts +10 -0
  15. package/dist/components/docs/AccordionItem.svelte +41 -0
  16. package/dist/components/docs/AccordionItem.svelte.d.ts +10 -0
  17. package/dist/components/docs/Badge.svelte +28 -0
  18. package/dist/components/docs/Badge.svelte.d.ts +9 -0
  19. package/dist/components/docs/Breadcrumb.svelte +80 -0
  20. package/dist/components/docs/Breadcrumb.svelte.d.ts +8 -0
  21. package/dist/components/docs/Callout.svelte +96 -0
  22. package/dist/components/docs/Callout.svelte.d.ts +10 -0
  23. package/dist/components/docs/Card.svelte +63 -0
  24. package/dist/components/docs/Card.svelte.d.ts +12 -0
  25. package/dist/components/docs/CardGrid.svelte +24 -0
  26. package/dist/components/docs/CardGrid.svelte.d.ts +8 -0
  27. package/dist/components/docs/CategoryIndex.svelte +110 -0
  28. package/dist/components/docs/CategoryIndex.svelte.d.ts +29 -0
  29. package/dist/components/docs/CodeBlock.svelte +172 -0
  30. package/dist/components/docs/CodeBlock.svelte.d.ts +8 -0
  31. package/dist/components/docs/Column.svelte +25 -0
  32. package/dist/components/docs/Column.svelte.d.ts +8 -0
  33. package/dist/components/docs/Columns.svelte +38 -0
  34. package/dist/components/docs/Columns.svelte.d.ts +13 -0
  35. package/dist/components/docs/DevModeBadge.svelte +15 -0
  36. package/dist/components/docs/DevModeBadge.svelte.d.ts +18 -0
  37. package/dist/components/docs/DocBadge.svelte +28 -0
  38. package/dist/components/docs/DocBadge.svelte.d.ts +9 -0
  39. package/dist/components/docs/DocLayout.svelte +107 -0
  40. package/dist/components/docs/DocLayout.svelte.d.ts +32 -0
  41. package/dist/components/docs/DocLoading.svelte +53 -0
  42. package/dist/components/docs/DocLoading.svelte.d.ts +18 -0
  43. package/dist/components/docs/DocMetadata.svelte +106 -0
  44. package/dist/components/docs/DocMetadata.svelte.d.ts +18 -0
  45. package/dist/components/docs/DocNavigation.svelte +56 -0
  46. package/dist/components/docs/DocNavigation.svelte.d.ts +12 -0
  47. package/dist/components/docs/DocTags.svelte +22 -0
  48. package/dist/components/docs/DocTags.svelte.d.ts +6 -0
  49. package/dist/components/docs/DraftBadge.svelte +10 -0
  50. package/dist/components/docs/DraftBadge.svelte.d.ts +18 -0
  51. package/dist/components/docs/Footer.svelte +72 -0
  52. package/dist/components/docs/Footer.svelte.d.ts +7 -0
  53. package/dist/components/docs/Frame.svelte +27 -0
  54. package/dist/components/docs/Frame.svelte.d.ts +9 -0
  55. package/dist/components/docs/Header.svelte +123 -0
  56. package/dist/components/docs/Header.svelte.d.ts +9 -0
  57. package/dist/components/docs/HeaderWithMenu.svelte +34 -0
  58. package/dist/components/docs/HeaderWithMenu.svelte.d.ts +17 -0
  59. package/dist/components/docs/HotReloadIndicator.svelte +44 -0
  60. package/dist/components/docs/HotReloadIndicator.svelte.d.ts +3 -0
  61. package/dist/components/docs/Icon.svelte +103 -0
  62. package/dist/components/docs/Icon.svelte.d.ts +11 -0
  63. package/dist/components/docs/Image.svelte +88 -0
  64. package/dist/components/docs/Image.svelte.d.ts +11 -0
  65. package/dist/components/docs/ImageCard.svelte +91 -0
  66. package/dist/components/docs/ImageCard.svelte.d.ts +12 -0
  67. package/dist/components/docs/ImageCardGrid.svelte +25 -0
  68. package/dist/components/docs/ImageCardGrid.svelte.d.ts +8 -0
  69. package/dist/components/docs/LayoutProviders.svelte +57 -0
  70. package/dist/components/docs/LayoutProviders.svelte.d.ts +9 -0
  71. package/dist/components/docs/Logo.svelte +25 -0
  72. package/dist/components/docs/Logo.svelte.d.ts +11 -0
  73. package/dist/components/docs/Math.svelte +54 -0
  74. package/dist/components/docs/Math.svelte.d.ts +7 -0
  75. package/dist/components/docs/MdxContent.svelte +41 -0
  76. package/dist/components/docs/MdxHotReload.svelte +78 -0
  77. package/dist/components/docs/MdxHotReload.svelte.d.ts +9 -0
  78. package/dist/components/docs/MdxLayout.svelte +16 -0
  79. package/dist/components/docs/MdxLayout.svelte.d.ts +6 -0
  80. package/dist/components/docs/Mermaid.svelte +88 -0
  81. package/dist/components/docs/Mermaid.svelte.d.ts +7 -0
  82. package/dist/components/docs/MobileDocLayout.svelte +211 -0
  83. package/dist/components/docs/MobileDocLayout.svelte.d.ts +35 -0
  84. package/dist/components/docs/MobileSidebar.svelte +122 -0
  85. package/dist/components/docs/MobileSidebar.svelte.d.ts +31 -0
  86. package/dist/components/docs/MobileSidebarWrapper.svelte +122 -0
  87. package/dist/components/docs/MobileSidebarWrapper.svelte.d.ts +32 -0
  88. package/dist/components/docs/NotFoundContent.svelte +40 -0
  89. package/dist/components/docs/NotFoundContent.svelte.d.ts +6 -0
  90. package/dist/components/docs/SearchHighlight.svelte +116 -0
  91. package/dist/components/docs/SearchHighlight.svelte.d.ts +3 -0
  92. package/dist/components/docs/SearchModal.svelte +239 -0
  93. package/dist/components/docs/SearchModal.svelte.d.ts +9 -0
  94. package/dist/components/docs/Sidebar.svelte +69 -0
  95. package/dist/components/docs/Sidebar.svelte.d.ts +31 -0
  96. package/dist/components/docs/SidebarMenuItems.svelte +344 -0
  97. package/dist/components/docs/SidebarMenuItems.svelte.d.ts +33 -0
  98. package/dist/components/docs/SidebarSkeleton.svelte +50 -0
  99. package/dist/components/docs/SidebarSkeleton.svelte.d.ts +18 -0
  100. package/dist/components/docs/SiteBanner.svelte +92 -0
  101. package/dist/components/docs/SiteBanner.svelte.d.ts +7 -0
  102. package/dist/components/docs/Step.svelte +44 -0
  103. package/dist/components/docs/Step.svelte.d.ts +8 -0
  104. package/dist/components/docs/Steps.svelte +15 -0
  105. package/dist/components/docs/Steps.svelte.d.ts +7 -0
  106. package/dist/components/docs/Tab.svelte +40 -0
  107. package/dist/components/docs/Tab.svelte.d.ts +8 -0
  108. package/dist/components/docs/TabGroups.svelte +183 -0
  109. package/dist/components/docs/TabGroups.svelte.d.ts +25 -0
  110. package/dist/components/docs/TableOfContents.svelte +100 -0
  111. package/dist/components/docs/TableOfContents.svelte.d.ts +9 -0
  112. package/dist/components/docs/Tabs.svelte +69 -0
  113. package/dist/components/docs/Tabs.svelte.d.ts +8 -0
  114. package/dist/components/docs/ThemeToggle.svelte +16 -0
  115. package/dist/components/docs/ThemeToggle.svelte.d.ts +18 -0
  116. package/dist/components/docs/Tooltip.svelte +44 -0
  117. package/dist/components/docs/Tooltip.svelte.d.ts +10 -0
  118. package/dist/components/docs/VersionSwitcher.svelte +95 -0
  119. package/dist/components/docs/VersionSwitcher.svelte.d.ts +7 -0
  120. package/dist/components/docs/Video.svelte +84 -0
  121. package/dist/components/docs/Video.svelte.d.ts +12 -0
  122. package/dist/components/docs/api/ApiEndpoint.svelte +61 -0
  123. package/dist/components/docs/api/ApiEndpoint.svelte.d.ts +11 -0
  124. package/dist/components/docs/api/ApiParams.svelte +80 -0
  125. package/dist/components/docs/api/ApiParams.svelte.d.ts +14 -0
  126. package/dist/components/docs/api/ApiPlayground.svelte +259 -0
  127. package/dist/components/docs/api/ApiPlayground.svelte.d.ts +16 -0
  128. package/dist/components/docs/api/ApiReference.svelte +278 -0
  129. package/dist/components/docs/api/ApiReference.svelte.d.ts +23 -0
  130. package/dist/components/docs/api/ApiResponse.svelte +66 -0
  131. package/dist/components/docs/api/ApiResponse.svelte.d.ts +9 -0
  132. package/dist/components/docs/api/index.d.ts +5 -0
  133. package/dist/components/docs/api/index.js +5 -0
  134. package/dist/components/docs/componentTextProps.d.ts +3 -0
  135. package/dist/components/docs/componentTextProps.js +61 -0
  136. package/dist/components/docs/index.d.ts +54 -0
  137. package/dist/components/docs/index.js +56 -0
  138. package/dist/components/global/VersionNotFound.svelte +48 -0
  139. package/dist/components/global/VersionNotFound.svelte.d.ts +7 -0
  140. package/dist/components/global/index.d.ts +1 -0
  141. package/dist/components/global/index.js +1 -0
  142. package/dist/components/index.d.ts +6 -822
  143. package/dist/components/index.js +11 -3854
  144. package/dist/components/ui/Badge.svelte +48 -0
  145. package/dist/components/ui/Badge.svelte.d.ts +15 -0
  146. package/dist/components/ui/Button.svelte +58 -0
  147. package/dist/components/ui/Button.svelte.d.ts +17 -0
  148. package/dist/components/ui/Dialog.svelte +16 -0
  149. package/dist/components/ui/Dialog.svelte.d.ts +9 -0
  150. package/dist/components/ui/DialogClose.svelte +16 -0
  151. package/dist/components/ui/DialogClose.svelte.d.ts +9 -0
  152. package/dist/components/ui/DialogContent.svelte +43 -0
  153. package/dist/components/ui/DialogContent.svelte.d.ts +10 -0
  154. package/dist/components/ui/DialogDescription.svelte +21 -0
  155. package/dist/components/ui/DialogDescription.svelte.d.ts +9 -0
  156. package/dist/components/ui/DialogFooter.svelte +20 -0
  157. package/dist/components/ui/DialogFooter.svelte.d.ts +9 -0
  158. package/dist/components/ui/DialogHeader.svelte +20 -0
  159. package/dist/components/ui/DialogHeader.svelte.d.ts +9 -0
  160. package/dist/components/ui/DialogTitle.svelte +21 -0
  161. package/dist/components/ui/DialogTitle.svelte.d.ts +9 -0
  162. package/dist/components/ui/Input.svelte +23 -0
  163. package/dist/components/ui/Input.svelte.d.ts +8 -0
  164. package/dist/components/ui/Textarea.svelte +19 -0
  165. package/dist/components/ui/Textarea.svelte.d.ts +7 -0
  166. package/dist/components/ui/index.d.ts +11 -0
  167. package/dist/components/ui/index.js +11 -0
  168. package/dist/config.d.ts +8 -0
  169. package/dist/config.js +9 -0
  170. package/dist/config.schema.json +471 -0
  171. package/dist/config.server.d.ts +46 -0
  172. package/dist/config.server.js +149 -0
  173. package/dist/{mdx-ColN3Cyg.d.mts → config.types.d.ts} +22 -75
  174. package/dist/config.types.js +39 -0
  175. package/dist/dev-utils.d.ts +29 -0
  176. package/dist/dev-utils.js +63 -0
  177. package/dist/index.d.ts +19 -4
  178. package/dist/index.js +25 -4861
  179. package/dist/mdx-cache.d.ts +41 -0
  180. package/dist/mdx-cache.js +160 -0
  181. package/dist/mdx-components.js +50 -1931
  182. package/dist/mdx-security.d.ts +76 -0
  183. package/dist/mdx-security.js +217 -0
  184. package/dist/mdx.d.ts +73 -0
  185. package/dist/mdx.js +1099 -0
  186. package/dist/middleware/index.d.ts +1 -0
  187. package/dist/middleware/index.js +2 -0
  188. package/dist/middleware/security.d.ts +22 -47
  189. package/dist/middleware/security.js +111 -137
  190. package/dist/parsers/base-parser.d.ts +14 -0
  191. package/dist/parsers/base-parser.js +1 -0
  192. package/dist/parsers/index.d.ts +16 -0
  193. package/dist/parsers/index.js +51 -0
  194. package/dist/parsers/openapi-parser.d.ts +18 -0
  195. package/dist/parsers/openapi-parser.js +209 -0
  196. package/dist/parsers/postman-parser.d.ts +20 -0
  197. package/dist/parsers/postman-parser.js +260 -0
  198. package/dist/parsers/specra-parser.d.ts +10 -0
  199. package/dist/parsers/specra-parser.js +18 -0
  200. package/dist/redirects.d.ts +12 -0
  201. package/dist/redirects.js +30 -0
  202. package/dist/remark-code-meta.d.ts +6 -0
  203. package/dist/remark-code-meta.js +21 -0
  204. package/dist/sidebar-utils.d.ts +59 -0
  205. package/dist/sidebar-utils.js +144 -0
  206. package/dist/stores/config.d.ts +20 -0
  207. package/dist/stores/config.js +45 -0
  208. package/dist/stores/index.d.ts +4 -0
  209. package/dist/stores/index.js +4 -0
  210. package/dist/stores/sidebar.d.ts +7 -0
  211. package/dist/stores/sidebar.js +12 -0
  212. package/dist/stores/tabs.d.ts +6 -0
  213. package/dist/stores/tabs.js +41 -0
  214. package/dist/stores/theme.d.ts +7 -0
  215. package/dist/stores/theme.js +75 -0
  216. package/dist/{styles.css → styles/globals.css} +136 -6
  217. package/dist/toc.d.ts +9 -0
  218. package/dist/toc.js +15 -0
  219. package/dist/utils.d.ts +13 -0
  220. package/dist/utils.js +30 -0
  221. package/package.json +47 -90
  222. package/dist/app/api/mdx-watch/route.d.mts +0 -10
  223. package/dist/app/api/mdx-watch/route.d.ts +0 -10
  224. package/dist/app/api/mdx-watch/route.js +0 -118
  225. package/dist/app/api/mdx-watch/route.js.map +0 -1
  226. package/dist/app/api/mdx-watch/route.mjs +0 -91
  227. package/dist/app/api/mdx-watch/route.mjs.map +0 -1
  228. package/dist/chunk-6S3EJVEO.mjs +0 -259
  229. package/dist/chunk-6S3EJVEO.mjs.map +0 -1
  230. package/dist/chunk-BE7EROIW.mjs +0 -212
  231. package/dist/chunk-BE7EROIW.mjs.map +0 -1
  232. package/dist/chunk-CWHRZHZO.mjs +0 -168
  233. package/dist/chunk-CWHRZHZO.mjs.map +0 -1
  234. package/dist/chunk-D5VDVYFY.mjs +0 -1325
  235. package/dist/chunk-D5VDVYFY.mjs.map +0 -1
  236. package/dist/chunk-WMCO2UX5.mjs +0 -585
  237. package/dist/chunk-WMCO2UX5.mjs.map +0 -1
  238. package/dist/chunk-XEMGCPZZ.mjs +0 -475
  239. package/dist/chunk-XEMGCPZZ.mjs.map +0 -1
  240. package/dist/components/index.d.mts +0 -822
  241. package/dist/components/index.js.map +0 -1
  242. package/dist/components/index.mjs +0 -3741
  243. package/dist/components/index.mjs.map +0 -1
  244. package/dist/index.d.mts +0 -4
  245. package/dist/index.js.map +0 -1
  246. package/dist/index.mjs +0 -1897
  247. package/dist/index.mjs.map +0 -1
  248. package/dist/layouts/index.d.mts +0 -34
  249. package/dist/layouts/index.d.ts +0 -34
  250. package/dist/layouts/index.js +0 -453
  251. package/dist/layouts/index.js.map +0 -1
  252. package/dist/layouts/index.mjs +0 -173
  253. package/dist/layouts/index.mjs.map +0 -1
  254. package/dist/lib/index.d.mts +0 -583
  255. package/dist/lib/index.d.ts +0 -583
  256. package/dist/lib/index.js +0 -1595
  257. package/dist/lib/index.js.map +0 -1
  258. package/dist/lib/index.mjs +0 -111
  259. package/dist/lib/index.mjs.map +0 -1
  260. package/dist/mdx-ColN3Cyg.d.ts +0 -352
  261. package/dist/mdx-components.d.mts +0 -86
  262. package/dist/mdx-components.d.ts +0 -86
  263. package/dist/mdx-components.js.map +0 -1
  264. package/dist/mdx-components.mjs +0 -206
  265. package/dist/mdx-components.mjs.map +0 -1
  266. package/dist/middleware/security.d.mts +0 -82
  267. package/dist/middleware/security.js.map +0 -1
  268. package/dist/middleware/security.mjs +0 -84
  269. package/dist/middleware/security.mjs.map +0 -1
  270. package/dist/styles.css.map +0 -1
  271. package/dist/styles.d.mts +0 -2
  272. package/dist/styles.d.ts +0 -2
  273. package/dist/styles.js +0 -2
  274. package/dist/styles.js.map +0 -1
  275. package/dist/styles.mjs +0 -1
  276. package/dist/styles.mjs.map +0 -1
@@ -0,0 +1,344 @@
1
+ <script lang="ts">
2
+ import { page } from '$app/stores';
3
+ import { ChevronRight, ChevronDown, FolderOpen, Lock } from 'lucide-svelte';
4
+ import type { SpecraConfig } from '../../config.types.js';
5
+ import Icon from './Icon.svelte';
6
+ import { sortSidebarItems, sortSidebarGroups } from '../../sidebar-utils.js';
7
+
8
+ interface DocItem {
9
+ title: string;
10
+ slug: string;
11
+ filePath: string;
12
+ section?: string;
13
+ group?: string;
14
+ sidebar?: string;
15
+ sidebar_position?: number;
16
+ categoryLabel?: string;
17
+ categoryPosition?: number;
18
+ categoryCollapsible?: boolean;
19
+ categoryCollapsed?: boolean;
20
+ categoryIcon?: string;
21
+ categoryTabGroup?: string;
22
+ meta?: {
23
+ icon?: string;
24
+ tab_group?: string;
25
+ sidebar_position?: number;
26
+ order?: number;
27
+ [key: string]: any;
28
+ };
29
+ }
30
+
31
+ interface SidebarGroup {
32
+ label: string;
33
+ path: string;
34
+ icon?: string;
35
+ items: DocItem[];
36
+ position: number;
37
+ collapsible: boolean;
38
+ defaultCollapsed: boolean;
39
+ children: Record<string, SidebarGroup>;
40
+ }
41
+
42
+ interface Props {
43
+ docs: DocItem[];
44
+ version: string;
45
+ onLinkClick?: () => void;
46
+ config: SpecraConfig;
47
+ activeTabGroup?: string;
48
+ }
49
+
50
+ let { docs, version, onLinkClick, config, activeTabGroup }: Props = $props();
51
+
52
+ let collapsed: Record<string, boolean> = $state({});
53
+ let pathname = $derived($page.url.pathname);
54
+
55
+ // Filter docs by active tab group if tab groups are configured
56
+ let hasTabGroups = $derived(
57
+ config.navigation?.tabGroups && config.navigation.tabGroups.length > 0
58
+ );
59
+
60
+ let filteredDocs = $derived.by(() => {
61
+ if (hasTabGroups && activeTabGroup) {
62
+ return docs.filter((doc) => {
63
+ const docTabGroup = doc.meta?.tab_group || doc.categoryTabGroup;
64
+ if (!docTabGroup) {
65
+ return activeTabGroup === config.navigation?.tabGroups?.[0]?.id;
66
+ }
67
+ return docTabGroup === activeTabGroup;
68
+ });
69
+ }
70
+ return docs;
71
+ });
72
+
73
+ // Build hierarchical tree structure
74
+ interface SidebarStructure {
75
+ rootGroups: Record<string, SidebarGroup>;
76
+ standalone: DocItem[];
77
+ }
78
+
79
+ let structure = $derived.by((): SidebarStructure => {
80
+ const rootGroups: Record<string, SidebarGroup> = {};
81
+ const standalone: DocItem[] = [];
82
+
83
+ filteredDocs.forEach((doc) => {
84
+ const pathParts = doc.filePath.split('/');
85
+ const isIndexFile =
86
+ doc.filePath.endsWith('/index') ||
87
+ doc.filePath === 'index' ||
88
+ (pathParts.length > 1 && doc.slug === pathParts.slice(0, -1).join('/'));
89
+
90
+ const customGroup = doc.sidebar || doc.group;
91
+
92
+ if (customGroup) {
93
+ const groupName = customGroup.charAt(0).toUpperCase() + customGroup.slice(1);
94
+ if (!rootGroups[groupName]) {
95
+ rootGroups[groupName] = {
96
+ label: groupName,
97
+ path: customGroup,
98
+ items: [],
99
+ position: 999,
100
+ collapsible: doc.categoryCollapsible ?? true,
101
+ defaultCollapsed: doc.categoryCollapsed ?? false,
102
+ children: {}
103
+ };
104
+ }
105
+ if (isIndexFile) {
106
+ rootGroups[groupName].position = doc.sidebar_position ?? 999;
107
+ rootGroups[groupName].icon = doc.categoryIcon;
108
+ } else {
109
+ rootGroups[groupName].items.push(doc);
110
+ }
111
+ return;
112
+ }
113
+
114
+ if (pathParts.length > 1) {
115
+ const folderParts = pathParts.slice(0, -1);
116
+ let currentLevel = rootGroups;
117
+ let currentPath = '';
118
+
119
+ for (let i = 0; i < folderParts.length; i++) {
120
+ const folder = folderParts[i];
121
+ currentPath = currentPath ? `${currentPath}/${folder}` : folder;
122
+ const folderLabel = folder
123
+ .split('-')
124
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
125
+ .join(' ');
126
+
127
+ if (!currentLevel[folder]) {
128
+ currentLevel[folder] = {
129
+ label:
130
+ doc.categoryLabel && i === folderParts.length - 1
131
+ ? doc.categoryLabel
132
+ : folderLabel,
133
+ path: currentPath,
134
+ icon: doc.categoryIcon,
135
+ items: [],
136
+ position: doc.categoryPosition ?? 999,
137
+ collapsible: doc.categoryCollapsible ?? true,
138
+ defaultCollapsed: doc.categoryCollapsed ?? false,
139
+ children: {}
140
+ };
141
+ }
142
+
143
+ if (i === folderParts.length - 1) {
144
+ if (isIndexFile) {
145
+ currentLevel[folder].position =
146
+ doc.categoryPosition ?? doc.sidebar_position ?? 999;
147
+ if (doc.categoryLabel) {
148
+ currentLevel[folder].label = doc.categoryLabel;
149
+ }
150
+ if (doc.categoryIcon) {
151
+ currentLevel[folder].icon = doc.categoryIcon;
152
+ }
153
+ } else {
154
+ currentLevel[folder].items.push(doc);
155
+ }
156
+ }
157
+
158
+ currentLevel = currentLevel[folder].children;
159
+ }
160
+ } else {
161
+ if (!isIndexFile) {
162
+ standalone.push(doc);
163
+ }
164
+ }
165
+ });
166
+
167
+ return { rootGroups, standalone };
168
+ });
169
+
170
+ let sortedRootGroups = $derived(sortSidebarGroups(structure.rootGroups));
171
+ let sortedStandalone = $derived(sortSidebarItems(structure.standalone));
172
+
173
+ function toggleSection(section: string) {
174
+ collapsed = { ...collapsed, [section]: !collapsed[section] };
175
+ }
176
+
177
+ function isActiveInGroup(group: SidebarGroup): boolean {
178
+ const hasActiveItem = group.items.some(
179
+ (doc) => pathname === `/docs/${version}/${doc.slug}`
180
+ );
181
+ if (hasActiveItem) return true;
182
+ return Object.values(group.children).some((child) => isActiveInGroup(child));
183
+ }
184
+
185
+ function getGroupHref(group: SidebarGroup): string {
186
+ let groupHref = `/docs/${version}/${group.path}`;
187
+
188
+ if (config.features?.i18n) {
189
+ const i18n = config.features.i18n;
190
+ const locales = typeof i18n === 'object' ? i18n.locales : ['en'];
191
+ const pathParts = pathname?.split('/') || [];
192
+ const potentialLocale = pathParts[3];
193
+
194
+ if (potentialLocale && locales.includes(potentialLocale)) {
195
+ groupHref = `/docs/${version}/${potentialLocale}/${group.path}`;
196
+ }
197
+ }
198
+
199
+ return groupHref;
200
+ }
201
+
202
+ function isGroupCollapsed(groupKey: string, group: SidebarGroup): boolean {
203
+ const hasActive = isActiveInGroup(group);
204
+ const isGroupActive = pathname === `/docs/${version}/${group.path}`;
205
+ if (hasActive || isGroupActive) return false;
206
+ return collapsed[groupKey] ?? group.defaultCollapsed;
207
+ }
208
+
209
+ type MergedItem =
210
+ | { type: 'group'; key: string; group: SidebarGroup; position: number }
211
+ | { type: 'item'; doc: DocItem; position: number };
212
+
213
+ function getMergedItems(group: SidebarGroup): MergedItem[] {
214
+ const sortedItems = sortSidebarItems(group.items);
215
+ const sortedChildren = sortSidebarGroups(group.children);
216
+
217
+ const merged: MergedItem[] = [
218
+ ...sortedChildren.map(([childKey, childGroup]) => ({
219
+ type: 'group' as const,
220
+ key: childKey,
221
+ group: childGroup,
222
+ position: childGroup.position
223
+ })),
224
+ ...sortedItems.map((doc) => ({
225
+ type: 'item' as const,
226
+ doc,
227
+ position: doc.sidebar_position ?? doc.meta?.sidebar_position ?? doc.meta?.order ?? 999
228
+ }))
229
+ ];
230
+
231
+ merged.sort((a, b) => a.position - b.position);
232
+ return merged;
233
+ }
234
+ </script>
235
+
236
+ <!-- Recursive group renderer component -->
237
+ {#snippet renderGroup(groupKey: string, group: SidebarGroup, depth: number)}
238
+ {@const sortedItems = sortSidebarItems(group.items)}
239
+ {@const sortedChildren = sortSidebarGroups(group.children)}
240
+ {@const hasChildren = sortedChildren.length > 0}
241
+ {@const hasItems = sortedItems.length > 0}
242
+ {@const hasContent = hasChildren || hasItems}
243
+ {@const isGroupActive = pathname === `/docs/${version}/${group.path}`}
244
+ {@const isCollapsed = isGroupCollapsed(groupKey, group)}
245
+ {@const marginLeft = depth > 0 ? 'ml-4' : ''}
246
+ {@const groupHref = getGroupHref(group)}
247
+ {@const mergedItems = getMergedItems(group)}
248
+
249
+ <div class="space-y-1 {marginLeft}">
250
+ <div class="flex items-center group">
251
+ <a
252
+ href={groupHref}
253
+ onclick={(e) => {
254
+ e.preventDefault();
255
+ toggleSection(groupKey);
256
+ }}
257
+ class="flex items-center gap-2 flex-1 px-3 py-2 text-sm font-semibold rounded-l-xl transition-all {isGroupActive
258
+ ? 'bg-primary/10 text-primary'
259
+ : 'text-foreground hover:bg-accent/50'}"
260
+ >
261
+ {#if group.icon}
262
+ <Icon icon={group.icon} size={16} className="shrink-0" />
263
+ {:else}
264
+ <FolderOpen size={16} class="shrink-0" />
265
+ {/if}
266
+ {group.label}
267
+ </a>
268
+
269
+ {#if hasContent && group.collapsible && config.navigation?.collapsibleSidebar}
270
+ <button
271
+ onclick={(e) => {
272
+ e.preventDefault();
273
+ e.stopPropagation();
274
+ toggleSection(groupKey);
275
+ }}
276
+ class="p-2 rounded-r-xl transition-all {isGroupActive ? 'hover:bg-primary/20' : 'hover:bg-accent/50'}"
277
+ aria-label={isCollapsed ? 'Expand section' : 'Collapse section'}
278
+ >
279
+ {#if isCollapsed}
280
+ <ChevronRight class="h-4 w-4 {isGroupActive ? 'text-primary' : 'text-muted-foreground'}" />
281
+ {:else}
282
+ <ChevronDown class="h-4 w-4 {isGroupActive ? 'text-primary' : 'text-muted-foreground'}" />
283
+ {/if}
284
+ </button>
285
+ {/if}
286
+ </div>
287
+
288
+ {#if !isCollapsed && hasContent}
289
+ <div class="ml-4 space-y-1">
290
+ {#each mergedItems as item}
291
+ {#if item.type === 'group'}
292
+ {@render renderGroup(`${groupKey}/${item.key}`, item.group, depth + 1)}
293
+ {:else}
294
+ {@const href = `/docs/${version}/${item.doc.slug}`}
295
+ {@const isActive = pathname === href}
296
+ <a
297
+ {href}
298
+ onclick={onLinkClick}
299
+ class="flex items-center gap-2 px-3 py-2 text-sm rounded-xl transition-all {isActive
300
+ ? 'bg-primary/10 text-primary font-medium'
301
+ : 'text-foreground hover:text-foreground hover:bg-accent/50'}"
302
+ >
303
+ {#if item.doc.meta?.icon}
304
+ <Icon icon={item.doc.meta.icon} size={16} className="shrink-0" />
305
+ {/if}
306
+ {item.doc.title}
307
+ {#if item.doc.meta?.isProtected}
308
+ <Lock size={14} class="shrink-0 text-muted-foreground ml-auto" />
309
+ {/if}
310
+ </a>
311
+ {/if}
312
+ {/each}
313
+ </div>
314
+ {/if}
315
+ </div>
316
+ {/snippet}
317
+
318
+ <nav class="space-y-1">
319
+ {#if sortedStandalone.length > 0}
320
+ {#each sortedStandalone as doc (doc.slug)}
321
+ {@const href = `/docs/${version}/${doc.slug}`}
322
+ {@const isActive = pathname === href}
323
+ <a
324
+ {href}
325
+ onclick={onLinkClick}
326
+ class="flex items-center gap-2 px-3 py-2 text-sm rounded-xl transition-all {isActive
327
+ ? 'bg-primary/10 text-primary font-medium'
328
+ : 'text-foreground hover:text-foreground hover:bg-accent/50'}"
329
+ >
330
+ {#if doc.meta?.icon}
331
+ <Icon icon={doc.meta.icon} size={16} className="shrink-0" />
332
+ {/if}
333
+ {doc.title}
334
+ {#if doc.meta?.isProtected}
335
+ <Lock size={14} class="shrink-0 text-muted-foreground ml-auto" />
336
+ {/if}
337
+ </a>
338
+ {/each}
339
+ {/if}
340
+
341
+ {#each sortedRootGroups as [groupKey, group] (groupKey)}
342
+ {@render renderGroup(groupKey, group, 0)}
343
+ {/each}
344
+ </nav>
@@ -0,0 +1,33 @@
1
+ import type { SpecraConfig } from '../../config.types.js';
2
+ interface DocItem {
3
+ title: string;
4
+ slug: string;
5
+ filePath: string;
6
+ section?: string;
7
+ group?: string;
8
+ sidebar?: string;
9
+ sidebar_position?: number;
10
+ categoryLabel?: string;
11
+ categoryPosition?: number;
12
+ categoryCollapsible?: boolean;
13
+ categoryCollapsed?: boolean;
14
+ categoryIcon?: string;
15
+ categoryTabGroup?: string;
16
+ meta?: {
17
+ icon?: string;
18
+ tab_group?: string;
19
+ sidebar_position?: number;
20
+ order?: number;
21
+ [key: string]: any;
22
+ };
23
+ }
24
+ interface Props {
25
+ docs: DocItem[];
26
+ version: string;
27
+ onLinkClick?: () => void;
28
+ config: SpecraConfig;
29
+ activeTabGroup?: string;
30
+ }
31
+ declare const SidebarMenuItems: import("svelte").Component<Props, {}, "">;
32
+ type SidebarMenuItems = ReturnType<typeof SidebarMenuItems>;
33
+ export default SidebarMenuItems;
@@ -0,0 +1,50 @@
1
+ <script lang="ts">
2
+ // SidebarSkeleton: Loading skeleton for the sidebar navigation
3
+ </script>
4
+
5
+ <div class="animate-pulse space-y-4 px-4 py-6">
6
+ <!-- Search skeleton -->
7
+ <div class="h-9 bg-muted rounded-md w-full"></div>
8
+
9
+ <!-- Group 1 -->
10
+ <div class="space-y-2">
11
+ <div class="h-4 bg-muted rounded-md w-24"></div>
12
+ <div class="space-y-1.5 pl-2">
13
+ <div class="h-4 bg-muted rounded-md w-36"></div>
14
+ <div class="h-4 bg-muted rounded-md w-28"></div>
15
+ <div class="h-4 bg-muted rounded-md w-32"></div>
16
+ <div class="h-4 bg-muted rounded-md w-24"></div>
17
+ </div>
18
+ </div>
19
+
20
+ <!-- Group 2 -->
21
+ <div class="space-y-2">
22
+ <div class="h-4 bg-muted rounded-md w-20"></div>
23
+ <div class="space-y-1.5 pl-2">
24
+ <div class="h-4 bg-muted rounded-md w-32"></div>
25
+ <div class="h-4 bg-muted rounded-md w-36"></div>
26
+ <div class="h-4 bg-muted rounded-md w-24"></div>
27
+ </div>
28
+ </div>
29
+
30
+ <!-- Group 3 -->
31
+ <div class="space-y-2">
32
+ <div class="h-4 bg-muted rounded-md w-28"></div>
33
+ <div class="space-y-1.5 pl-2">
34
+ <div class="h-4 bg-muted rounded-md w-28"></div>
35
+ <div class="h-4 bg-muted rounded-md w-36"></div>
36
+ <div class="h-4 bg-muted rounded-md w-32"></div>
37
+ <div class="h-4 bg-muted rounded-md w-20"></div>
38
+ <div class="h-4 bg-muted rounded-md w-28"></div>
39
+ </div>
40
+ </div>
41
+
42
+ <!-- Group 4 -->
43
+ <div class="space-y-2">
44
+ <div class="h-4 bg-muted rounded-md w-24"></div>
45
+ <div class="space-y-1.5 pl-2">
46
+ <div class="h-4 bg-muted rounded-md w-32"></div>
47
+ <div class="h-4 bg-muted rounded-md w-28"></div>
48
+ </div>
49
+ </div>
50
+ </div>
@@ -0,0 +1,18 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const SidebarSkeleton: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type SidebarSkeleton = InstanceType<typeof SidebarSkeleton>;
18
+ export default SidebarSkeleton;
@@ -0,0 +1,92 @@
1
+ <script lang="ts">
2
+ import { Info, AlertTriangle, CheckCircle, XCircle, X } from 'lucide-svelte';
3
+ import { browser } from '$app/environment';
4
+ import type { SpecraConfig } from '../../config.types.js';
5
+
6
+ interface Props {
7
+ config: SpecraConfig;
8
+ }
9
+
10
+ let { config }: Props = $props();
11
+
12
+ const banner = $derived(config.banner);
13
+ const isEnabled = $derived(banner?.enabled && banner?.message);
14
+ const bannerType = $derived(banner?.type || 'info');
15
+ const isDismissible = $derived(banner?.dismissible !== false);
16
+
17
+ let isDismissed = $state(false);
18
+
19
+ // Safe hash for storage key (handles non-Latin1 chars like emojis)
20
+ function safeHash(str: string): string {
21
+ let hash = 0;
22
+ for (let i = 0; i < str.length; i++) {
23
+ const char = str.charCodeAt(i);
24
+ hash = ((hash << 5) - hash) + char;
25
+ hash |= 0;
26
+ }
27
+ return Math.abs(hash).toString(36);
28
+ }
29
+
30
+ // Check localStorage for dismissal state
31
+ $effect(() => {
32
+ if (!browser || !banner?.message) return;
33
+ const storageKey = `specra-banner-dismissed-${safeHash(banner.message)}`;
34
+ const dismissed = localStorage.getItem(storageKey);
35
+ if (dismissed === 'true') {
36
+ isDismissed = true;
37
+ }
38
+ });
39
+
40
+ function dismiss() {
41
+ isDismissed = true;
42
+ if (browser && banner?.message) {
43
+ const storageKey = `specra-banner-dismissed-${safeHash(banner.message)}`;
44
+ localStorage.setItem(storageKey, 'true');
45
+ }
46
+ }
47
+
48
+ const typeStyles: Record<string, string> = {
49
+ info: 'bg-blue-500/10 border-blue-500/20 text-blue-700 dark:text-blue-300',
50
+ warning: 'bg-yellow-500/10 border-yellow-500/20 text-yellow-700 dark:text-yellow-300',
51
+ success: 'bg-green-500/10 border-green-500/20 text-green-700 dark:text-green-300',
52
+ error: 'bg-red-500/10 border-red-500/20 text-red-700 dark:text-red-300'
53
+ };
54
+
55
+ const iconColor: Record<string, string> = {
56
+ info: 'text-blue-500',
57
+ warning: 'text-yellow-500',
58
+ success: 'text-green-500',
59
+ error: 'text-red-500'
60
+ };
61
+ </script>
62
+
63
+ {#if isEnabled && !isDismissed}
64
+ <div
65
+ class="relative flex items-center justify-center gap-2 px-4 py-2.5 text-sm border-b {typeStyles[bannerType] || typeStyles.info}"
66
+ role="banner"
67
+ >
68
+ <span class="shrink-0 {iconColor[bannerType] || iconColor.info}">
69
+ {#if bannerType === 'info'}
70
+ <Info class="h-4 w-4" />
71
+ {:else if bannerType === 'warning'}
72
+ <AlertTriangle class="h-4 w-4" />
73
+ {:else if bannerType === 'success'}
74
+ <CheckCircle class="h-4 w-4" />
75
+ {:else if bannerType === 'error'}
76
+ <XCircle class="h-4 w-4" />
77
+ {/if}
78
+ </span>
79
+
80
+ <span class="text-center font-medium">{banner?.message}</span>
81
+
82
+ {#if isDismissible}
83
+ <button
84
+ onclick={dismiss}
85
+ class="absolute right-3 top-1/2 -translate-y-1/2 p-1 rounded-md hover:bg-black/10 dark:hover:bg-white/10 transition-colors"
86
+ aria-label="Dismiss banner"
87
+ >
88
+ <X class="h-3.5 w-3.5" />
89
+ </button>
90
+ {/if}
91
+ </div>
92
+ {/if}
@@ -0,0 +1,7 @@
1
+ import type { SpecraConfig } from '../../config.types.js';
2
+ interface Props {
3
+ config: SpecraConfig;
4
+ }
5
+ declare const SiteBanner: import("svelte").Component<Props, {}, "">;
6
+ type SiteBanner = ReturnType<typeof SiteBanner>;
7
+ export default SiteBanner;
@@ -0,0 +1,44 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ title: string;
6
+ children?: Snippet;
7
+ }
8
+
9
+ let { title, children }: Props = $props();
10
+ </script>
11
+
12
+ <div class="step-item relative pl-8 pb-6 border-l-2 border-border last:border-l-0 last:pb-0">
13
+ <div class="mb-2">
14
+ <h3 class="text-lg font-semibold text-foreground">{title}</h3>
15
+ </div>
16
+ <div class="prose prose-sm dark:prose-invert max-w-none [&>*:last-child]:mb-0">
17
+ {#if children}
18
+ {@render children()}
19
+ {/if}
20
+ </div>
21
+ </div>
22
+
23
+ <style>
24
+ .step-item {
25
+ counter-increment: step;
26
+ }
27
+ .step-item::before {
28
+ content: counter(step);
29
+ position: absolute;
30
+ left: 0;
31
+ transform: translateX(-50%);
32
+ width: 2rem;
33
+ height: 2rem;
34
+ border-radius: 9999px;
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ font-size: 0.875rem;
39
+ font-weight: 600;
40
+ z-index: 10;
41
+ background: hsl(var(--primary));
42
+ color: hsl(var(--primary-foreground));
43
+ }
44
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ title: string;
4
+ children?: Snippet;
5
+ }
6
+ declare const Step: import("svelte").Component<Props, {}, "">;
7
+ type Step = ReturnType<typeof Step>;
8
+ export default Step;
@@ -0,0 +1,15 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ children?: Snippet;
6
+ }
7
+
8
+ let { children }: Props = $props();
9
+ </script>
10
+
11
+ <div class="my-6 ml-4 space-y-6" style="counter-reset: step;">
12
+ {#if children}
13
+ {@render children()}
14
+ {/if}
15
+ </div>
@@ -0,0 +1,7 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ children?: Snippet;
4
+ }
5
+ declare const Steps: import("svelte").Component<Props, {}, "">;
6
+ type Steps = ReturnType<typeof Steps>;
7
+ export default Steps;
@@ -0,0 +1,40 @@
1
+ <script lang="ts">
2
+ import { getContext, onMount } from 'svelte';
3
+ import type { Writable } from 'svelte/store';
4
+ import type { Snippet } from 'svelte';
5
+
6
+ interface Props {
7
+ label: string;
8
+ children?: Snippet;
9
+ }
10
+
11
+ let { label, children }: Props = $props();
12
+
13
+ const ctx = getContext<{
14
+ registerTab: (label: string) => void;
15
+ activeTab: Writable<string>;
16
+ setActiveTab: (label: string) => void;
17
+ }>('specra-tabs');
18
+
19
+ let isActive = $state(false);
20
+
21
+ onMount(() => {
22
+ ctx.registerTab(label);
23
+ });
24
+
25
+ // Subscribe to active tab changes
26
+ $effect(() => {
27
+ const unsub = ctx.activeTab.subscribe((value) => {
28
+ isActive = value === label;
29
+ });
30
+ return unsub;
31
+ });
32
+ </script>
33
+
34
+ {#if isActive}
35
+ <div class="prose prose-slate dark:prose-invert max-w-none [&>*:first-child]:mt-0">
36
+ {#if children}
37
+ {@render children()}
38
+ {/if}
39
+ </div>
40
+ {/if}
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ label: string;
4
+ children?: Snippet;
5
+ }
6
+ declare const Tab: import("svelte").Component<Props, {}, "">;
7
+ type Tab = ReturnType<typeof Tab>;
8
+ export default Tab;