@alliance-droid/svelte-docs-system 0.0.2 → 0.1.1
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.
- package/README.md +155 -23
- package/dist/components/APITable.svelte.d.ts +21 -0
- package/dist/components/Breadcrumbs.svelte.d.ts +14 -0
- package/dist/components/Callout.svelte.d.ts +15 -0
- package/dist/components/CodeBlock.svelte.d.ts +12 -0
- package/{src/lib → dist}/components/DocLayout.svelte +18 -6
- package/dist/components/DocLayout.svelte.d.ts +18 -0
- package/dist/components/DocsPage.svelte +39 -0
- package/dist/components/DocsPage.svelte.d.ts +8 -0
- package/dist/components/Documentation.svelte +639 -0
- package/dist/components/Footer.svelte.d.ts +10 -0
- package/dist/components/Image.svelte.d.ts +15 -0
- package/{src/lib → dist}/components/Navbar.svelte +4 -4
- package/dist/components/Navbar.svelte.d.ts +10 -0
- package/{src/lib → dist}/components/Search.svelte +2 -2
- package/dist/components/Search.svelte.d.ts +6 -0
- package/{template-starter/src/lib → dist}/components/Sidebar.svelte +2 -2
- package/dist/components/Sidebar.svelte.d.ts +9 -0
- package/dist/components/Tabs.svelte.d.ts +16 -0
- package/dist/config.d.ts +93 -0
- package/dist/config.js +89 -0
- package/dist/configLoader.d.ts +48 -0
- package/dist/configLoader.js +187 -0
- package/dist/configParser.d.ts +27 -0
- package/dist/configParser.js +208 -0
- package/{template-starter/src/lib/index.ts → dist/index.d.ts} +6 -7
- package/dist/index.js +18 -0
- package/dist/navigationBuilder.d.ts +64 -0
- package/dist/navigationBuilder.js +225 -0
- package/dist/plugin.d.ts +30 -0
- package/dist/plugin.js +172 -0
- package/dist/routing.d.ts +48 -0
- package/dist/routing.js +92 -0
- package/dist/stores/i18n.d.ts +20 -0
- package/dist/stores/i18n.js +119 -0
- package/dist/stores/nav.d.ts +20 -0
- package/dist/stores/nav.js +15 -0
- package/dist/stores/search.d.ts +49 -0
- package/dist/stores/search.js +127 -0
- package/dist/stores/theme.d.ts +7 -0
- package/dist/stores/theme.js +152 -0
- package/dist/stores/version.d.ts +18 -0
- package/dist/stores/version.js +93 -0
- package/dist/themeCustomization.d.ts +46 -0
- package/dist/themeCustomization.js +188 -0
- package/dist/utils/highlight.d.ts +13 -0
- package/dist/utils/highlight.js +83 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/markdown.d.ts +40 -0
- package/dist/utils/markdown.js +165 -0
- package/package.json +45 -23
- package/COMPONENTS.md +0 -365
- package/COVERAGE_REPORT.md +0 -663
- package/SEARCH_VERIFICATION.md +0 -229
- package/TEST_SUMMARY.md +0 -344
- package/bin/init.js +0 -821
- package/docs/COMPONENT_LIBRARY_INTEGRATION_REPORT.md +0 -153
- package/docs/DARK_MODE_AUDIT_REPORT.md +0 -403
- package/docs/E2E_TESTS.md +0 -354
- package/docs/TESTING.md +0 -754
- package/docs/THEME_INHERITANCE.md +0 -192
- package/docs/de/index.md +0 -41
- package/docs/en/COMPONENTS.md +0 -443
- package/docs/en/api/examples.md +0 -100
- package/docs/en/api/overview.md +0 -69
- package/docs/en/components/index.md +0 -622
- package/docs/en/config/navigation.md +0 -505
- package/docs/en/config/theme-and-colors.md +0 -395
- package/docs/en/getting-started/integration.md +0 -406
- package/docs/en/guides/common-setups.md +0 -651
- package/docs/en/index.md +0 -243
- package/docs/en/markdown.md +0 -102
- package/docs/en/routing.md +0 -64
- package/docs/en/setup.md +0 -52
- package/docs/en/troubleshooting.md +0 -704
- package/docs/es/index.md +0 -41
- package/docs/fr/index.md +0 -41
- package/docs/ja/index.md +0 -41
- package/pagefind.toml +0 -8
- package/postcss.config.js +0 -5
- package/src/app.css +0 -119
- package/src/app.d.ts +0 -13
- package/src/app.html +0 -11
- package/src/lib/components/APITable.test.ts +0 -153
- package/src/lib/components/Breadcrumbs.test.ts +0 -148
- package/src/lib/components/Callout.test.ts +0 -100
- package/src/lib/components/CodeBlock.test.ts +0 -133
- package/src/lib/components/Image.test.ts +0 -163
- package/src/lib/components/Sidebar.svelte +0 -110
- package/src/lib/components/Tabs.test.ts +0 -102
- package/src/lib/config.test.ts +0 -140
- package/src/lib/config.ts +0 -179
- package/src/lib/configIntegration.test.ts +0 -272
- package/src/lib/configLoader.ts +0 -231
- package/src/lib/configParser.test.ts +0 -217
- package/src/lib/configParser.ts +0 -234
- package/src/lib/index.ts +0 -37
- package/src/lib/integration.test.ts +0 -426
- package/src/lib/navigationBuilder.test.ts +0 -338
- package/src/lib/navigationBuilder.ts +0 -268
- package/src/lib/performance.test.ts +0 -369
- package/src/lib/routing.test.ts +0 -202
- package/src/lib/routing.ts +0 -127
- package/src/lib/search-functionality.test.ts +0 -493
- package/src/lib/stores/i18n.test.ts +0 -180
- package/src/lib/stores/i18n.ts +0 -143
- package/src/lib/stores/nav.ts +0 -36
- package/src/lib/stores/search.test.ts +0 -140
- package/src/lib/stores/search.ts +0 -162
- package/src/lib/stores/theme.test.ts +0 -117
- package/src/lib/stores/theme.ts +0 -167
- package/src/lib/stores/version.test.ts +0 -139
- package/src/lib/stores/version.ts +0 -111
- package/src/lib/themeCustomization.test.ts +0 -223
- package/src/lib/themeCustomization.ts +0 -212
- package/src/lib/utils/highlight.test.ts +0 -136
- package/src/lib/utils/highlight.ts +0 -100
- package/src/lib/utils/index.ts +0 -7
- package/src/lib/utils/markdown.test.ts +0 -357
- package/src/lib/utils/markdown.ts +0 -77
- package/src/routes/+layout.server.ts +0 -1
- package/src/routes/+layout.svelte +0 -29
- package/src/routes/+page.svelte +0 -165
- package/src/routes/quote-demo/+page.svelte +0 -141
- package/static/robots.txt +0 -3
- package/svelte.config.js +0 -15
- package/tailwind.config.ts +0 -55
- package/template-starter/.github/workflows/build.yml +0 -40
- package/template-starter/.github/workflows/deploy-github-pages.yml +0 -47
- package/template-starter/.github/workflows/deploy-netlify.yml +0 -41
- package/template-starter/.github/workflows/deploy-vercel.yml +0 -64
- package/template-starter/NPM-PACKAGE-SETUP.md +0 -233
- package/template-starter/README.md +0 -320
- package/template-starter/docs/_config.json +0 -39
- package/template-starter/docs/api/components.md +0 -257
- package/template-starter/docs/api/overview.md +0 -169
- package/template-starter/docs/guides/configuration.md +0 -145
- package/template-starter/docs/guides/github-pages-deployment.md +0 -254
- package/template-starter/docs/guides/netlify-deployment.md +0 -159
- package/template-starter/docs/guides/vercel-deployment.md +0 -131
- package/template-starter/docs/index.md +0 -49
- package/template-starter/docs/setup.md +0 -149
- package/template-starter/package.json +0 -31
- package/template-starter/pagefind.toml +0 -3
- package/template-starter/postcss.config.js +0 -5
- package/template-starter/src/app.css +0 -34
- package/template-starter/src/app.d.ts +0 -13
- package/template-starter/src/app.html +0 -11
- package/template-starter/src/lib/components/APITable.svelte +0 -120
- package/template-starter/src/lib/components/APITable.test.ts +0 -96
- package/template-starter/src/lib/components/Breadcrumbs.svelte +0 -85
- package/template-starter/src/lib/components/Breadcrumbs.test.ts +0 -82
- package/template-starter/src/lib/components/Callout.svelte +0 -60
- package/template-starter/src/lib/components/Callout.test.ts +0 -91
- package/template-starter/src/lib/components/CodeBlock.svelte +0 -68
- package/template-starter/src/lib/components/CodeBlock.test.ts +0 -62
- package/template-starter/src/lib/components/DocLayout.svelte +0 -84
- package/template-starter/src/lib/components/Footer.svelte +0 -78
- package/template-starter/src/lib/components/Image.svelte +0 -100
- package/template-starter/src/lib/components/Image.test.ts +0 -81
- package/template-starter/src/lib/components/Navbar.svelte +0 -141
- package/template-starter/src/lib/components/Search.svelte +0 -248
- package/template-starter/src/lib/components/Tabs.svelte +0 -48
- package/template-starter/src/lib/components/Tabs.test.ts +0 -89
- package/template-starter/src/routes/+layout.svelte +0 -28
- package/template-starter/src/routes/+page.svelte +0 -92
- package/template-starter/svelte.config.js +0 -17
- package/template-starter/tailwind.config.ts +0 -17
- package/template-starter/tsconfig.json +0 -13
- package/template-starter/vite.config.ts +0 -6
- package/tests/e2e/example.spec.ts +0 -345
- package/tsconfig.json +0 -20
- package/vite.config.ts +0 -6
- package/vitest.config.ts +0 -33
- package/vitest.setup.ts +0 -21
- /package/{src/lib → dist}/assets/favicon.svg +0 -0
- /package/{src/lib → dist}/components/APITable.svelte +0 -0
- /package/{src/lib → dist}/components/Breadcrumbs.svelte +0 -0
- /package/{src/lib → dist}/components/Callout.svelte +0 -0
- /package/{src/lib → dist}/components/CodeBlock.svelte +0 -0
- /package/{src/lib → dist}/components/Footer.svelte +0 -0
- /package/{src/lib → dist}/components/Image.svelte +0 -0
- /package/{src/lib → dist}/components/Tabs.svelte +0 -0
- /package/{src/lib → dist}/svelte-component-library.d.ts +0 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme customization system
|
|
3
|
+
* Generates CSS variables from theme configuration
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Generate CSS custom properties (variables) from theme config
|
|
7
|
+
* These can be injected into the document or stylesheet
|
|
8
|
+
*/
|
|
9
|
+
export function generateCSSVariables(theme) {
|
|
10
|
+
const vars = {};
|
|
11
|
+
// Color variables
|
|
12
|
+
if (theme.primary)
|
|
13
|
+
vars['--color-primary'] = theme.primary;
|
|
14
|
+
if (theme.secondary)
|
|
15
|
+
vars['--color-secondary'] = theme.secondary;
|
|
16
|
+
if (theme.textLight)
|
|
17
|
+
vars['--color-text-light'] = theme.textLight;
|
|
18
|
+
if (theme.textDark)
|
|
19
|
+
vars['--color-text-dark'] = theme.textDark;
|
|
20
|
+
if (theme.bgLight)
|
|
21
|
+
vars['--color-bg-light'] = theme.bgLight;
|
|
22
|
+
if (theme.bgDark)
|
|
23
|
+
vars['--color-bg-dark'] = theme.bgDark;
|
|
24
|
+
if (theme.sidebarBg)
|
|
25
|
+
vars['--color-sidebar-bg'] = theme.sidebarBg;
|
|
26
|
+
if (theme.navbarBg)
|
|
27
|
+
vars['--color-navbar-bg'] = theme.navbarBg;
|
|
28
|
+
if (theme.codeBg)
|
|
29
|
+
vars['--color-code-bg'] = theme.codeBg;
|
|
30
|
+
// Font variables
|
|
31
|
+
if (theme.fontFamily)
|
|
32
|
+
vars['--font-family-body'] = theme.fontFamily;
|
|
33
|
+
if (theme.headingFont)
|
|
34
|
+
vars['--font-family-heading'] = theme.headingFont;
|
|
35
|
+
return vars;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Apply CSS variables to the document root
|
|
39
|
+
*/
|
|
40
|
+
export function applyCSSVariables(vars) {
|
|
41
|
+
if (typeof window === 'undefined') {
|
|
42
|
+
return; // Skip in SSR
|
|
43
|
+
}
|
|
44
|
+
const root = document.documentElement;
|
|
45
|
+
Object.entries(vars).forEach(([key, value]) => {
|
|
46
|
+
root.style.setProperty(key, value);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create a stylesheet string from CSS variables
|
|
51
|
+
* Useful for injecting into <style> tags
|
|
52
|
+
*/
|
|
53
|
+
export function createCSSVariablesStylesheet(vars) {
|
|
54
|
+
const entries = Object.entries(vars)
|
|
55
|
+
.map(([key, value]) => ` ${key}: ${value};`)
|
|
56
|
+
.join('\n');
|
|
57
|
+
return `:root {\n${entries}\n}`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Parse a color value and validate it
|
|
61
|
+
*/
|
|
62
|
+
export function parseColor(color) {
|
|
63
|
+
if (!color || typeof color !== 'string') {
|
|
64
|
+
return { valid: false, error: 'Color must be a non-empty string' };
|
|
65
|
+
}
|
|
66
|
+
const trimmed = color.trim();
|
|
67
|
+
// Hex colors
|
|
68
|
+
if (/^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?([0-9a-fA-F]{2})?$/.test(trimmed)) {
|
|
69
|
+
return { valid: true, value: trimmed };
|
|
70
|
+
}
|
|
71
|
+
// RGB/RGBA
|
|
72
|
+
if (/^rgba?\s*\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}(\s*,\s*[\d.]+)?\s*\)$/.test(trimmed)) {
|
|
73
|
+
return { valid: true, value: trimmed };
|
|
74
|
+
}
|
|
75
|
+
// Named colors (basic list)
|
|
76
|
+
const namedColors = [
|
|
77
|
+
'red',
|
|
78
|
+
'blue',
|
|
79
|
+
'green',
|
|
80
|
+
'white',
|
|
81
|
+
'black',
|
|
82
|
+
'gray',
|
|
83
|
+
'grey',
|
|
84
|
+
'transparent',
|
|
85
|
+
'currentColor',
|
|
86
|
+
];
|
|
87
|
+
if (namedColors.includes(trimmed.toLowerCase())) {
|
|
88
|
+
return { valid: true, value: trimmed };
|
|
89
|
+
}
|
|
90
|
+
// CSS custom property
|
|
91
|
+
if (/^var\s*\(\s*--[a-zA-Z0-9-]+\s*\)$/.test(trimmed)) {
|
|
92
|
+
return { valid: true, value: trimmed };
|
|
93
|
+
}
|
|
94
|
+
return { valid: false, error: `Invalid color format: ${color}` };
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Validate theme configuration
|
|
98
|
+
*/
|
|
99
|
+
export function validateTheme(theme) {
|
|
100
|
+
const errors = [];
|
|
101
|
+
if (!theme || typeof theme !== 'object') {
|
|
102
|
+
return { valid: false, errors: ['Theme must be an object'] };
|
|
103
|
+
}
|
|
104
|
+
// Validate colors
|
|
105
|
+
const colorFields = ['primary', 'secondary', 'textLight', 'textDark', 'bgLight', 'bgDark', 'sidebarBg', 'navbarBg', 'codeBg'];
|
|
106
|
+
colorFields.forEach((field) => {
|
|
107
|
+
if (theme[field] !== undefined) {
|
|
108
|
+
const validation = parseColor(theme[field]);
|
|
109
|
+
if (!validation.valid) {
|
|
110
|
+
errors.push(`${field}: ${validation.error}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
// Validate fonts
|
|
115
|
+
if (theme.fontFamily !== undefined && typeof theme.fontFamily !== 'string') {
|
|
116
|
+
errors.push('fontFamily must be a string');
|
|
117
|
+
}
|
|
118
|
+
if (theme.headingFont !== undefined && typeof theme.headingFont !== 'string') {
|
|
119
|
+
errors.push('headingFont must be a string');
|
|
120
|
+
}
|
|
121
|
+
return { valid: errors.length === 0, errors };
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Merge theme configs, with priority to the first argument
|
|
125
|
+
*/
|
|
126
|
+
export function mergeThemes(primary, secondary) {
|
|
127
|
+
return {
|
|
128
|
+
...secondary,
|
|
129
|
+
...primary,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
export function getThemeTemplate(template = 'default') {
|
|
133
|
+
const templates = {
|
|
134
|
+
default: {
|
|
135
|
+
primary: '#0066cc',
|
|
136
|
+
secondary: '#ff6b6b',
|
|
137
|
+
textLight: '#333333',
|
|
138
|
+
textDark: '#f0f0f0',
|
|
139
|
+
bgLight: '#ffffff',
|
|
140
|
+
bgDark: '#1a1a1a',
|
|
141
|
+
sidebarBg: '#f5f5f5',
|
|
142
|
+
navbarBg: '#ffffff',
|
|
143
|
+
codeBg: '#f4f4f4',
|
|
144
|
+
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
145
|
+
headingFont: 'system-ui, -apple-system, sans-serif',
|
|
146
|
+
},
|
|
147
|
+
dark: {
|
|
148
|
+
primary: '#4da6ff',
|
|
149
|
+
secondary: '#ff9999',
|
|
150
|
+
textLight: '#e0e0e0',
|
|
151
|
+
textDark: '#1a1a1a',
|
|
152
|
+
bgLight: '#1a1a1a',
|
|
153
|
+
bgDark: '#0d0d0d',
|
|
154
|
+
sidebarBg: '#262626',
|
|
155
|
+
navbarBg: '#1a1a1a',
|
|
156
|
+
codeBg: '#2d2d2d',
|
|
157
|
+
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
158
|
+
headingFont: 'system-ui, -apple-system, sans-serif',
|
|
159
|
+
},
|
|
160
|
+
minimal: {
|
|
161
|
+
primary: '#000000',
|
|
162
|
+
secondary: '#666666',
|
|
163
|
+
textLight: '#333333',
|
|
164
|
+
textDark: '#cccccc',
|
|
165
|
+
bgLight: '#ffffff',
|
|
166
|
+
bgDark: '#181818',
|
|
167
|
+
sidebarBg: '#fafafa',
|
|
168
|
+
navbarBg: '#ffffff',
|
|
169
|
+
codeBg: '#eeeeee',
|
|
170
|
+
fontFamily: 'Georgia, serif',
|
|
171
|
+
headingFont: 'Georgia, serif',
|
|
172
|
+
},
|
|
173
|
+
colorful: {
|
|
174
|
+
primary: '#ff006e',
|
|
175
|
+
secondary: '#00d9ff',
|
|
176
|
+
textLight: '#2d3142',
|
|
177
|
+
textDark: '#e0e0e0',
|
|
178
|
+
bgLight: '#fafafa',
|
|
179
|
+
bgDark: '#0d1b2a',
|
|
180
|
+
sidebarBg: '#f0f3ff',
|
|
181
|
+
navbarBg: '#ffffff',
|
|
182
|
+
codeBg: '#f5f5f5',
|
|
183
|
+
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
184
|
+
headingFont: '"Segoe UI", Tahoma, sans-serif',
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
return templates[template] || templates.default;
|
|
188
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Highlight search terms in text
|
|
3
|
+
* Returns HTML with highlighted terms wrapped in <mark> tags
|
|
4
|
+
*/
|
|
5
|
+
export declare function highlightSearchTerms(text: string, searchQuery: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Truncate text to a maximum length and ensure it ends at a word boundary
|
|
8
|
+
*/
|
|
9
|
+
export declare function truncateText(text: string, maxLength?: number): string;
|
|
10
|
+
/**
|
|
11
|
+
* Extract excerpt around search terms
|
|
12
|
+
*/
|
|
13
|
+
export declare function extractExcerpt(text: string, searchQuery: string, maxLength?: number): string;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Highlight search terms in text
|
|
3
|
+
* Returns HTML with highlighted terms wrapped in <mark> tags
|
|
4
|
+
*/
|
|
5
|
+
export function highlightSearchTerms(text, searchQuery) {
|
|
6
|
+
if (!text || !searchQuery.trim()) {
|
|
7
|
+
return text;
|
|
8
|
+
}
|
|
9
|
+
// Split search query into individual terms and escape special regex characters
|
|
10
|
+
const terms = searchQuery
|
|
11
|
+
.trim()
|
|
12
|
+
.split(/\s+/)
|
|
13
|
+
.filter((term) => term.length > 0)
|
|
14
|
+
.map((term) => term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
15
|
+
if (terms.length === 0) {
|
|
16
|
+
return text;
|
|
17
|
+
}
|
|
18
|
+
// Create a regex that matches any of the terms (case-insensitive)
|
|
19
|
+
const regex = new RegExp(`\\b(${terms.join('|')})\\b`, 'gi');
|
|
20
|
+
// Replace matches with highlighted version
|
|
21
|
+
return text.replace(regex, '<span class="search-highlight">$1</span>');
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Truncate text to a maximum length and ensure it ends at a word boundary
|
|
25
|
+
*/
|
|
26
|
+
export function truncateText(text, maxLength = 150) {
|
|
27
|
+
if (text.length <= maxLength) {
|
|
28
|
+
return text;
|
|
29
|
+
}
|
|
30
|
+
// Truncate and find the last space
|
|
31
|
+
const truncated = text.substring(0, maxLength);
|
|
32
|
+
const lastSpace = truncated.lastIndexOf(' ');
|
|
33
|
+
if (lastSpace > maxLength * 0.7) {
|
|
34
|
+
// If the last space is relatively close to the end, use it
|
|
35
|
+
return truncated.substring(0, lastSpace) + '...';
|
|
36
|
+
}
|
|
37
|
+
// Otherwise just truncate and add ellipsis
|
|
38
|
+
return truncated + '...';
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Extract excerpt around search terms
|
|
42
|
+
*/
|
|
43
|
+
export function extractExcerpt(text, searchQuery, maxLength = 200) {
|
|
44
|
+
if (!text || !searchQuery.trim()) {
|
|
45
|
+
return truncateText(text, maxLength);
|
|
46
|
+
}
|
|
47
|
+
const searchTerms = searchQuery
|
|
48
|
+
.trim()
|
|
49
|
+
.split(/\s+/)
|
|
50
|
+
.filter((term) => term.length > 0)
|
|
51
|
+
.map((term) => term.toLowerCase());
|
|
52
|
+
// Find the position of the first search term in the text
|
|
53
|
+
let earliestIndex = text.length;
|
|
54
|
+
for (const term of searchTerms) {
|
|
55
|
+
const index = text.toLowerCase().indexOf(term);
|
|
56
|
+
if (index !== -1 && index < earliestIndex) {
|
|
57
|
+
earliestIndex = index;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Extract context around the search term
|
|
61
|
+
let startIndex = Math.max(0, earliestIndex - 50);
|
|
62
|
+
let endIndex = Math.min(text.length, startIndex + maxLength);
|
|
63
|
+
// Adjust if we're too close to the end
|
|
64
|
+
if (endIndex === text.length && startIndex > 0) {
|
|
65
|
+
startIndex = Math.max(0, endIndex - maxLength);
|
|
66
|
+
}
|
|
67
|
+
let excerpt = text.substring(startIndex, endIndex);
|
|
68
|
+
// Remove partial words at the beginning
|
|
69
|
+
if (startIndex > 0) {
|
|
70
|
+
const firstSpace = excerpt.indexOf(' ');
|
|
71
|
+
if (firstSpace !== -1 && firstSpace < 20) {
|
|
72
|
+
excerpt = excerpt.substring(firstSpace + 1);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Add ellipsis if needed
|
|
76
|
+
if (startIndex > 0) {
|
|
77
|
+
excerpt = '...' + excerpt;
|
|
78
|
+
}
|
|
79
|
+
if (endIndex < text.length) {
|
|
80
|
+
excerpt = excerpt + '...';
|
|
81
|
+
}
|
|
82
|
+
return excerpt;
|
|
83
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { renderMarkdown, renderMarkdownSync, renderMarkdownWithState, createLoadingResult, extractMetadata, getExcerpt, type MarkdownMetadata, type MarkdownResult, type MarkdownLoadingResult } from './markdown.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { renderMarkdown, renderMarkdownSync, renderMarkdownWithState, createLoadingResult, extractMetadata, getExcerpt } from './markdown.js';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface MarkdownMetadata {
|
|
2
|
+
title?: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
author?: string;
|
|
5
|
+
date?: string;
|
|
6
|
+
order?: number;
|
|
7
|
+
[key: string]: any;
|
|
8
|
+
}
|
|
9
|
+
export interface MarkdownResult {
|
|
10
|
+
html: string;
|
|
11
|
+
metadata: MarkdownMetadata;
|
|
12
|
+
}
|
|
13
|
+
export interface MarkdownLoadingResult extends MarkdownResult {
|
|
14
|
+
loading: boolean;
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Create a loading result
|
|
19
|
+
*/
|
|
20
|
+
export declare function createLoadingResult(): MarkdownLoadingResult;
|
|
21
|
+
/**
|
|
22
|
+
* Render markdown synchronously (browser-safe)
|
|
23
|
+
*/
|
|
24
|
+
export declare function renderMarkdownSync(content: unknown): MarkdownResult;
|
|
25
|
+
/**
|
|
26
|
+
* Render markdown (async version, browser-safe)
|
|
27
|
+
*/
|
|
28
|
+
export declare function renderMarkdown(content: unknown): MarkdownResult;
|
|
29
|
+
/**
|
|
30
|
+
* Render markdown with loading state
|
|
31
|
+
*/
|
|
32
|
+
export declare function renderMarkdownWithState(content: unknown): MarkdownLoadingResult;
|
|
33
|
+
/**
|
|
34
|
+
* Extract metadata from markdown content
|
|
35
|
+
*/
|
|
36
|
+
export declare function extractMetadata(content: string): MarkdownMetadata;
|
|
37
|
+
/**
|
|
38
|
+
* Get excerpt from markdown content
|
|
39
|
+
*/
|
|
40
|
+
export declare function getExcerpt(content: string, wordLimit?: number): string;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { marked } from 'marked';
|
|
2
|
+
/**
|
|
3
|
+
* Browser-safe frontmatter parser (no Buffer dependency)
|
|
4
|
+
*/
|
|
5
|
+
function parseFrontmatter(content) {
|
|
6
|
+
const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/;
|
|
7
|
+
const match = content.match(frontmatterRegex);
|
|
8
|
+
if (!match) {
|
|
9
|
+
return { data: {}, content };
|
|
10
|
+
}
|
|
11
|
+
const frontmatterStr = match[1];
|
|
12
|
+
const markdownContent = match[2];
|
|
13
|
+
// Parse YAML-like frontmatter (simple key: value pairs)
|
|
14
|
+
const data = {};
|
|
15
|
+
const lines = frontmatterStr.split('\n');
|
|
16
|
+
for (const line of lines) {
|
|
17
|
+
const colonIndex = line.indexOf(':');
|
|
18
|
+
if (colonIndex > 0) {
|
|
19
|
+
const key = line.slice(0, colonIndex).trim();
|
|
20
|
+
let value = line.slice(colonIndex + 1).trim();
|
|
21
|
+
// Remove quotes if present
|
|
22
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
23
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
24
|
+
value = value.slice(1, -1);
|
|
25
|
+
}
|
|
26
|
+
// Parse numbers
|
|
27
|
+
if (/^\d+$/.test(value)) {
|
|
28
|
+
value = parseInt(value, 10);
|
|
29
|
+
}
|
|
30
|
+
else if (/^\d+\.\d+$/.test(value)) {
|
|
31
|
+
value = parseFloat(value);
|
|
32
|
+
}
|
|
33
|
+
// Parse booleans
|
|
34
|
+
if (value === 'true')
|
|
35
|
+
value = true;
|
|
36
|
+
if (value === 'false')
|
|
37
|
+
value = false;
|
|
38
|
+
data[key] = value;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { data, content: markdownContent };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Create a custom renderer that escapes raw HTML
|
|
45
|
+
*/
|
|
46
|
+
function createSafeRenderer() {
|
|
47
|
+
const renderer = new marked.Renderer();
|
|
48
|
+
renderer.html = function (token) {
|
|
49
|
+
const html = token.text;
|
|
50
|
+
const escaped = html
|
|
51
|
+
.replace(/&/g, '&')
|
|
52
|
+
.replace(/</g, '<')
|
|
53
|
+
.replace(/>/g, '>')
|
|
54
|
+
.replace(/"/g, '"')
|
|
55
|
+
.replace(/'/g, ''');
|
|
56
|
+
return `<p>${escaped}</p>\n`;
|
|
57
|
+
};
|
|
58
|
+
return renderer;
|
|
59
|
+
}
|
|
60
|
+
const markedOptions = {
|
|
61
|
+
breaks: true,
|
|
62
|
+
gfm: true,
|
|
63
|
+
async: false,
|
|
64
|
+
renderer: createSafeRenderer()
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Validate content input
|
|
68
|
+
*/
|
|
69
|
+
function validateContent(content) {
|
|
70
|
+
if (content === undefined || content === null) {
|
|
71
|
+
throw new Error('Content is undefined or null');
|
|
72
|
+
}
|
|
73
|
+
if (typeof content !== 'string') {
|
|
74
|
+
throw new Error(`Content must be a string, received ${typeof content}`);
|
|
75
|
+
}
|
|
76
|
+
return content;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Create a loading result
|
|
80
|
+
*/
|
|
81
|
+
export function createLoadingResult() {
|
|
82
|
+
return {
|
|
83
|
+
html: '',
|
|
84
|
+
metadata: {},
|
|
85
|
+
loading: true
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Render markdown synchronously (browser-safe)
|
|
90
|
+
*/
|
|
91
|
+
export function renderMarkdownSync(content) {
|
|
92
|
+
try {
|
|
93
|
+
const validContent = validateContent(content);
|
|
94
|
+
if (validContent.trim().length === 0) {
|
|
95
|
+
return { html: '', metadata: {} };
|
|
96
|
+
}
|
|
97
|
+
const { data, content: markdownContent } = parseFrontmatter(validContent);
|
|
98
|
+
const html = marked.parse(markdownContent, markedOptions);
|
|
99
|
+
return {
|
|
100
|
+
html: html || '',
|
|
101
|
+
metadata: data
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.error('Error rendering markdown:', error);
|
|
106
|
+
return {
|
|
107
|
+
html: `<p class="markdown-error">Error: ${error instanceof Error ? error.message : 'Unknown error'}</p>`,
|
|
108
|
+
metadata: {}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Render markdown (async version, browser-safe)
|
|
114
|
+
*/
|
|
115
|
+
export function renderMarkdown(content) {
|
|
116
|
+
return renderMarkdownSync(content);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Render markdown with loading state
|
|
120
|
+
*/
|
|
121
|
+
export function renderMarkdownWithState(content) {
|
|
122
|
+
const result = renderMarkdownSync(content);
|
|
123
|
+
return { ...result, loading: false };
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Extract metadata from markdown content
|
|
127
|
+
*/
|
|
128
|
+
export function extractMetadata(content) {
|
|
129
|
+
try {
|
|
130
|
+
const { data } = parseFrontmatter(content);
|
|
131
|
+
return data;
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
console.error('Error extracting metadata:', error);
|
|
135
|
+
return {};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get excerpt from markdown content
|
|
140
|
+
*/
|
|
141
|
+
export function getExcerpt(content, wordLimit = 150) {
|
|
142
|
+
try {
|
|
143
|
+
if (!content || content.trim().length === 0) {
|
|
144
|
+
return '';
|
|
145
|
+
}
|
|
146
|
+
const { content: markdownContent } = parseFrontmatter(content);
|
|
147
|
+
const text = markdownContent
|
|
148
|
+
.replace(/#{1,6}\s/g, '')
|
|
149
|
+
.replace(/[*_]{1,2}(.*?)[*_]{1,2}/g, '$1')
|
|
150
|
+
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
|
|
151
|
+
.replace(/`([^`]+)`/g, '$1')
|
|
152
|
+
.replace(/^>\s/gm, '')
|
|
153
|
+
.trim();
|
|
154
|
+
const words = text.split(/\s+/).filter(word => word.length > 0);
|
|
155
|
+
if (words.length === 0) {
|
|
156
|
+
return '';
|
|
157
|
+
}
|
|
158
|
+
const excerpt = words.slice(0, wordLimit).join(' ');
|
|
159
|
+
return excerpt.length < text.length ? excerpt + '...' : excerpt;
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
console.error('Error extracting excerpt:', error);
|
|
163
|
+
return '';
|
|
164
|
+
}
|
|
165
|
+
}
|
package/package.json
CHANGED
|
@@ -1,44 +1,66 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alliance-droid/svelte-docs-system",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"
|
|
6
|
-
|
|
5
|
+
"description": "Drop-in documentation system for SvelteKit - just add markdown files",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"svelte": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./plugin": {
|
|
15
|
+
"types": "./dist/plugin.d.ts",
|
|
16
|
+
"default": "./dist/plugin.js"
|
|
17
|
+
}
|
|
7
18
|
},
|
|
19
|
+
"svelte": "./dist/index.js",
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"!dist/**/*.test.*"
|
|
23
|
+
],
|
|
8
24
|
"scripts": {
|
|
9
25
|
"dev": "vite dev",
|
|
10
|
-
"build": "vite build &&
|
|
26
|
+
"build": "vite build && npm run package",
|
|
27
|
+
"package": "svelte-kit sync && svelte-package -o dist",
|
|
11
28
|
"preview": "vite preview",
|
|
12
29
|
"prepare": "svelte-kit sync || echo ''",
|
|
30
|
+
"prepublishOnly": "npm run package",
|
|
13
31
|
"test": "vitest",
|
|
14
|
-
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json"
|
|
15
|
-
|
|
32
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"svelte": "^5.0.0",
|
|
36
|
+
"@sveltejs/kit": "^2.0.0"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"gray-matter": "^4.0.3",
|
|
40
|
+
"marked": "^17.0.1"
|
|
16
41
|
},
|
|
17
42
|
"devDependencies": {
|
|
18
43
|
"@sveltejs/adapter-auto": "^7.0.0",
|
|
19
|
-
"@sveltejs/adapter-static": "^3.0.10",
|
|
20
|
-
"@sveltejs/adapter-vercel": "^6.3.1",
|
|
21
44
|
"@sveltejs/kit": "^2.50.1",
|
|
45
|
+
"@sveltejs/package": "^2.3.10",
|
|
22
46
|
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
23
|
-
"@
|
|
24
|
-
"@testing-library/user-event": "^14.6.1",
|
|
25
|
-
"autoprefixer": "^10.4.24",
|
|
26
|
-
"happy-dom": "^20.5.0",
|
|
27
|
-
"jsdom": "^28.0.0",
|
|
28
|
-
"pagefind": "^1.4.0",
|
|
29
|
-
"postcss": "^8.5.6",
|
|
47
|
+
"@types/node": "^22.15.29",
|
|
30
48
|
"svelte": "^5.48.2",
|
|
31
49
|
"svelte-check": "^4.3.5",
|
|
32
|
-
"tailwindcss": "^4.1.18",
|
|
33
50
|
"typescript": "^5.9.3",
|
|
34
51
|
"vite": "^7.3.1",
|
|
35
52
|
"vitest": "^4.0.18"
|
|
36
53
|
},
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
54
|
+
"keywords": [
|
|
55
|
+
"svelte",
|
|
56
|
+
"sveltekit",
|
|
57
|
+
"documentation",
|
|
58
|
+
"markdown",
|
|
59
|
+
"docs"
|
|
60
|
+
],
|
|
61
|
+
"repository": {
|
|
62
|
+
"type": "git",
|
|
63
|
+
"url": "https://github.com/alliance-droid/svelte-docs-system"
|
|
64
|
+
},
|
|
65
|
+
"license": "MIT"
|
|
44
66
|
}
|