@alliance-droid/svelte-docs-system 0.0.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/COMPONENTS.md +365 -0
- package/COVERAGE_REPORT.md +663 -0
- package/README.md +42 -0
- package/SEARCH_VERIFICATION.md +229 -0
- package/TEST_SUMMARY.md +344 -0
- package/bin/init.js +821 -0
- package/docs/E2E_TESTS.md +354 -0
- package/docs/TESTING.md +754 -0
- package/docs/de/index.md +41 -0
- package/docs/en/COMPONENTS.md +443 -0
- package/docs/en/api/examples.md +100 -0
- package/docs/en/api/overview.md +69 -0
- package/docs/en/components/index.md +622 -0
- package/docs/en/config/navigation.md +505 -0
- package/docs/en/config/theme-and-colors.md +395 -0
- package/docs/en/getting-started/integration.md +406 -0
- package/docs/en/guides/common-setups.md +651 -0
- package/docs/en/index.md +243 -0
- package/docs/en/markdown.md +102 -0
- package/docs/en/routing.md +64 -0
- package/docs/en/setup.md +52 -0
- package/docs/en/troubleshooting.md +704 -0
- package/docs/es/index.md +41 -0
- package/docs/fr/index.md +41 -0
- package/docs/ja/index.md +41 -0
- package/package.json +40 -0
- package/pagefind.toml +8 -0
- package/postcss.config.js +5 -0
- package/src/app.css +119 -0
- package/src/app.d.ts +13 -0
- package/src/app.html +11 -0
- package/src/lib/assets/favicon.svg +1 -0
- package/src/lib/components/APITable.svelte +120 -0
- package/src/lib/components/APITable.test.ts +153 -0
- package/src/lib/components/Breadcrumbs.svelte +85 -0
- package/src/lib/components/Breadcrumbs.test.ts +148 -0
- package/src/lib/components/Callout.svelte +60 -0
- package/src/lib/components/Callout.test.ts +100 -0
- package/src/lib/components/CodeBlock.svelte +68 -0
- package/src/lib/components/CodeBlock.test.ts +133 -0
- package/src/lib/components/DocLayout.svelte +84 -0
- package/src/lib/components/Footer.svelte +78 -0
- package/src/lib/components/Image.svelte +100 -0
- package/src/lib/components/Image.test.ts +163 -0
- package/src/lib/components/Navbar.svelte +141 -0
- package/src/lib/components/Search.svelte +248 -0
- package/src/lib/components/Sidebar.svelte +110 -0
- package/src/lib/components/Tabs.svelte +48 -0
- package/src/lib/components/Tabs.test.ts +102 -0
- package/src/lib/config.test.ts +140 -0
- package/src/lib/config.ts +179 -0
- package/src/lib/configIntegration.test.ts +272 -0
- package/src/lib/configLoader.ts +231 -0
- package/src/lib/configParser.test.ts +217 -0
- package/src/lib/configParser.ts +234 -0
- package/src/lib/index.ts +34 -0
- package/src/lib/integration.test.ts +426 -0
- package/src/lib/navigationBuilder.test.ts +338 -0
- package/src/lib/navigationBuilder.ts +268 -0
- package/src/lib/performance.test.ts +369 -0
- package/src/lib/routing.test.ts +202 -0
- package/src/lib/routing.ts +127 -0
- package/src/lib/search-functionality.test.ts +493 -0
- package/src/lib/stores/i18n.test.ts +180 -0
- package/src/lib/stores/i18n.ts +143 -0
- package/src/lib/stores/nav.ts +36 -0
- package/src/lib/stores/search.test.ts +140 -0
- package/src/lib/stores/search.ts +162 -0
- package/src/lib/stores/theme.ts +59 -0
- package/src/lib/stores/version.test.ts +139 -0
- package/src/lib/stores/version.ts +111 -0
- package/src/lib/themeCustomization.test.ts +223 -0
- package/src/lib/themeCustomization.ts +212 -0
- package/src/lib/utils/highlight.test.ts +136 -0
- package/src/lib/utils/highlight.ts +100 -0
- package/src/lib/utils/index.ts +7 -0
- package/src/lib/utils/markdown.test.ts +357 -0
- package/src/lib/utils/markdown.ts +77 -0
- package/src/routes/+layout.server.ts +1 -0
- package/src/routes/+layout.svelte +28 -0
- package/src/routes/+page.svelte +165 -0
- package/static/robots.txt +3 -0
- package/svelte.config.js +18 -0
- package/tailwind.config.ts +55 -0
- package/template-starter/.github/workflows/build.yml +40 -0
- package/template-starter/.github/workflows/deploy-github-pages.yml +47 -0
- package/template-starter/.github/workflows/deploy-netlify.yml +41 -0
- package/template-starter/.github/workflows/deploy-vercel.yml +64 -0
- package/template-starter/NPM-PACKAGE-SETUP.md +233 -0
- package/template-starter/README.md +320 -0
- package/template-starter/docs/_config.json +39 -0
- package/template-starter/docs/api/components.md +257 -0
- package/template-starter/docs/api/overview.md +169 -0
- package/template-starter/docs/guides/configuration.md +145 -0
- package/template-starter/docs/guides/github-pages-deployment.md +254 -0
- package/template-starter/docs/guides/netlify-deployment.md +159 -0
- package/template-starter/docs/guides/vercel-deployment.md +131 -0
- package/template-starter/docs/index.md +49 -0
- package/template-starter/docs/setup.md +149 -0
- package/template-starter/package.json +31 -0
- package/template-starter/pagefind.toml +3 -0
- package/template-starter/postcss.config.js +5 -0
- package/template-starter/src/app.css +34 -0
- package/template-starter/src/app.d.ts +13 -0
- package/template-starter/src/app.html +11 -0
- package/template-starter/src/lib/components/APITable.svelte +120 -0
- package/template-starter/src/lib/components/APITable.test.ts +19 -0
- package/template-starter/src/lib/components/Breadcrumbs.svelte +85 -0
- package/template-starter/src/lib/components/Breadcrumbs.test.ts +19 -0
- package/template-starter/src/lib/components/Callout.svelte +60 -0
- package/template-starter/src/lib/components/Callout.test.ts +16 -0
- package/template-starter/src/lib/components/CodeBlock.svelte +68 -0
- package/template-starter/src/lib/components/CodeBlock.test.ts +12 -0
- package/template-starter/src/lib/components/DocLayout.svelte +84 -0
- package/template-starter/src/lib/components/Footer.svelte +78 -0
- package/template-starter/src/lib/components/Image.svelte +100 -0
- package/template-starter/src/lib/components/Image.test.ts +15 -0
- package/template-starter/src/lib/components/Navbar.svelte +141 -0
- package/template-starter/src/lib/components/Search.svelte +248 -0
- package/template-starter/src/lib/components/Sidebar.svelte +110 -0
- package/template-starter/src/lib/components/Tabs.svelte +48 -0
- package/template-starter/src/lib/components/Tabs.test.ts +17 -0
- package/template-starter/src/lib/index.ts +15 -0
- package/template-starter/src/routes/+layout.svelte +28 -0
- package/template-starter/src/routes/+page.svelte +92 -0
- package/template-starter/svelte.config.js +17 -0
- package/template-starter/tailwind.config.ts +17 -0
- package/template-starter/tsconfig.json +13 -0
- package/template-starter/vite.config.ts +6 -0
- package/tests/e2e/example.spec.ts +345 -0
- package/tsconfig.json +20 -0
- package/vite.config.ts +6 -0
- package/vitest.config.ts +34 -0
- package/vitest.setup.ts +21 -0
|
@@ -0,0 +1,248 @@
|
|
|
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>
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { NavSection, NavItem } from '$lib/stores/nav';
|
|
3
|
+
import { sidebarOpen } from '$lib/stores/nav';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
sections = [] as NavSection[],
|
|
7
|
+
currentPath = '',
|
|
8
|
+
onNavigate = () => {},
|
|
9
|
+
}: {
|
|
10
|
+
sections?: NavSection[];
|
|
11
|
+
currentPath?: string;
|
|
12
|
+
onNavigate?: (path: string) => void;
|
|
13
|
+
} = $props();
|
|
14
|
+
|
|
15
|
+
function handleNavClick(path: string) {
|
|
16
|
+
onNavigate(path);
|
|
17
|
+
sidebarOpen.set(false);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isActive(href: string | undefined): boolean {
|
|
21
|
+
if (!href) return false;
|
|
22
|
+
return currentPath === href || currentPath.startsWith(href + '/');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function renderNavItem(item: NavItem) {
|
|
26
|
+
const active = isActive(item.href);
|
|
27
|
+
return {
|
|
28
|
+
label: item.label,
|
|
29
|
+
href: item.href,
|
|
30
|
+
children: item.children,
|
|
31
|
+
active,
|
|
32
|
+
icon: item.icon,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<!-- Sidebar Overlay for Mobile -->
|
|
38
|
+
{#if $sidebarOpen}
|
|
39
|
+
<button
|
|
40
|
+
class="fixed inset-0 z-30 bg-black/20 md:hidden"
|
|
41
|
+
onclick={() => sidebarOpen.set(false)}
|
|
42
|
+
aria-label="Close menu"
|
|
43
|
+
></button>
|
|
44
|
+
{/if}
|
|
45
|
+
|
|
46
|
+
<!-- Sidebar -->
|
|
47
|
+
<aside
|
|
48
|
+
class="fixed left-0 top-16 bottom-0 w-64 overflow-y-auto border-r border-claude-border dark:border-claude-dark-border bg-white dark:bg-claude-dark-bg-secondary transition-transform md:relative md:top-0 {$sidebarOpen
|
|
49
|
+
? 'translate-x-0'
|
|
50
|
+
: '-translate-x-full md:translate-x-0'} z-30 md:z-auto"
|
|
51
|
+
>
|
|
52
|
+
<div class="p-6 space-y-8">
|
|
53
|
+
{#each sections as section (section.title)}
|
|
54
|
+
<div>
|
|
55
|
+
<h3 class="text-sm font-semibold text-claude-text dark:text-claude-dark-text uppercase tracking-wide mb-3 opacity-60">
|
|
56
|
+
{section.title}
|
|
57
|
+
</h3>
|
|
58
|
+
<nav class="space-y-2">
|
|
59
|
+
{#each section.items as item (item.label)}
|
|
60
|
+
<a
|
|
61
|
+
href={item.href || '#'}
|
|
62
|
+
onclick={(e) => {
|
|
63
|
+
if (item.href) {
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
handleNavClick(item.href);
|
|
66
|
+
}
|
|
67
|
+
}}
|
|
68
|
+
class="block px-3 py-2 rounded-lg transition-colors {isActive(
|
|
69
|
+
item.href
|
|
70
|
+
)
|
|
71
|
+
? 'bg-claude-accent text-white dark:bg-claude-dark-accent'
|
|
72
|
+
: 'text-claude-text dark:text-claude-dark-text hover:bg-claude-bg-secondary dark:hover:bg-claude-dark-bg'}"
|
|
73
|
+
>
|
|
74
|
+
<span class="flex items-center gap-2">
|
|
75
|
+
{#if item.icon}
|
|
76
|
+
<span class="text-sm">{item.icon}</span>
|
|
77
|
+
{/if}
|
|
78
|
+
<span class="text-sm font-medium">{item.label}</span>
|
|
79
|
+
</span>
|
|
80
|
+
</a>
|
|
81
|
+
|
|
82
|
+
<!-- Nested Items -->
|
|
83
|
+
{#if item.children && item.children.length > 0}
|
|
84
|
+
<div class="ml-3 space-y-1 border-l border-claude-border dark:border-claude-dark-border pl-3">
|
|
85
|
+
{#each item.children as child (child.label)}
|
|
86
|
+
<a
|
|
87
|
+
href={child.href || '#'}
|
|
88
|
+
onclick={(e) => {
|
|
89
|
+
if (child.href) {
|
|
90
|
+
e.preventDefault();
|
|
91
|
+
handleNavClick(child.href);
|
|
92
|
+
}
|
|
93
|
+
}}
|
|
94
|
+
class="block px-3 py-1 text-xs rounded transition-colors {isActive(
|
|
95
|
+
child.href
|
|
96
|
+
)
|
|
97
|
+
? 'bg-claude-accent text-white dark:bg-claude-dark-accent'
|
|
98
|
+
: 'text-claude-text-secondary dark:text-claude-dark-text-secondary hover:text-claude-text dark:hover:text-claude-dark-text'}"
|
|
99
|
+
>
|
|
100
|
+
{child.label}
|
|
101
|
+
</a>
|
|
102
|
+
{/each}
|
|
103
|
+
</div>
|
|
104
|
+
{/if}
|
|
105
|
+
{/each}
|
|
106
|
+
</nav>
|
|
107
|
+
</div>
|
|
108
|
+
{/each}
|
|
109
|
+
</div>
|
|
110
|
+
</aside>
|
|
@@ -0,0 +1,48 @@
|
|
|
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>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { render, screen } from '@testing-library/svelte';
|
|
3
|
+
import Tabs from './Tabs.svelte';
|
|
4
|
+
|
|
5
|
+
describe('Tabs Component', () => {
|
|
6
|
+
it('renders all tab buttons', () => {
|
|
7
|
+
const items = [
|
|
8
|
+
{ label: 'Tab 1', content: 'Content 1' },
|
|
9
|
+
{ label: 'Tab 2', content: 'Content 2' }
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
render(Tabs, { props: { items } });
|
|
13
|
+
|
|
14
|
+
expect(screen.getByText('Tab 1')).toBeTruthy();
|
|
15
|
+
expect(screen.getByText('Tab 2')).toBeTruthy();
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Re-export documentation components for use in your templates
|
|
2
|
+
|
|
3
|
+
export { default as Callout } from './components/Callout.svelte';
|
|
4
|
+
export { default as Tabs } from './components/Tabs.svelte';
|
|
5
|
+
export { default as CodeBlock } from './components/CodeBlock.svelte';
|
|
6
|
+
export { default as Image } from './components/Image.svelte';
|
|
7
|
+
export { default as APITable } from './components/APITable.svelte';
|
|
8
|
+
export { default as Breadcrumbs } from './components/Breadcrumbs.svelte';
|
|
9
|
+
export { default as Search } from './components/Search.svelte';
|
|
10
|
+
|
|
11
|
+
// Re-export theme components
|
|
12
|
+
export { default as DocLayout } from './components/DocLayout.svelte';
|
|
13
|
+
export { default as Navbar } from './components/Navbar.svelte';
|
|
14
|
+
export { default as Sidebar } from './components/Sidebar.svelte';
|
|
15
|
+
export { default as Footer } from './components/Footer.svelte';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import '../app.css';
|
|
3
|
+
|
|
4
|
+
let { children } = $props();
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<svelte:window
|
|
8
|
+
on:resize={() => {
|
|
9
|
+
// Handle window resize if needed
|
|
10
|
+
}}
|
|
11
|
+
/>
|
|
12
|
+
|
|
13
|
+
<div class="app">
|
|
14
|
+
{@render children?.()}
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<style>
|
|
18
|
+
:global(body) {
|
|
19
|
+
margin: 0;
|
|
20
|
+
padding: 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.app {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
min-height: 100vh;
|
|
27
|
+
}
|
|
28
|
+
</style>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// Home page for documentation starter
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<main class="mx-auto max-w-4xl px-6 py-12">
|
|
6
|
+
<div class="mb-12">
|
|
7
|
+
<h1 class="mb-4 text-5xl font-bold">Documentation Starter</h1>
|
|
8
|
+
<p class="mb-6 text-xl text-gray-600">
|
|
9
|
+
Welcome to your documentation site built with Svelte Docs System.
|
|
10
|
+
</p>
|
|
11
|
+
<p class="text-lg text-gray-500">
|
|
12
|
+
Edit the <code class="rounded bg-gray-100 px-2 py-1 font-mono">docs/</code> folder to add your documentation content.
|
|
13
|
+
</p>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<section class="mb-12">
|
|
17
|
+
<h2 class="mb-4 text-3xl font-bold">Getting Started</h2>
|
|
18
|
+
<div class="space-y-4">
|
|
19
|
+
<div class="rounded-lg border border-gray-200 p-6">
|
|
20
|
+
<h3 class="mb-2 font-semibold">📝 Add Documentation</h3>
|
|
21
|
+
<p class="text-gray-600">Create markdown files in the <code class="font-mono">docs/</code> folder and they'll automatically be added to your site.</p>
|
|
22
|
+
</div>
|
|
23
|
+
<div class="rounded-lg border border-gray-200 p-6">
|
|
24
|
+
<h3 class="mb-2 font-semibold">⚙️ Configure Navigation</h3>
|
|
25
|
+
<p class="text-gray-600">Edit <code class="font-mono">docs/_config.json</code> to customize the sidebar navigation structure.</p>
|
|
26
|
+
</div>
|
|
27
|
+
<div class="rounded-lg border border-gray-200 p-6">
|
|
28
|
+
<h3 class="mb-2 font-semibold">🎨 Customize Styling</h3>
|
|
29
|
+
<p class="text-gray-600">Update <code class="font-mono">tailwind.config.ts</code> and CSS files to match your brand.</p>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</section>
|
|
33
|
+
|
|
34
|
+
<section class="mb-12">
|
|
35
|
+
<h2 class="mb-4 text-3xl font-bold">Available Scripts</h2>
|
|
36
|
+
<ul class="space-y-2 text-gray-700">
|
|
37
|
+
<li><code class="font-mono">npm run dev</code> - Start development server</li>
|
|
38
|
+
<li><code class="font-mono">npm run build</code> - Build for production</li>
|
|
39
|
+
<li><code class="font-mono">npm run preview</code> - Preview production build</li>
|
|
40
|
+
<li><code class="font-mono">npm run check</code> - Type check and lint</li>
|
|
41
|
+
</ul>
|
|
42
|
+
</section>
|
|
43
|
+
|
|
44
|
+
<section>
|
|
45
|
+
<h2 class="mb-4 text-3xl font-bold">Learn More</h2>
|
|
46
|
+
<p class="text-gray-700">
|
|
47
|
+
Check the documentation in the <code class="font-mono">docs/</code> folder to learn how to use all available features.
|
|
48
|
+
</p>
|
|
49
|
+
</section>
|
|
50
|
+
</main>
|
|
51
|
+
|
|
52
|
+
<style>
|
|
53
|
+
:global(body) {
|
|
54
|
+
background-color: white;
|
|
55
|
+
color: #333;
|
|
56
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
:global(.dark) :global(body) {
|
|
60
|
+
background-color: #030712;
|
|
61
|
+
color: #f3f4f6;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
main {
|
|
65
|
+
max-width: 56rem;
|
|
66
|
+
margin: 0 auto;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
h1 {
|
|
70
|
+
color: #111827;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
:global(.dark) h1 {
|
|
74
|
+
color: white;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
h2 {
|
|
78
|
+
color: #111827;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
:global(.dark) h2 {
|
|
82
|
+
color: white;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
code {
|
|
86
|
+
color: #dc2626;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
:global(.dark) code {
|
|
90
|
+
color: #fca5a5;
|
|
91
|
+
}
|
|
92
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import adapter from '@sveltejs/adapter-static';
|
|
2
|
+
|
|
3
|
+
/** @type {import('@sveltejs/kit').Config} */
|
|
4
|
+
const config = {
|
|
5
|
+
kit: {
|
|
6
|
+
adapter: adapter({
|
|
7
|
+
pages: 'build',
|
|
8
|
+
assets: 'build',
|
|
9
|
+
fallback: 'index.html'
|
|
10
|
+
}),
|
|
11
|
+
prerender: {
|
|
12
|
+
entries: ['*']
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default config;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
content: ['./src/**/*.{html,js,svelte,ts}'],
|
|
3
|
+
theme: {
|
|
4
|
+
extend: {
|
|
5
|
+
colors: {
|
|
6
|
+
'docs': {
|
|
7
|
+
'primary': '#3b82f6',
|
|
8
|
+
'dark': '#1f2937',
|
|
9
|
+
'light': '#f9fafb',
|
|
10
|
+
'border': '#e5e7eb'
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
plugins: [],
|
|
16
|
+
darkMode: 'class'
|
|
17
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./.svelte-kit/tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"allowJs": true,
|
|
5
|
+
"checkJs": true,
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"forceConsistentCasingInFileNames": true,
|
|
8
|
+
"resolveJsonModule": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"strict": true
|
|
12
|
+
}
|
|
13
|
+
}
|