@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,100 +0,0 @@
1
- <script lang="ts">
2
- /**
3
- * Image component with caption support
4
- * Provides responsive images with optional captions and alt text
5
- */
6
-
7
- interface Props {
8
- src: string;
9
- alt: string;
10
- caption?: string;
11
- width?: number;
12
- height?: number;
13
- zoomable?: boolean;
14
- }
15
-
16
- let { src, alt, caption, width, height, zoomable = true }: Props = $props();
17
-
18
- let isZoomed = $state(false);
19
-
20
- function handleZoom() {
21
- if (zoomable) {
22
- isZoomed = !isZoomed;
23
- }
24
- }
25
-
26
- function handleKeydown(e: KeyboardEvent) {
27
- if (e.key === 'Escape' && isZoomed) {
28
- isZoomed = false;
29
- }
30
- }
31
- </script>
32
-
33
- <svelte:window on:keydown={handleKeydown} />
34
-
35
- <figure class="my-6 flex flex-col items-center gap-2">
36
- <div class="relative w-full max-w-4xl overflow-hidden rounded-lg border border-gray-200 dark:border-gray-700">
37
- <!-- Zoomed overlay -->
38
- {#if isZoomed}
39
- <div
40
- class="fixed inset-0 z-50 flex cursor-pointer items-center justify-center bg-black/80 backdrop-blur"
41
- onclick={() => (isZoomed = false)}
42
- onkeydown={(e) => {
43
- if (e.key === 'Enter' || e.key === ' ') {
44
- isZoomed = false;
45
- }
46
- }}
47
- role="button"
48
- tabindex="0"
49
- >
50
- <img
51
- {src}
52
- {alt}
53
- class="max-h-[90vh] max-w-[90vw] rounded-lg object-contain"
54
- {width}
55
- {height}
56
- />
57
- </div>
58
- {/if}
59
-
60
- <!-- Regular image wrapper for clickability -->
61
- <button
62
- onclick={handleZoom}
63
- class="w-full border-0 bg-transparent p-0"
64
- aria-label={zoomable ? 'Click to zoom image' : ''}
65
- type="button"
66
- >
67
- <img
68
- {src}
69
- {alt}
70
- {width}
71
- {height}
72
- class="w-full object-cover {zoomable ? 'cursor-zoom-in hover:opacity-90' : ''} transition-opacity"
73
- />
74
- </button>
75
- </div>
76
-
77
- {#if caption}
78
- <figcaption class="text-center text-sm text-gray-600 dark:text-gray-400">
79
- {caption}
80
- </figcaption>
81
- {/if}
82
- </figure>
83
-
84
- <style>
85
- figure {
86
- margin-top: 1.5rem;
87
- margin-bottom: 1.5rem;
88
- }
89
-
90
- figcaption {
91
- margin-top: 0.5rem;
92
- text-align: center;
93
- font-size: 0.875rem;
94
- color: #4b5563;
95
- }
96
-
97
- :global(.dark) figcaption {
98
- color: #9ca3af;
99
- }
100
- </style>
@@ -1,81 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
-
3
- /**
4
- * Unit tests for Image component
5
- * Tests component configuration and prop handling
6
- */
7
-
8
- describe('Image Component', () => {
9
- describe('Props', () => {
10
- it('renders image with src and alt', () => {
11
- const props = {
12
- src: 'test.jpg',
13
- alt: 'Test image'
14
- };
15
- expect(props.src).toBeTruthy();
16
- expect(props.alt).toBeTruthy();
17
- expect(props.src).toMatch(/\.(jpg|png|gif|webp|svg)$/);
18
- });
19
-
20
- it('should accept required src prop', () => {
21
- const src = 'image.png';
22
- expect(typeof src).toBe('string');
23
- expect(src).toBeTruthy();
24
- });
25
-
26
- it('should accept required alt text prop', () => {
27
- const alt = 'Descriptive alt text';
28
- expect(typeof alt).toBe('string');
29
- expect(alt.length).toBeGreaterThan(0);
30
- });
31
-
32
- it('should accept optional caption prop', () => {
33
- const caption = 'Image caption';
34
- expect(typeof caption).toBe('string');
35
- expect(caption).toBeTruthy();
36
- });
37
-
38
- it('should accept optional width and height props', () => {
39
- const width = 800;
40
- const height = 600;
41
- expect(typeof width).toBe('number');
42
- expect(typeof height).toBe('number');
43
- expect(width).toBeGreaterThan(0);
44
- expect(height).toBeGreaterThan(0);
45
- });
46
-
47
- it('should have zoomable enabled by default', () => {
48
- const zoomable = true;
49
- expect(zoomable).toBe(true);
50
- });
51
- });
52
-
53
- describe('Functionality', () => {
54
- it('should support zoom functionality when enabled', () => {
55
- const zoomable = true;
56
- const isZoomed = false;
57
- expect(zoomable).toBe(true);
58
- expect(typeof isZoomed).toBe('boolean');
59
- });
60
-
61
- it('should handle Escape key to close zoom', () => {
62
- const key = 'Escape';
63
- expect(['Escape', 'Enter', ' ']).toContain(key);
64
- });
65
-
66
- it('should display caption with image', () => {
67
- const props = {
68
- src: 'test.jpg',
69
- alt: 'Test',
70
- caption: 'This is a caption'
71
- };
72
- expect(props.caption).toBeDefined();
73
- expect(props.caption).toBeTruthy();
74
- });
75
-
76
- it('should be accessible with proper alt text', () => {
77
- const alt = 'Important diagram showing architecture';
78
- expect(alt.length).toBeGreaterThan(10);
79
- });
80
- });
81
- });
@@ -1,141 +0,0 @@
1
- <script lang="ts">
2
- import { theme } from '$lib/stores/theme';
3
- import { sidebarOpen } from '$lib/stores/nav';
4
- import { version } from '$lib/stores/version';
5
- import { i18n } from '$lib/stores/i18n';
6
- import { onMount } from 'svelte';
7
-
8
- let {
9
- title = 'Docs',
10
- logo = null,
11
- onLogoClick = () => {},
12
- onVersionChange = (ver: string) => {},
13
- onLanguageChange = (lang: string) => {},
14
- }: {
15
- title?: string;
16
- logo?: string | null;
17
- onLogoClick?: () => void;
18
- onVersionChange?: (version: string) => void;
19
- onLanguageChange?: (language: string) => void;
20
- } = $props();
21
-
22
- let isDark = $derived($theme === 'dark');
23
- let currentVersion = $derived.by(() => $version.current);
24
- let currentLanguage = $derived.by(() => $i18n.currentLanguage);
25
- let availableVersions = $derived.by(() => $version.availableVersions);
26
- let availableLanguages = $derived.by(() => $i18n.availableLanguages);
27
- let isMounted = $state(false);
28
-
29
- onMount(() => {
30
- isMounted = true;
31
- });
32
-
33
- function toggleTheme() {
34
- theme.toggle();
35
- }
36
-
37
- function toggleSidebar() {
38
- sidebarOpen.update(v => !v);
39
- }
40
-
41
- function handleVersionChange(e: Event) {
42
- const select = e.target as HTMLSelectElement;
43
- const newVersion = select.value;
44
- version.setVersion(newVersion);
45
- onVersionChange(newVersion);
46
- }
47
-
48
- function handleLanguageChange(e: Event) {
49
- const select = e.target as HTMLSelectElement;
50
- const newLanguage = select.value;
51
- i18n.setLanguage(newLanguage as any);
52
- onLanguageChange(newLanguage);
53
- }
54
- </script>
55
-
56
- <nav class="sticky top-0 z-40 border-b border-claude-border dark:border-claude-dark-border bg-white dark:bg-claude-dark-bg-secondary backdrop-blur-sm">
57
- <div class="doc-container flex items-center justify-between h-16 px-4 sm:px-6">
58
- <!-- Logo / Brand -->
59
- <button
60
- onclick={onLogoClick}
61
- class="flex items-center gap-2 font-semibold text-lg text-claude-text dark:text-claude-dark-text hover:text-claude-accent dark:hover:text-claude-dark-accent transition-colors"
62
- aria-label="Home"
63
- >
64
- {#if logo}
65
- <img src={logo} alt="Logo" class="h-8 w-8" />
66
- {/if}
67
- <span>{title}</span>
68
- </button>
69
-
70
- <!-- Mobile Menu Button -->
71
- <button
72
- onclick={toggleSidebar}
73
- class="md:hidden p-2 hover:bg-claude-bg-secondary dark:hover:bg-claude-dark-bg rounded-lg transition-colors"
74
- aria-label="Toggle menu"
75
- >
76
- <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
77
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
78
- </svg>
79
- </button>
80
-
81
- <!-- Right Section: Language, Version, Theme Toggle -->
82
- <div class="hidden md:flex items-center gap-4">
83
- <!-- Language Switcher -->
84
- {#if isMounted && availableLanguages.length > 0}
85
- <select
86
- value={currentLanguage}
87
- onchange={handleLanguageChange}
88
- class="px-3 py-2 rounded-lg bg-claude-bg-secondary dark:bg-claude-dark-bg border border-claude-border dark:border-claude-dark-border text-claude-text dark:text-claude-dark-text text-sm cursor-pointer hover:border-claude-accent dark:hover:border-claude-dark-accent transition-colors"
89
- aria-label="Select language"
90
- >
91
- {#each availableLanguages as lang (lang.code)}
92
- <option value={lang.code}>
93
- {lang.label}
94
- </option>
95
- {/each}
96
- </select>
97
- {/if}
98
-
99
- <!-- Version Switcher -->
100
- {#if isMounted && availableVersions.length > 0}
101
- <select
102
- value={currentVersion}
103
- onchange={handleVersionChange}
104
- class="px-3 py-2 rounded-lg bg-claude-bg-secondary dark:bg-claude-dark-bg border border-claude-border dark:border-claude-dark-border text-claude-text dark:text-claude-dark-text text-sm cursor-pointer hover:border-claude-accent dark:hover:border-claude-dark-accent transition-colors"
105
- aria-label="Select version"
106
- >
107
- {#each availableVersions as ver (ver.version)}
108
- <option value={ver.version}>
109
- {ver.label}
110
- </option>
111
- {/each}
112
- </select>
113
- {/if}
114
-
115
- <!-- Dark Mode Toggle -->
116
- <button
117
- onclick={toggleTheme}
118
- class="p-2 rounded-lg bg-claude-bg-secondary dark:bg-claude-dark-bg border border-claude-border dark:border-claude-dark-border hover:border-claude-accent dark:hover:border-claude-dark-accent transition-colors"
119
- aria-label="Toggle dark mode"
120
- >
121
- {#if isDark}
122
- <!-- Moon Icon -->
123
- <svg class="w-5 h-5 text-claude-accent dark:text-claude-dark-accent" fill="currentColor" viewBox="0 0 20 20">
124
- <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
125
- </svg>
126
- {:else}
127
- <!-- Sun Icon -->
128
- <svg class="w-5 h-5 text-claude-accent" fill="currentColor" viewBox="0 0 20 20">
129
- <path fill-rule="evenodd" d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4.323 2.323a1 1 0 011.414 0l.707.707a1 1 0 11-1.414 1.414l-.707-.707a1 1 0 010-1.414zm2.828 2.828a1 1 0 011.414 0l.707.707a1 1 0 11-1.414 1.414l-.707-.707a1 1 0 010-1.414zm2.828 2.828a1 1 0 011.414 0l.707.707a1 1 0 11-1.414 1.414l-.707-.707a1 1 0 010-1.414zM10 11a1 1 0 110 2 1 1 0 010-2zm4.464-1.465a1 1 0 111.414-1.414l.707.707a1 1 0 11-1.414 1.414l-.707-.707zm2.828 2.828a1 1 0 111.414-1.414l.707.707a1 1 0 11-1.414 1.414l-.707-.707zm2.828 2.828a1 1 0 111.414-1.414l.707.707a1 1 0 11-1.414 1.414l-.707-.707zM10 18a1 1 0 110-2 1 1 0 010 2zm-4.464-1.465a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 1.414l-.707.707zm-2.828-2.828a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 1.414l-.707.707zm-2.828-2.828a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 1.414l-.707.707z" clip-rule="evenodd" />
130
- </svg>
131
- {/if}
132
- </button>
133
- </div>
134
- </div>
135
- </nav>
136
-
137
- <style>
138
- :global(html.dark nav) {
139
- background-color: #0f0f0f;
140
- }
141
- </style>
@@ -1,248 +0,0 @@
1
- <script lang="ts">
2
- import { onMount } from 'svelte';
3
- import {
4
- query,
5
- results,
6
- loading,
7
- resultCount,
8
- initPagefind,
9
- updateSearch,
10
- clearSearch
11
- } from '$lib/stores/search';
12
- import { highlightSearchTerms, extractExcerpt } from '$lib/utils/highlight';
13
-
14
- interface Props {
15
- showResults?: boolean;
16
- }
17
-
18
- let { showResults = true }: Props = $props();
19
-
20
- let searchInput: HTMLInputElement | undefined;
21
- let isInitialized = $state(false);
22
- let searchError = $state<string | null>(null);
23
- let isDropdownOpen = $state(false);
24
-
25
- onMount(async () => {
26
- try {
27
- await initPagefind();
28
- isInitialized = true;
29
- } catch (error) {
30
- console.error('Failed to initialize search:', error);
31
- searchError = 'Failed to initialize search';
32
- }
33
- });
34
-
35
- async function handleSearch(e: Event) {
36
- const target = e.target as HTMLInputElement;
37
- const searchQuery = target.value;
38
-
39
- try {
40
- searchError = null;
41
- await updateSearch(searchQuery);
42
- } catch (error) {
43
- console.error('Search error:', error);
44
- searchError = 'Search failed. Please try again.';
45
- }
46
- }
47
-
48
- function handleClear() {
49
- clearSearch();
50
- if (searchInput) {
51
- searchInput.focus();
52
- }
53
- }
54
-
55
- function handleKeyDown(e: KeyboardEvent) {
56
- if (e.key === 'Escape') {
57
- handleClear();
58
- isDropdownOpen = false;
59
- }
60
- }
61
-
62
- function handleFocus() {
63
- isDropdownOpen = true;
64
- }
65
-
66
- function handleBlur(e: FocusEvent) {
67
- // Only close dropdown if focus moves outside the search container
68
- const target = e.relatedTarget as HTMLElement;
69
- if (!target || !target.closest('[data-search-container]')) {
70
- setTimeout(() => {
71
- isDropdownOpen = false;
72
- }, 100);
73
- }
74
- }
75
- </script>
76
-
77
- <div class="relative w-full" data-search-container>
78
- <!-- Search Input -->
79
- <div class="relative">
80
- <input
81
- bind:this={searchInput}
82
- type="text"
83
- placeholder="Search documentation..."
84
- value={$query}
85
- oninput={handleSearch}
86
- onkeydown={handleKeyDown}
87
- onfocus={handleFocus}
88
- onblur={handleBlur}
89
- disabled={!isInitialized}
90
- class="w-full rounded-lg border border-gray-300 bg-white px-4 py-2 pl-10 text-sm text-gray-900 placeholder-gray-500 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 disabled:opacity-50 transition-all"
91
- aria-label="Search documentation"
92
- />
93
-
94
- <!-- Search Icon -->
95
- <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
96
- {#if $loading}
97
- <svg
98
- class="h-4 w-4 animate-spin text-gray-400"
99
- xmlns="http://www.w3.org/2000/svg"
100
- fill="none"
101
- viewBox="0 0 24 24"
102
- >
103
- <circle
104
- class="opacity-25"
105
- cx="12"
106
- cy="12"
107
- r="10"
108
- stroke="currentColor"
109
- stroke-width="4"
110
- ></circle>
111
- <path
112
- class="opacity-75"
113
- fill="currentColor"
114
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
115
- ></path>
116
- </svg>
117
- {:else}
118
- <svg
119
- class="h-4 w-4 text-gray-400"
120
- xmlns="http://www.w3.org/2000/svg"
121
- fill="none"
122
- viewBox="0 0 24 24"
123
- stroke="currentColor"
124
- >
125
- <path
126
- stroke-linecap="round"
127
- stroke-linejoin="round"
128
- stroke-width="2"
129
- d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
130
- />
131
- </svg>
132
- {/if}
133
- </div>
134
-
135
- <!-- Clear Button -->
136
- {#if $query}
137
- <button
138
- onclick={handleClear}
139
- class="absolute inset-y-0 right-0 pr-3 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
140
- aria-label="Clear search"
141
- >
142
- <svg class="h-4 w-4" fill="currentColor" viewBox="0 0 20 20">
143
- <path
144
- fill-rule="evenodd"
145
- d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
146
- clip-rule="evenodd"
147
- />
148
- </svg>
149
- </button>
150
- {/if}
151
- </div>
152
-
153
- <!-- Error Message -->
154
- {#if searchError}
155
- <div class="mt-2 rounded-lg bg-red-50 p-3 text-sm text-red-700 dark:bg-red-900/20 dark:text-red-400">
156
- {searchError}
157
- </div>
158
- {/if}
159
-
160
- <!-- Search Results -->
161
- {#if showResults && isDropdownOpen && ($query || $results.length > 0)}
162
- <div class="absolute top-full left-0 right-0 mt-2 max-h-96 overflow-y-auto rounded-lg border border-gray-300 bg-white shadow-lg dark:border-gray-600 dark:bg-gray-800 z-50">
163
- {#if $loading}
164
- <div class="px-4 py-8 text-center text-gray-500 dark:text-gray-400">
165
- <div class="inline-block">
166
- <svg
167
- class="h-5 w-5 animate-spin"
168
- xmlns="http://www.w3.org/2000/svg"
169
- fill="none"
170
- viewBox="0 0 24 24"
171
- >
172
- <circle
173
- class="opacity-25"
174
- cx="12"
175
- cy="12"
176
- r="10"
177
- stroke="currentColor"
178
- stroke-width="4"
179
- ></circle>
180
- <path
181
- class="opacity-75"
182
- fill="currentColor"
183
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
184
- ></path>
185
- </svg>
186
- </div>
187
- </div>
188
- {:else if $results.length === 0 && $query}
189
- <div class="px-4 py-8 text-center text-gray-500 dark:text-gray-400">
190
- No results found for "<strong>{$query}</strong>"
191
- </div>
192
- {:else if $results.length > 0}
193
- <div class="divide-y divide-gray-200 dark:divide-gray-700">
194
- {#each $results as result (result.id)}
195
- <a
196
- href={result.url}
197
- class="block px-4 py-3 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
198
- >
199
- <div class="font-semibold text-gray-900 dark:text-gray-100">
200
- {@html highlightSearchTerms(result.title, $query)}
201
- </div>
202
- {#if result.excerpt || result.content}
203
- <div class="mt-1 text-sm text-gray-600 dark:text-gray-400 line-clamp-2">
204
- {@html highlightSearchTerms(
205
- extractExcerpt(result.content || result.excerpt || '', $query, 200),
206
- $query
207
- )}
208
- </div>
209
- {/if}
210
- <div class="mt-2 text-xs text-gray-500 dark:text-gray-500">
211
- {result.url}
212
- </div>
213
- </a>
214
- {/each}
215
- </div>
216
-
217
- {#if $resultCount > 0}
218
- <div class="border-t border-gray-200 bg-gray-50 px-4 py-2 text-xs text-gray-600 dark:border-gray-700 dark:bg-gray-700/50 dark:text-gray-400">
219
- {$resultCount} result{$resultCount !== 1 ? 's' : ''} found
220
- </div>
221
- {/if}
222
- {/if}
223
- </div>
224
- {/if}
225
- </div>
226
-
227
- <style>
228
- :global(.line-clamp-2) {
229
- display: -webkit-box;
230
- -webkit-line-clamp: 2;
231
- -webkit-box-orient: vertical;
232
- line-clamp: 2;
233
- overflow: hidden;
234
- }
235
-
236
- :global(mark) {
237
- background-color: #fef3c7;
238
- color: #92400e;
239
- font-weight: 500;
240
- padding: 0 0.125rem;
241
- border-radius: 0.125rem;
242
- }
243
-
244
- :global(.dark mark) {
245
- background-color: #78350f;
246
- color: #fef3c7;
247
- }
248
- </style>
@@ -1,48 +0,0 @@
1
- <script lang="ts">
2
- /**
3
- * Tabs component for switching between content panels
4
- * Usage:
5
- * <Tabs items={[{label: 'Tab 1', content: 'Content 1'}]} />
6
- */
7
-
8
- interface TabItem {
9
- label: string;
10
- content?: string;
11
- }
12
-
13
- interface Props {
14
- items: TabItem[];
15
- defaultIndex?: number;
16
- }
17
-
18
- let { items, defaultIndex = 0 }: Props = $props();
19
-
20
- let activeIndex = $state(0);
21
-
22
- $effect(() => {
23
- activeIndex = defaultIndex;
24
- });
25
- </script>
26
-
27
- <div class="my-4">
28
- <div class="flex border-b border-gray-200 dark:border-gray-700">
29
- {#each items as item, index (index)}
30
- <button
31
- onclick={() => (activeIndex = index)}
32
- class="px-4 py-2 font-medium transition-colors {activeIndex === index
33
- ? 'border-b-2 border-blue-500 text-blue-600 dark:text-blue-400'
34
- : 'text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100'}"
35
- >
36
- {item.label}
37
- </button>
38
- {/each}
39
- </div>
40
-
41
- <div class="mt-4 rounded-b-lg bg-gray-50 p-4 dark:bg-gray-900/20">
42
- {#if items[activeIndex]?.content}
43
- <div class="prose dark:prose-invert">
44
- {@html items[activeIndex].content}
45
- </div>
46
- {/if}
47
- </div>
48
- </div>