diff --git a/.github/workflows/cloudflare-deploy.yml b/.github/workflows/cloudflare-deploy.yml index 21a5360..cd2a327 100644 --- a/.github/workflows/cloudflare-deploy.yml +++ b/.github/workflows/cloudflare-deploy.yml @@ -47,9 +47,9 @@ jobs: - name: Build Package run: pnpm run build - - name: Generate sitemap manifest - working-directory: docs - run: pnpm run sitemap:manifest + # The sitemap manifest now emits from docs-kit's + # `sitemapManifestPlugin` in `docs/vite.config.ts`, which runs + # automatically during the Vite build inside `pnpm run deploy`. - name: Deploy Docs working-directory: docs diff --git a/.gitignore b/.gitignore index dfe1b86..e85a538 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,7 @@ docs/static/llms-full.txt docs/static/llms.txt mprocs.log docs/src/lib/github-stats.json +docs/src/lib/demo-manifest.json +docs/src/lib/demo-loaders.ts +docs/src/lib/demo-virtual.d.ts +docs/src/lib/sitemap-manifest.json diff --git a/docs/.gitignore b/docs/.gitignore index 2e23468..fe4718c 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1 +1,3 @@ -.wrangler/ \ No newline at end of file +.wrangler/ +src/lib/demo-loaders.ts +src/lib/demo-virtual.d.ts diff --git a/docs/package.json b/docs/package.json index c23103d..f3f7b55 100644 --- a/docs/package.json +++ b/docs/package.json @@ -4,15 +4,13 @@ "private": true, "type": "module", "scripts": { - "build": "tsx ./scripts/fetch-github-stats.ts && node ./scripts/generate-sitemap-manifest.mjs && tsx ./scripts/generate-social-cards.ts && vite build", + "build": "tsx ./scripts/fetch-github-stats.ts && tsx ./scripts/generate-social-cards.ts && vite build", "cf-typegen": "wrangler types && mv worker-configuration.d.ts src/", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "deploy": "npm run build && wrangler deploy", "dev": "vite dev", "generate-social": "tsx ./scripts/generate-social-cards.ts", - "sitemap:manifest": "node ./scripts/generate-sitemap-manifest.mjs", - "sitemap:watch": "chokidar 'src/routes/**/*.svelte' 'src/routes/**/*.svx' 'src/routes/**/*.md' --ignore 'src/routes/examples/+page.ts' -c 'node ./scripts/generate-sitemap-manifest.mjs' --initial --silent", "social:watch": "chokidar 'src/lib/components/OG.svelte' 'scripts/generate-social-cards.ts' -c 'tsx ./scripts/generate-social-cards.ts' --initial --silent", "format": "prettier --write .", "lint": "prettier --check . && eslint .", @@ -20,7 +18,7 @@ "preview": "vite preview" }, "dependencies": { - "@humanspeak/docs-kit": "github:humanspeak/docs-kit#2026.6.4", + "@humanspeak/docs-kit": "github:humanspeak/docs-kit#2026.6.9", "@humanspeak/memory-cache": "workspace:*", "@humanspeak/svelte-markdown": "^1.5.4", "github-slugger": "^2.0.0", diff --git a/docs/scripts/generate-sitemap-manifest.mjs b/docs/scripts/generate-sitemap-manifest.mjs deleted file mode 100644 index d9823a0..0000000 --- a/docs/scripts/generate-sitemap-manifest.mjs +++ /dev/null @@ -1,153 +0,0 @@ -import { readdir, readFile, stat, writeFile } from 'node:fs/promises' -import { join, resolve as resolvePath } from 'node:path' - -const ROOT = resolvePath(process.cwd(), 'src', 'routes') - -/** Convert a +page file path to a route path */ -function toRoutePath(file) { - let p = file.replace(ROOT, '') - p = p.replace(/\/\+page\.(svelte|svx|md)$/i, '') - return p === '' ? '/' : p -} - -/** Recursively find +page files */ -async function findPageFiles(dir, out = []) { - const entries = await readdir(dir, { withFileTypes: true }) - for (const e of entries) { - const full = join(dir, e.name) - if (e.isDirectory()) { - await findPageFiles(full, out) - } else if (/\+page\.(svelte|svx|md)$/i.test(e.name)) { - out.push(full) - } - } - return out -} - -/** - * Load example metadata from individual +page.ts files - * @param {string} examplePath - Path to the example directory - * @returns {Promise} Example metadata or null if not found - */ -async function loadExampleMetadata(examplePath) { - try { - const pageTs = join(examplePath, '+page.ts') - const content = await readFile(pageTs, 'utf8') - - // Extract title from the load function return - const titleMatch = content.match(/title:\s*['"`]([^'"`]+)['"`]/) - const sourceUrlMatch = content.match(/sourceUrl:\s*['"`]([^'"`]+)['"`]/) - - if (titleMatch) { - return { - title: titleMatch[1], - sourceUrl: sourceUrlMatch?.[1] || null - } - } - } catch { - // File doesn't exist or can't be read, that's okay - } - return null -} - -/** - * Generate example metadata for the examples landing page - * @param {Object} manifest - The sitemap manifest - * @returns {Promise} Examples metadata object - */ -async function generateExamplesMetadata(manifest) { - const exampleRoutes = Object.keys(manifest) - .filter((route) => route.startsWith('/examples/') && route !== '/examples') - .sort() - - const examples = {} - - for (const route of exampleRoutes) { - const slug = route.replace('/examples/', '') - const examplePath = join(ROOT, 'examples', slug) - - // Try to load metadata from the example's +page.ts file - const metadata = await loadExampleMetadata(examplePath) - - if (metadata) { - const title = metadata.title - examples[slug] = { - title, - description: `Interactive ${title.toLowerCase()} animation example using Svelte Motion.`, - sourceUrl: metadata.sourceUrl - } - } else { - // Fallback: generate from slug - const title = slug - .split('-') - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' ') - - examples[slug] = { - title, - description: `Interactive ${title.toLowerCase()} animation example using Svelte Motion.`, - sourceUrl: null - } - } - } - - return examples -} - -/** - * Update the examples +page.ts file with the latest example metadata - * @param {Object} examples - Examples metadata object - */ -async function updateExamplesPageTs(examples) { - const examplesPageTs = resolvePath(process.cwd(), 'src', 'routes', 'examples', '+page.ts') - - try { - let content = await readFile(examplesPageTs, 'utf8') - - // Find the examples object in the file and replace it - // More robust regex that matches the entire examples object declaration - const examplesObjectRegex = /const examples\s*=\s*\{[\s\S]*?\n\s*\}(?=\s*\n\s*return)/ - // Format as JS object literal with single quotes and unquoted keys (matching prettier config) - const jsObject = JSON.stringify(examples, null, 4) - .replace(/"([^"]+)":/g, (_, key) => - /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? `${key}:` : `'${key}':` - ) - .replace(/: "([^"]*)"/g, (_, value) => `: '${value}'`) - .replace(/: null/g, ': null') - const newExamplesObject = `const examples = ${jsObject.replace(/^/gm, ' ').trim()}` - - if (examplesObjectRegex.test(content)) { - content = content.replace(examplesObjectRegex, newExamplesObject) - await writeFile(examplesPageTs, content, 'utf8') - console.log(`Updated examples metadata in ${examplesPageTs}`) - } else { - console.warn(`Could not find examples object pattern in ${examplesPageTs}`) - } - } catch (error) { - console.warn(`Could not update examples page: ${error.message}`) - } -} - -async function main() { - const files = await findPageFiles(ROOT) - const manifest = {} - for (const file of files) { - const s = await stat(file) - const route = toRoutePath(file) - // Non-recursive lastmod: use the +page file's mtime only - manifest[route] = new Date(s.mtimeMs).toISOString().slice(0, 10) - } - - const dest = resolvePath(process.cwd(), 'src', 'lib', 'sitemap-manifest.json') - await writeFile(dest, JSON.stringify(manifest, null, 2) + '\n', 'utf8') - console.log(`Sitemap manifest written to ${dest}`) - - // Generate and update examples metadata - const examples = await generateExamplesMetadata(manifest) - await updateExamplesPageTs(examples) -} - -main().catch((err) => { - console.error(err) - process.exit(1) -}) diff --git a/docs/src/app.css b/docs/src/app.css index 3108ce4..c28668d 100644 --- a/docs/src/app.css +++ b/docs/src/app.css @@ -1,8 +1,34 @@ @import 'tailwindcss'; @source '../node_modules/@humanspeak/docs-kit/dist'; @import '@humanspeak/docs-kit/styles/base.css'; +@import '@humanspeak/docs-kit/styles/brutalist.css'; +@import '@humanspeak/docs-kit/styles/prose-v2.css'; @plugin '@tailwindcss/typography'; +/* Project-specific tokens */ +:root { + --color-success: #22c55e; +} + +.dark { + --color-success: #4ade80; +} + +@theme inline { + --color-success: var(--color-success); +} + +/* Shiki theme switching outside .prose */ +@layer base { + html:not(.dark) .shiki-dark { + display: none; + } + + html.dark .shiki-light { + display: none; + } +} + /* Project-specific: landing page decorative styles */ .orb-a-bg { background: radial-gradient( @@ -34,3 +60,33 @@ background-size: 200% 100%; animation: sheen 6s ease-in-out infinite; } + +.shiny-button { + --radial-gradient-background: 250, 250, 250; + --solid-color-background: 15, 15, 15; + --overlay-color: 255, 255, 255; +} + +/* Project-specific: ComponentSource dialog */ +.component-source-dialog { + @apply border-border bg-background fixed inset-4 z-50 flex flex-col overflow-hidden rounded-xl border shadow-2xl; + max-width: 1100px; + max-height: 80vh; + margin: auto; +} + +.component-source-code pre.shiki { + @apply m-0 rounded-none bg-transparent! p-4; + counter-reset: line; +} + +.component-source-code pre.shiki code { + @apply bg-transparent!; + counter-reset: line; +} + +.component-source-code pre.shiki code .line::before { + counter-increment: line; + content: counter(line); + @apply text-muted-foreground/50 mr-6 inline-block w-4 text-right; +} diff --git a/docs/src/app.html b/docs/src/app.html index 637d0ff..851e8fd 100644 --- a/docs/src/app.html +++ b/docs/src/app.html @@ -2,18 +2,34 @@ - + + %sveltekit.head% - %sveltekit.head% -
%sveltekit.body%
+
%sveltekit.body%
diff --git a/docs/src/lib/components/general/Footer.svelte b/docs/src/lib/components/general/Footer.svelte index 459c2aa..d533c66 100644 --- a/docs/src/lib/components/general/Footer.svelte +++ b/docs/src/lib/components/general/Footer.svelte @@ -1,5 +1,8 @@ -