@alliance-droid/svelte-docs-system 0.0.2 → 0.1.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 (185) hide show
  1. package/README.md +155 -23
  2. package/dist/components/APITable.svelte.d.ts +21 -0
  3. package/dist/components/Breadcrumbs.svelte.d.ts +14 -0
  4. package/dist/components/Callout.svelte.d.ts +15 -0
  5. package/dist/components/CodeBlock.svelte.d.ts +12 -0
  6. package/{src/lib → dist}/components/DocLayout.svelte +18 -6
  7. package/dist/components/DocLayout.svelte.d.ts +18 -0
  8. package/dist/components/DocsPage.svelte +39 -0
  9. package/dist/components/DocsPage.svelte.d.ts +8 -0
  10. package/dist/components/Documentation.svelte +639 -0
  11. package/dist/components/Footer.svelte.d.ts +10 -0
  12. package/dist/components/Image.svelte.d.ts +15 -0
  13. package/{src/lib → dist}/components/Navbar.svelte +4 -4
  14. package/dist/components/Navbar.svelte.d.ts +10 -0
  15. package/{src/lib → dist}/components/Search.svelte +2 -2
  16. package/dist/components/Search.svelte.d.ts +6 -0
  17. package/{template-starter/src/lib → dist}/components/Sidebar.svelte +2 -2
  18. package/dist/components/Sidebar.svelte.d.ts +9 -0
  19. package/dist/components/Tabs.svelte.d.ts +16 -0
  20. package/dist/config.d.ts +93 -0
  21. package/dist/config.js +89 -0
  22. package/dist/configLoader.d.ts +48 -0
  23. package/dist/configLoader.js +187 -0
  24. package/dist/configParser.d.ts +27 -0
  25. package/dist/configParser.js +208 -0
  26. package/{template-starter/src/lib/index.ts → dist/index.d.ts} +6 -7
  27. package/dist/index.js +18 -0
  28. package/dist/navigationBuilder.d.ts +64 -0
  29. package/dist/navigationBuilder.js +225 -0
  30. package/dist/plugin.d.ts +30 -0
  31. package/dist/plugin.js +172 -0
  32. package/dist/routing.d.ts +48 -0
  33. package/dist/routing.js +92 -0
  34. package/dist/stores/i18n.d.ts +20 -0
  35. package/dist/stores/i18n.js +119 -0
  36. package/dist/stores/nav.d.ts +20 -0
  37. package/dist/stores/nav.js +15 -0
  38. package/dist/stores/search.d.ts +49 -0
  39. package/dist/stores/search.js +127 -0
  40. package/dist/stores/theme.d.ts +7 -0
  41. package/dist/stores/theme.js +152 -0
  42. package/dist/stores/version.d.ts +18 -0
  43. package/dist/stores/version.js +93 -0
  44. package/dist/themeCustomization.d.ts +46 -0
  45. package/dist/themeCustomization.js +188 -0
  46. package/dist/utils/highlight.d.ts +13 -0
  47. package/dist/utils/highlight.js +83 -0
  48. package/dist/utils/index.d.ts +1 -0
  49. package/dist/utils/index.js +1 -0
  50. package/dist/utils/markdown.d.ts +40 -0
  51. package/dist/utils/markdown.js +165 -0
  52. package/package.json +44 -23
  53. package/COMPONENTS.md +0 -365
  54. package/COVERAGE_REPORT.md +0 -663
  55. package/SEARCH_VERIFICATION.md +0 -229
  56. package/TEST_SUMMARY.md +0 -344
  57. package/bin/init.js +0 -821
  58. package/docs/COMPONENT_LIBRARY_INTEGRATION_REPORT.md +0 -153
  59. package/docs/DARK_MODE_AUDIT_REPORT.md +0 -403
  60. package/docs/E2E_TESTS.md +0 -354
  61. package/docs/TESTING.md +0 -754
  62. package/docs/THEME_INHERITANCE.md +0 -192
  63. package/docs/de/index.md +0 -41
  64. package/docs/en/COMPONENTS.md +0 -443
  65. package/docs/en/api/examples.md +0 -100
  66. package/docs/en/api/overview.md +0 -69
  67. package/docs/en/components/index.md +0 -622
  68. package/docs/en/config/navigation.md +0 -505
  69. package/docs/en/config/theme-and-colors.md +0 -395
  70. package/docs/en/getting-started/integration.md +0 -406
  71. package/docs/en/guides/common-setups.md +0 -651
  72. package/docs/en/index.md +0 -243
  73. package/docs/en/markdown.md +0 -102
  74. package/docs/en/routing.md +0 -64
  75. package/docs/en/setup.md +0 -52
  76. package/docs/en/troubleshooting.md +0 -704
  77. package/docs/es/index.md +0 -41
  78. package/docs/fr/index.md +0 -41
  79. package/docs/ja/index.md +0 -41
  80. package/pagefind.toml +0 -8
  81. package/postcss.config.js +0 -5
  82. package/src/app.css +0 -119
  83. package/src/app.d.ts +0 -13
  84. package/src/app.html +0 -11
  85. package/src/lib/components/APITable.test.ts +0 -153
  86. package/src/lib/components/Breadcrumbs.test.ts +0 -148
  87. package/src/lib/components/Callout.test.ts +0 -100
  88. package/src/lib/components/CodeBlock.test.ts +0 -133
  89. package/src/lib/components/Image.test.ts +0 -163
  90. package/src/lib/components/Sidebar.svelte +0 -110
  91. package/src/lib/components/Tabs.test.ts +0 -102
  92. package/src/lib/config.test.ts +0 -140
  93. package/src/lib/config.ts +0 -179
  94. package/src/lib/configIntegration.test.ts +0 -272
  95. package/src/lib/configLoader.ts +0 -231
  96. package/src/lib/configParser.test.ts +0 -217
  97. package/src/lib/configParser.ts +0 -234
  98. package/src/lib/index.ts +0 -37
  99. package/src/lib/integration.test.ts +0 -426
  100. package/src/lib/navigationBuilder.test.ts +0 -338
  101. package/src/lib/navigationBuilder.ts +0 -268
  102. package/src/lib/performance.test.ts +0 -369
  103. package/src/lib/routing.test.ts +0 -202
  104. package/src/lib/routing.ts +0 -127
  105. package/src/lib/search-functionality.test.ts +0 -493
  106. package/src/lib/stores/i18n.test.ts +0 -180
  107. package/src/lib/stores/i18n.ts +0 -143
  108. package/src/lib/stores/nav.ts +0 -36
  109. package/src/lib/stores/search.test.ts +0 -140
  110. package/src/lib/stores/search.ts +0 -162
  111. package/src/lib/stores/theme.test.ts +0 -117
  112. package/src/lib/stores/theme.ts +0 -167
  113. package/src/lib/stores/version.test.ts +0 -139
  114. package/src/lib/stores/version.ts +0 -111
  115. package/src/lib/themeCustomization.test.ts +0 -223
  116. package/src/lib/themeCustomization.ts +0 -212
  117. package/src/lib/utils/highlight.test.ts +0 -136
  118. package/src/lib/utils/highlight.ts +0 -100
  119. package/src/lib/utils/index.ts +0 -7
  120. package/src/lib/utils/markdown.test.ts +0 -357
  121. package/src/lib/utils/markdown.ts +0 -77
  122. package/src/routes/+layout.server.ts +0 -1
  123. package/src/routes/+layout.svelte +0 -29
  124. package/src/routes/+page.svelte +0 -165
  125. package/src/routes/quote-demo/+page.svelte +0 -141
  126. package/static/robots.txt +0 -3
  127. package/svelte.config.js +0 -15
  128. package/tailwind.config.ts +0 -55
  129. package/template-starter/.github/workflows/build.yml +0 -40
  130. package/template-starter/.github/workflows/deploy-github-pages.yml +0 -47
  131. package/template-starter/.github/workflows/deploy-netlify.yml +0 -41
  132. package/template-starter/.github/workflows/deploy-vercel.yml +0 -64
  133. package/template-starter/NPM-PACKAGE-SETUP.md +0 -233
  134. package/template-starter/README.md +0 -320
  135. package/template-starter/docs/_config.json +0 -39
  136. package/template-starter/docs/api/components.md +0 -257
  137. package/template-starter/docs/api/overview.md +0 -169
  138. package/template-starter/docs/guides/configuration.md +0 -145
  139. package/template-starter/docs/guides/github-pages-deployment.md +0 -254
  140. package/template-starter/docs/guides/netlify-deployment.md +0 -159
  141. package/template-starter/docs/guides/vercel-deployment.md +0 -131
  142. package/template-starter/docs/index.md +0 -49
  143. package/template-starter/docs/setup.md +0 -149
  144. package/template-starter/package.json +0 -31
  145. package/template-starter/pagefind.toml +0 -3
  146. package/template-starter/postcss.config.js +0 -5
  147. package/template-starter/src/app.css +0 -34
  148. package/template-starter/src/app.d.ts +0 -13
  149. package/template-starter/src/app.html +0 -11
  150. package/template-starter/src/lib/components/APITable.svelte +0 -120
  151. package/template-starter/src/lib/components/APITable.test.ts +0 -96
  152. package/template-starter/src/lib/components/Breadcrumbs.svelte +0 -85
  153. package/template-starter/src/lib/components/Breadcrumbs.test.ts +0 -82
  154. package/template-starter/src/lib/components/Callout.svelte +0 -60
  155. package/template-starter/src/lib/components/Callout.test.ts +0 -91
  156. package/template-starter/src/lib/components/CodeBlock.svelte +0 -68
  157. package/template-starter/src/lib/components/CodeBlock.test.ts +0 -62
  158. package/template-starter/src/lib/components/DocLayout.svelte +0 -84
  159. package/template-starter/src/lib/components/Footer.svelte +0 -78
  160. package/template-starter/src/lib/components/Image.svelte +0 -100
  161. package/template-starter/src/lib/components/Image.test.ts +0 -81
  162. package/template-starter/src/lib/components/Navbar.svelte +0 -141
  163. package/template-starter/src/lib/components/Search.svelte +0 -248
  164. package/template-starter/src/lib/components/Tabs.svelte +0 -48
  165. package/template-starter/src/lib/components/Tabs.test.ts +0 -89
  166. package/template-starter/src/routes/+layout.svelte +0 -28
  167. package/template-starter/src/routes/+page.svelte +0 -92
  168. package/template-starter/svelte.config.js +0 -17
  169. package/template-starter/tailwind.config.ts +0 -17
  170. package/template-starter/tsconfig.json +0 -13
  171. package/template-starter/vite.config.ts +0 -6
  172. package/tests/e2e/example.spec.ts +0 -345
  173. package/tsconfig.json +0 -20
  174. package/vite.config.ts +0 -6
  175. package/vitest.config.ts +0 -33
  176. package/vitest.setup.ts +0 -21
  177. /package/{src/lib → dist}/assets/favicon.svg +0 -0
  178. /package/{src/lib → dist}/components/APITable.svelte +0 -0
  179. /package/{src/lib → dist}/components/Breadcrumbs.svelte +0 -0
  180. /package/{src/lib → dist}/components/Callout.svelte +0 -0
  181. /package/{src/lib → dist}/components/CodeBlock.svelte +0 -0
  182. /package/{src/lib → dist}/components/Footer.svelte +0 -0
  183. /package/{src/lib → dist}/components/Image.svelte +0 -0
  184. /package/{src/lib → dist}/components/Tabs.svelte +0 -0
  185. /package/{src/lib → dist}/svelte-component-library.d.ts +0 -0
@@ -1,212 +0,0 @@
1
- /**
2
- * Theme customization system
3
- * Generates CSS variables from theme configuration
4
- */
5
-
6
- import type { ThemeConfig } from './config';
7
-
8
- export interface CSSVariables {
9
- [key: string]: string;
10
- }
11
-
12
- /**
13
- * Generate CSS custom properties (variables) from theme config
14
- * These can be injected into the document or stylesheet
15
- */
16
- export function generateCSSVariables(theme: ThemeConfig): CSSVariables {
17
- const vars: CSSVariables = {};
18
-
19
- // Color variables
20
- if (theme.primary) vars['--color-primary'] = theme.primary;
21
- if (theme.secondary) vars['--color-secondary'] = theme.secondary;
22
- if (theme.textLight) vars['--color-text-light'] = theme.textLight;
23
- if (theme.textDark) vars['--color-text-dark'] = theme.textDark;
24
- if (theme.bgLight) vars['--color-bg-light'] = theme.bgLight;
25
- if (theme.bgDark) vars['--color-bg-dark'] = theme.bgDark;
26
- if (theme.sidebarBg) vars['--color-sidebar-bg'] = theme.sidebarBg;
27
- if (theme.navbarBg) vars['--color-navbar-bg'] = theme.navbarBg;
28
- if (theme.codeBg) vars['--color-code-bg'] = theme.codeBg;
29
-
30
- // Font variables
31
- if (theme.fontFamily) vars['--font-family-body'] = theme.fontFamily;
32
- if (theme.headingFont) vars['--font-family-heading'] = theme.headingFont;
33
-
34
- return vars;
35
- }
36
-
37
- /**
38
- * Apply CSS variables to the document root
39
- */
40
- export function applyCSSVariables(vars: CSSVariables): void {
41
- if (typeof window === 'undefined') {
42
- return; // Skip in SSR
43
- }
44
-
45
- const root = document.documentElement;
46
- Object.entries(vars).forEach(([key, value]) => {
47
- root.style.setProperty(key, value);
48
- });
49
- }
50
-
51
- /**
52
- * Create a stylesheet string from CSS variables
53
- * Useful for injecting into <style> tags
54
- */
55
- export function createCSSVariablesStylesheet(vars: CSSVariables): string {
56
- const entries = Object.entries(vars)
57
- .map(([key, value]) => ` ${key}: ${value};`)
58
- .join('\n');
59
-
60
- return `:root {\n${entries}\n}`;
61
- }
62
-
63
- /**
64
- * Parse a color value and validate it
65
- */
66
- export function parseColor(color: string): { valid: boolean; value?: string; error?: string } {
67
- if (!color || typeof color !== 'string') {
68
- return { valid: false, error: 'Color must be a non-empty string' };
69
- }
70
-
71
- const trimmed = color.trim();
72
-
73
- // Hex colors
74
- if (/^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?([0-9a-fA-F]{2})?$/.test(trimmed)) {
75
- return { valid: true, value: trimmed };
76
- }
77
-
78
- // RGB/RGBA
79
- if (/^rgba?\s*\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}(\s*,\s*[\d.]+)?\s*\)$/.test(trimmed)) {
80
- return { valid: true, value: trimmed };
81
- }
82
-
83
- // Named colors (basic list)
84
- const namedColors = [
85
- 'red',
86
- 'blue',
87
- 'green',
88
- 'white',
89
- 'black',
90
- 'gray',
91
- 'grey',
92
- 'transparent',
93
- 'currentColor',
94
- ];
95
- if (namedColors.includes(trimmed.toLowerCase())) {
96
- return { valid: true, value: trimmed };
97
- }
98
-
99
- // CSS custom property
100
- if (/^var\s*\(\s*--[a-zA-Z0-9-]+\s*\)$/.test(trimmed)) {
101
- return { valid: true, value: trimmed };
102
- }
103
-
104
- return { valid: false, error: `Invalid color format: ${color}` };
105
- }
106
-
107
- /**
108
- * Validate theme configuration
109
- */
110
- export function validateTheme(theme: ThemeConfig): { valid: boolean; errors: string[] } {
111
- const errors: string[] = [];
112
-
113
- if (!theme || typeof theme !== 'object') {
114
- return { valid: false, errors: ['Theme must be an object'] };
115
- }
116
-
117
- // Validate colors
118
- const colorFields = ['primary', 'secondary', 'textLight', 'textDark', 'bgLight', 'bgDark', 'sidebarBg', 'navbarBg', 'codeBg'] as const;
119
- colorFields.forEach((field) => {
120
- if (theme[field] !== undefined) {
121
- const validation = parseColor(theme[field]!);
122
- if (!validation.valid) {
123
- errors.push(`${field}: ${validation.error}`);
124
- }
125
- }
126
- });
127
-
128
- // Validate fonts
129
- if (theme.fontFamily !== undefined && typeof theme.fontFamily !== 'string') {
130
- errors.push('fontFamily must be a string');
131
- }
132
-
133
- if (theme.headingFont !== undefined && typeof theme.headingFont !== 'string') {
134
- errors.push('headingFont must be a string');
135
- }
136
-
137
- return { valid: errors.length === 0, errors };
138
- }
139
-
140
- /**
141
- * Merge theme configs, with priority to the first argument
142
- */
143
- export function mergeThemes(primary: ThemeConfig, secondary: ThemeConfig): ThemeConfig {
144
- return {
145
- ...secondary,
146
- ...primary,
147
- };
148
- }
149
-
150
- /**
151
- * Get a predefined theme template
152
- */
153
- export type ThemeTemplate = 'default' | 'dark' | 'minimal' | 'colorful';
154
-
155
- export function getThemeTemplate(template: ThemeTemplate = 'default'): ThemeConfig {
156
- const templates: Record<ThemeTemplate, ThemeConfig> = {
157
- default: {
158
- primary: '#0066cc',
159
- secondary: '#ff6b6b',
160
- textLight: '#333333',
161
- textDark: '#f0f0f0',
162
- bgLight: '#ffffff',
163
- bgDark: '#1a1a1a',
164
- sidebarBg: '#f5f5f5',
165
- navbarBg: '#ffffff',
166
- codeBg: '#f4f4f4',
167
- fontFamily: 'system-ui, -apple-system, sans-serif',
168
- headingFont: 'system-ui, -apple-system, sans-serif',
169
- },
170
- dark: {
171
- primary: '#4da6ff',
172
- secondary: '#ff9999',
173
- textLight: '#e0e0e0',
174
- textDark: '#1a1a1a',
175
- bgLight: '#1a1a1a',
176
- bgDark: '#0d0d0d',
177
- sidebarBg: '#262626',
178
- navbarBg: '#1a1a1a',
179
- codeBg: '#2d2d2d',
180
- fontFamily: 'system-ui, -apple-system, sans-serif',
181
- headingFont: 'system-ui, -apple-system, sans-serif',
182
- },
183
- minimal: {
184
- primary: '#000000',
185
- secondary: '#666666',
186
- textLight: '#333333',
187
- textDark: '#cccccc',
188
- bgLight: '#ffffff',
189
- bgDark: '#181818',
190
- sidebarBg: '#fafafa',
191
- navbarBg: '#ffffff',
192
- codeBg: '#eeeeee',
193
- fontFamily: 'Georgia, serif',
194
- headingFont: 'Georgia, serif',
195
- },
196
- colorful: {
197
- primary: '#ff006e',
198
- secondary: '#00d9ff',
199
- textLight: '#2d3142',
200
- textDark: '#e0e0e0',
201
- bgLight: '#fafafa',
202
- bgDark: '#0d1b2a',
203
- sidebarBg: '#f0f3ff',
204
- navbarBg: '#ffffff',
205
- codeBg: '#f5f5f5',
206
- fontFamily: 'system-ui, -apple-system, sans-serif',
207
- headingFont: '"Segoe UI", Tahoma, sans-serif',
208
- },
209
- };
210
-
211
- return templates[template] || templates.default;
212
- }
@@ -1,136 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { highlightSearchTerms, truncateText, extractExcerpt } from './highlight';
3
-
4
- describe('Highlight Utilities', () => {
5
- describe('highlightSearchTerms', () => {
6
- it('should highlight single search term', () => {
7
- const text = 'This is a test sentence';
8
- const result = highlightSearchTerms(text, 'test');
9
- expect(result).toContain('<mark>test</mark>');
10
- });
11
-
12
- it('should highlight multiple occurrences', () => {
13
- const text = 'test case with test words';
14
- const result = highlightSearchTerms(text, 'test');
15
- const matches = (result.match(/<mark>test<\/mark>/g) || []).length;
16
- expect(matches).toBe(2);
17
- });
18
-
19
- it('should handle multiple search terms', () => {
20
- const text = 'This is a test case';
21
- const result = highlightSearchTerms(text, 'test case');
22
- expect(result).toContain('<mark>test</mark>');
23
- expect(result).toContain('<mark>case</mark>');
24
- });
25
-
26
- it('should be case insensitive', () => {
27
- const text = 'TEST and Test and test';
28
- const result = highlightSearchTerms(text, 'test');
29
- const matches = (result.match(/<mark>/g) || []).length;
30
- expect(matches).toBe(3);
31
- });
32
-
33
- it('should handle special regex characters', () => {
34
- const text = 'This (test) has [special] {chars}';
35
- const result = highlightSearchTerms(text, 'test');
36
- expect(result).toContain('<mark>test</mark>');
37
- expect(result).not.toThrow;
38
- });
39
-
40
- it('should return original text if search query is empty', () => {
41
- const text = 'This is a test';
42
- const result = highlightSearchTerms(text, '');
43
- expect(result).toBe(text);
44
- });
45
-
46
- it('should return empty string if text is empty', () => {
47
- const result = highlightSearchTerms('', 'test');
48
- expect(result).toBe('');
49
- });
50
-
51
- it('should use word boundaries', () => {
52
- const text = 'testing test';
53
- const result = highlightSearchTerms(text, 'test');
54
- // Should highlight 'test' in 'testing' as part of word, and 'test' as standalone
55
- // Actually, \b word boundary means 'test' won't match in 'testing'
56
- const matches = (result.match(/<mark>test<\/mark>/g) || []).length;
57
- expect(matches).toBe(1); // Only the standalone 'test'
58
- });
59
- });
60
-
61
- describe('truncateText', () => {
62
- it('should not truncate short text', () => {
63
- const text = 'Short text';
64
- const result = truncateText(text, 50);
65
- expect(result).toBe(text);
66
- });
67
-
68
- it('should truncate long text with ellipsis', () => {
69
- const text = 'This is a very long text that should be truncated';
70
- const result = truncateText(text, 20);
71
- expect(result).toContain('...');
72
- expect(result.length).toBeLessThan(text.length);
73
- });
74
-
75
- it('should truncate at word boundary if possible', () => {
76
- const text = 'This is a test sentence for truncation';
77
- const result = truncateText(text, 15);
78
- expect(result.endsWith('...')).toBe(true);
79
- // Result should have ellipsis and be shorter
80
- expect(result.length).toBeLessThan(text.length);
81
- });
82
-
83
- it('should use default max length', () => {
84
- const longText = 'a'.repeat(200);
85
- const result = truncateText(longText);
86
- expect(result).toContain('...');
87
- expect(result.length).toBeLessThan(longText.length);
88
- });
89
- });
90
-
91
- describe('extractExcerpt', () => {
92
- it('should extract text around search term', () => {
93
- const text = 'The quick brown fox jumps over the lazy dog';
94
- const result = extractExcerpt(text, 'brown', 20);
95
- expect(result).toContain('brown');
96
- });
97
-
98
- it('should return truncated text if no search query', () => {
99
- const text = 'a'.repeat(300);
100
- const result = extractExcerpt(text, '', 50);
101
- expect(result).toContain('...');
102
- });
103
-
104
- it('should handle short excerpts', () => {
105
- const text = 'The quick brown fox jumps';
106
- const result = extractExcerpt(text, 'brown', 30);
107
- expect(result).toContain('brown');
108
- });
109
-
110
- it('should handle longer text with excerpt', () => {
111
- const text = 'The quick brown fox jumps over lazy dog';
112
- const result = extractExcerpt(text, 'brown', 50);
113
- expect(result).toContain('brown');
114
- });
115
-
116
- it('should handle search term at start', () => {
117
- const text = 'brown fox jumps';
118
- const result = extractExcerpt(text, 'brown', 50);
119
- expect(result).toContain('brown');
120
- expect(result.startsWith('...')).toBe(false);
121
- });
122
-
123
- it('should handle search term at end', () => {
124
- const text = 'the quick brown';
125
- const result = extractExcerpt(text, 'brown', 50);
126
- expect(result).toContain('brown');
127
- expect(result.endsWith('...')).toBe(false);
128
- });
129
-
130
- it('should be case insensitive', () => {
131
- const text = 'The Brown fox';
132
- const result = extractExcerpt(text, 'brown', 50);
133
- expect(result.toLowerCase()).toContain('brown');
134
- });
135
- });
136
- });
@@ -1,100 +0,0 @@
1
- /**
2
- * Highlight search terms in text
3
- * Returns HTML with highlighted terms wrapped in <mark> tags
4
- */
5
- export function highlightSearchTerms(text: string, searchQuery: string): string {
6
- if (!text || !searchQuery.trim()) {
7
- return text;
8
- }
9
-
10
- // Split search query into individual terms and escape special regex characters
11
- const terms = searchQuery
12
- .trim()
13
- .split(/\s+/)
14
- .filter((term) => term.length > 0)
15
- .map((term) => term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
16
-
17
- if (terms.length === 0) {
18
- return text;
19
- }
20
-
21
- // Create a regex that matches any of the terms (case-insensitive)
22
- const regex = new RegExp(`\\b(${terms.join('|')})\\b`, 'gi');
23
-
24
- // Replace matches with highlighted version
25
- return text.replace(regex, '<span class="search-highlight">$1</span>');
26
- }
27
-
28
- /**
29
- * Truncate text to a maximum length and ensure it ends at a word boundary
30
- */
31
- export function truncateText(text: string, maxLength: number = 150): string {
32
- if (text.length <= maxLength) {
33
- return text;
34
- }
35
-
36
- // Truncate and find the last space
37
- const truncated = text.substring(0, maxLength);
38
- const lastSpace = truncated.lastIndexOf(' ');
39
-
40
- if (lastSpace > maxLength * 0.7) {
41
- // If the last space is relatively close to the end, use it
42
- return truncated.substring(0, lastSpace) + '...';
43
- }
44
-
45
- // Otherwise just truncate and add ellipsis
46
- return truncated + '...';
47
- }
48
-
49
- /**
50
- * Extract excerpt around search terms
51
- */
52
- export function extractExcerpt(text: string, searchQuery: string, maxLength: number = 200): string {
53
- if (!text || !searchQuery.trim()) {
54
- return truncateText(text, maxLength);
55
- }
56
-
57
- const searchTerms = searchQuery
58
- .trim()
59
- .split(/\s+/)
60
- .filter((term) => term.length > 0)
61
- .map((term) => term.toLowerCase());
62
-
63
- // Find the position of the first search term in the text
64
- let earliestIndex = text.length;
65
- for (const term of searchTerms) {
66
- const index = text.toLowerCase().indexOf(term);
67
- if (index !== -1 && index < earliestIndex) {
68
- earliestIndex = index;
69
- }
70
- }
71
-
72
- // Extract context around the search term
73
- let startIndex = Math.max(0, earliestIndex - 50);
74
- let endIndex = Math.min(text.length, startIndex + maxLength);
75
-
76
- // Adjust if we're too close to the end
77
- if (endIndex === text.length && startIndex > 0) {
78
- startIndex = Math.max(0, endIndex - maxLength);
79
- }
80
-
81
- let excerpt = text.substring(startIndex, endIndex);
82
-
83
- // Remove partial words at the beginning
84
- if (startIndex > 0) {
85
- const firstSpace = excerpt.indexOf(' ');
86
- if (firstSpace !== -1 && firstSpace < 20) {
87
- excerpt = excerpt.substring(firstSpace + 1);
88
- }
89
- }
90
-
91
- // Add ellipsis if needed
92
- if (startIndex > 0) {
93
- excerpt = '...' + excerpt;
94
- }
95
- if (endIndex < text.length) {
96
- excerpt = excerpt + '...';
97
- }
98
-
99
- return excerpt;
100
- }
@@ -1,7 +0,0 @@
1
- export {
2
- renderMarkdown,
3
- extractMetadata,
4
- getExcerpt,
5
- type MarkdownMetadata,
6
- type MarkdownResult
7
- } from './markdown';