specra 0.2.9 → 0.2.11

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.
@@ -42,8 +42,8 @@
42
42
  // Match direct children of this category path
43
43
  if (!docPath.startsWith(categoryPath + '/')) return false;
44
44
  const remaining = docPath.slice(categoryPath.length + 1);
45
- // Only direct children (no further slashes, or is an index)
46
- return !remaining.includes('/') && remaining !== 'index';
45
+ // Only direct children (no further slashes)
46
+ return !remaining.includes('/');
47
47
  })
48
48
  .sort((a, b) => {
49
49
  const posA = a.meta?.sidebar_position ?? 999;
@@ -1,14 +1,7 @@
1
1
  <script lang="ts">
2
2
  import MdxContent from './MdxContent.svelte';
3
3
  import type { Component } from 'svelte';
4
-
5
- interface MdxNode {
6
- type: 'html' | 'component';
7
- content?: string;
8
- name?: string;
9
- props?: Record<string, any>;
10
- children?: MdxNode[];
11
- }
4
+ import type { MdxNode } from '../../mdx.js';
12
5
 
13
6
  interface Props {
14
7
  nodes: MdxNode[];
@@ -0,0 +1,10 @@
1
+ import MdxContent from './MdxContent.svelte';
2
+ import type { Component } from 'svelte';
3
+ import type { MdxNode } from '../../mdx.js';
4
+ interface Props {
5
+ nodes: MdxNode[];
6
+ components: Record<string, Component>;
7
+ }
8
+ declare const MdxContent: Component<Props, {}, "">;
9
+ type MdxContent = ReturnType<typeof MdxContent>;
10
+ export default MdxContent;
@@ -3,11 +3,19 @@
3
3
 
4
4
  interface Props {
5
5
  version?: string;
6
+ product?: string;
6
7
  }
7
8
 
8
- let { version = '' }: Props = $props();
9
+ let { version = '', product = '' }: Props = $props();
9
10
 
10
- const homeLink = $derived(version ? `/${version}` : '/');
11
+ /** URL prefix: /docs/{product}/{version} for named products, /docs/{version} for default */
12
+ const homeLink = $derived(
13
+ product && product !== '_default_' && version
14
+ ? `/docs/${product}/${version}`
15
+ : version
16
+ ? `/docs/${version}`
17
+ : '/'
18
+ );
11
19
  </script>
12
20
 
13
21
  <div class="flex flex-col items-center justify-center min-h-[60vh] text-center px-4">
@@ -1,5 +1,6 @@
1
1
  interface Props {
2
2
  version?: string;
3
+ product?: string;
3
4
  }
4
5
  declare const NotFoundContent: import("svelte").Component<Props, {}, "">;
5
6
  type NotFoundContent = ReturnType<typeof NotFoundContent>;
@@ -134,9 +134,8 @@
134
134
  if (isIndexFile) {
135
135
  rootGroups[groupName].position = doc.sidebar_position ?? 999;
136
136
  rootGroups[groupName].icon = doc.categoryIcon;
137
- } else {
138
- rootGroups[groupName].items.push(doc);
139
137
  }
138
+ rootGroups[groupName].items.push(doc);
140
139
  return;
141
140
  }
142
141
 
@@ -179,17 +178,14 @@
179
178
  if (doc.categoryIcon) {
180
179
  currentLevel[folder].icon = doc.categoryIcon;
181
180
  }
182
- } else {
183
- currentLevel[folder].items.push(doc);
184
181
  }
182
+ currentLevel[folder].items.push(doc);
185
183
  }
186
184
 
187
185
  currentLevel = currentLevel[folder].children;
188
186
  }
189
187
  } else {
190
- if (!isIndexFile) {
191
- standalone.push(doc);
192
- }
188
+ standalone.push(doc);
193
189
  }
194
190
  });
195
191
 
@@ -0,0 +1,22 @@
1
+ /**
2
+ * MDX/mdsvex component map for Specra documentation.
3
+ *
4
+ * In mdsvex, custom components are imported directly in .svx files.
5
+ * This file exports the component map for programmatic use.
6
+ *
7
+ * Usage in .svx files:
8
+ * ```svelte
9
+ * <script>
10
+ * import { Callout, CodeBlock, Tabs, Tab } from 'specra/components'
11
+ * </script>
12
+ *
13
+ * <Callout type="info">This is a callout</Callout>
14
+ * ```
15
+ */
16
+ import type { Component } from 'svelte';
17
+ import { Callout, Accordion, AccordionItem, Tabs, Tab, Image, Video, Card, CardGrid, ImageCard, ImageCardGrid, Steps, Step, Icon, Mermaid, Math, Columns, Column, DocBadge, Tooltip, Frame, CodeBlock, Timeline, TimelineItem, ApiEndpoint, ApiParams, ApiResponse, ApiPlayground, ApiReference } from './components/docs';
18
+ export { Callout, Accordion, AccordionItem, Tabs, Tab, Image, Video, Card, CardGrid, ImageCard, ImageCardGrid, Steps, Step, Icon, Mermaid, Math, Columns, Column, DocBadge, Tooltip, Frame, CodeBlock, Timeline, TimelineItem, ApiEndpoint, ApiParams, ApiResponse, ApiPlayground, ApiReference, };
19
+ /**
20
+ * Component map for passing to layout components that render MDX content.
21
+ */
22
+ export declare const mdxComponents: Record<string, Component>;
@@ -44,22 +44,23 @@ export function validatePathWithinDirectory(filePath, allowedDir) {
44
44
  * These patterns can execute arbitrary code during SSR
45
45
  */
46
46
  const DANGEROUS_PATTERNS = [
47
- // JavaScript execution
48
- /eval\s*\(/gi,
49
- /Function\s*\(/gi,
50
- /import\s*\(/gi,
51
- /require\s*\(/gi,
47
+ // JavaScript execution — require expression context (after { ; = or line start)
48
+ // to avoid false positives on prose like "bulk import (CSV...)" or "fetch (data)"
49
+ /(?:^|[{;=,])\s*eval\s*\(/gim,
50
+ /(?:^|[{;=,])\s*Function\s*\(/gim,
51
+ /(?:^|[{;=,])\s*import\s*\(/gim,
52
+ /(?:^|[{;=,])\s*require\s*\(/gim,
52
53
  // File system access
53
54
  /fs\.[a-z]+/gi,
54
- /readFile/gi,
55
- /writeFile/gi,
55
+ /(?:^|[{;=,])\s*readFile/gim,
56
+ /(?:^|[{;=,])\s*writeFile/gim,
56
57
  /process\.env/gi,
57
- // Network requests during SSR (legitimate client-side usage should use components)
58
- /fetch\s*\(/gi,
58
+ // Network requests during SSR require expression context
59
+ /(?:^|[{;=,])\s*fetch\s*\(/gim,
59
60
  // Dangerous Node.js modules
60
61
  /child_process/gi,
61
- /exec\s*\(/gi,
62
- /spawn\s*\(/gi,
62
+ /(?:^|[{;=,])\s*exec\s*\(/gim,
63
+ /(?:^|[{;=,])\s*spawn\s*\(/gim,
63
64
  // Script tag injection
64
65
  /<script[>\s]/gi,
65
66
  /javascript:/gi,
@@ -79,13 +79,10 @@ export function buildSidebarStructure(docs) {
79
79
  };
80
80
  }
81
81
  if (isIndexFile) {
82
- // Use categoryPosition if available (from _category_.json), otherwise sidebar_position from frontmatter
83
82
  rootGroups[groupName].position = doc.categoryPosition ?? doc.meta.sidebar_position ?? 999;
84
83
  rootGroups[groupName].icon = doc.categoryIcon;
85
84
  }
86
- else {
87
- rootGroups[groupName].items.push(doc);
88
- }
85
+ rootGroups[groupName].items.push(doc);
89
86
  return;
90
87
  }
91
88
  if (pathParts.length > 1) {
@@ -127,17 +124,13 @@ export function buildSidebarStructure(docs) {
127
124
  currentLevel[folder].defaultCollapsed = doc.categoryCollapsed;
128
125
  }
129
126
  }
130
- else {
131
- currentLevel[folder].items.push(doc);
132
- }
127
+ currentLevel[folder].items.push(doc);
133
128
  }
134
129
  currentLevel = currentLevel[folder].children;
135
130
  }
136
131
  }
137
132
  else {
138
- if (!isIndexFile) {
139
- standalone.push(doc);
140
- }
133
+ standalone.push(doc);
141
134
  }
142
135
  });
143
136
  return { rootGroups, standalone };
@@ -159,28 +159,39 @@
159
159
  text-decoration: none;
160
160
  }
161
161
 
162
- /* Link styling - primary color, no underline until hover */
162
+ /* Prose text links - underlined for readability */
163
163
  main .prose a {
164
164
  color: var(--color-primary);
165
- text-decoration: none;
166
- transition: color 0.2s ease, text-decoration 0.2s ease;
165
+ text-decoration: underline;
166
+ text-underline-offset: 3px;
167
+ text-decoration-thickness: 1px;
168
+ text-decoration-color: color-mix(in srgb, var(--color-primary) 40%, transparent);
169
+ transition: text-decoration-color 0.2s ease;
167
170
  }
168
171
 
169
172
  main .prose a:hover {
170
- text-decoration: none !important;
171
- color: var(--color-primary);
173
+ text-decoration-color: var(--color-primary);
172
174
  }
173
175
 
174
- /* Prose links in documentation */
175
176
  .prose a {
176
177
  color: var(--color-primary);
177
- text-decoration: none !important;
178
+ text-decoration: underline;
179
+ text-underline-offset: 3px;
180
+ text-decoration-thickness: 1px;
181
+ text-decoration-color: color-mix(in srgb, var(--color-primary) 40%, transparent);
178
182
  font-weight: 500;
179
183
  }
180
184
 
181
185
  .prose a:hover {
186
+ text-decoration-color: var(--color-primary);
187
+ }
188
+
189
+ /* Navigation/UI links inside prose - no underline */
190
+ .prose .not-prose a,
191
+ .prose nav a,
192
+ .prose [data-doc-nav] a,
193
+ .prose .doc-navigation a {
182
194
  text-decoration: none !important;
183
- color: var(--color-primary);
184
195
  }
185
196
 
186
197
  /* Sidebar links - no underline on hover */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specra",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "description": "A modern documentation library for SvelteKit with built-in versioning, API reference generation, full-text search, and MDX support",
5
5
  "svelte": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",