diff --git a/.gitignore b/.gitignore index 397735a0..c58a0c9d 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,4 @@ docker-compose.yml Dockerfile # Markdown -src/pages.docs.json \ No newline at end of file +src/manifest.json \ No newline at end of file diff --git a/bun.lock b/bun.lock index c1b5ba78..f192cc58 100644 --- a/bun.lock +++ b/bun.lock @@ -7,12 +7,13 @@ "dependencies": { "@fontsource-variable/jetbrains-mono": "^5.2.8", "@fontsource-variable/outfit": "^5.2.8", + "chart.js": "^4.5.1", "lucide-svelte": "^1.0.1", }, "devDependencies": { "@eslint/compat": "^2.1.0", "@eslint/js": "^9.39.4", - "@lapikit/repl": "^0.0.0-insiders.fbbe434", + "@lapikit/repl": "^0.0.0-insiders.417ad1f", "@playwright/test": "^1.61.1", "@sveltejs/adapter-node": "^5.5.7", "@sveltejs/enhanced-img": "^0.10.4", @@ -27,11 +28,12 @@ "eslint-config-prettier": "^10.1.8", "eslint-plugin-svelte": "^3.20.0", "globals": "^17.7.0", - "lapikit": "^0.0.0-insiders.bd3f1f3", + "lapikit": "^0.0.0-insiders.b663ce9", "mdsvex": "^0.12.7", "prettier": "^3.9.3", "prettier-plugin-svelte": "^3.5.2", "prettier-plugin-tailwindcss": "^0.7.4", + "sass": "^1.102.0", "svelte": "^5.56.4", "svelte-check": "^4.7.1", "tailwindcss": "^4.3.2", @@ -189,10 +191,38 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@lapikit/repl": ["@lapikit/repl@0.0.0-insiders.fbbe434", "", { "dependencies": { "@lucide/svelte": "^0.562.0", "shiki": "^3.20.0" }, "peerDependencies": { "@sveltejs/kit": "^2.0.0", "lapikit": "^0.5.0", "svelte": "^5.0.0" } }, "sha512-/8gJ5fIiVOssR2viJFU3mTBxeER+taetE9BiYcGVBU2kr25hTA1/IjnL9hAw+tmlCxK0YJI5VZ0C/YLnj6a3RA=="], + "@kurkle/color": ["@kurkle/color@0.3.4", "", {}, "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="], + + "@lapikit/repl": ["@lapikit/repl@0.0.0-insiders.417ad1f", "", { "dependencies": { "@lucide/svelte": "^0.562.0", "shiki": "^3.20.0" }, "peerDependencies": { "@sveltejs/kit": "^2.0.0", "lapikit": "^0.6.7", "svelte": "^5.0.0" } }, "sha512-2pqjnEMjs7yFrwvqUaT1ZhKm6b2k/rrkIGDDbzfkvtfE7ebx/MqBc3bcFX6cOoxkRIPVzCP+KgmRKyEEgXvLZQ=="], "@lucide/svelte": ["@lucide/svelte@0.562.0", "", { "peerDependencies": { "svelte": "^5" } }, "sha512-wDMULwtTFN2Sc/TFBm6gfuVCNb4Y5P9LDrwxNnUbV52+IEU7NXZmvxwXoz+vrrpad6Xupq+Hw5eUlqIHEGhouw=="], + "@parcel/watcher": ["@parcel/watcher@2.6.0", "", { "dependencies": { "detect-libc": "^2.0.3", "is-glob": "^4.0.3", "node-addon-api": "^7.0.0", "picomatch": "^4.0.4" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.6.0", "@parcel/watcher-darwin-arm64": "2.6.0", "@parcel/watcher-darwin-x64": "2.6.0", "@parcel/watcher-freebsd-x64": "2.6.0", "@parcel/watcher-linux-arm-glibc": "2.6.0", "@parcel/watcher-linux-arm-musl": "2.6.0", "@parcel/watcher-linux-arm64-glibc": "2.6.0", "@parcel/watcher-linux-arm64-musl": "2.6.0", "@parcel/watcher-linux-x64-glibc": "2.6.0", "@parcel/watcher-linux-x64-musl": "2.6.0", "@parcel/watcher-win32-arm64": "2.6.0", "@parcel/watcher-win32-x64": "2.6.0" } }, "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w=="], + + "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.6.0", "", { "os": "android", "cpu": "arm64" }, "sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA=="], + + "@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.6.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw=="], + + "@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.6.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw=="], + + "@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.6.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ=="], + + "@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.6.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg=="], + + "@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.6.0", "", { "os": "linux", "cpu": "arm" }, "sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw=="], + + "@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.6.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g=="], + + "@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.6.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA=="], + + "@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.6.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A=="], + + "@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.6.0", "", { "os": "linux", "cpu": "x64" }, "sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw=="], + + "@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.6.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ=="], + + "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.6.0", "", { "os": "win32", "cpu": "x64" }, "sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A=="], + "@playwright/test": ["@playwright/test@1.61.1", "", { "dependencies": { "playwright": "1.61.1" }, "bin": { "playwright": "cli.js" } }, "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig=="], "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], @@ -389,7 +419,9 @@ "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], - "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + "chart.js": ["chart.js@4.5.1", "", { "dependencies": { "@kurkle/color": "^0.3.0" } }, "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw=="], + + "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], @@ -497,6 +529,8 @@ "imagetools-core": ["imagetools-core@9.1.0", "", {}, "sha512-xQjs+2vrxLnAjCq+omuNkd5UQTld9/bP8+YT0LyYTlKfuSQtgUBvqhUwGugzSAh6sCdN+LnROMuLswn5hZ9Fhg=="], + "immutable": ["immutable@5.1.9", "", {}, "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg=="], + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], @@ -529,7 +563,7 @@ "known-css-properties": ["known-css-properties@0.37.0", "", {}, "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ=="], - "lapikit": ["lapikit@0.0.0-insiders.bd3f1f3", "", { "peerDependencies": { "svelte": "^5.0.0" }, "bin": { "lapikit": "bin/index.js" } }, "sha512-eGmZvlcC002R1i+k5z3pLAu7Q1TkDePQENDWCpFbLoc1P9XeU8CevMXCsRVPCs7/aoKMxzfirWZWp5nJsl6RLg=="], + "lapikit": ["lapikit@0.0.0-insiders.b663ce9", "", { "peerDependencies": { "svelte": "^5.0.0" }, "bin": { "lapikit": "bin/index.js" } }, "sha512-Z10XIOlp9uOsz7iEAdVOaIQ8vCXGzQvmkf4UToCijJgHieg5NgPxHNZSMr54LrRSBuVfRBzec5a31bUu1rEezA=="], "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], @@ -597,6 +631,8 @@ "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], + "obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], "oniguruma-parser": ["oniguruma-parser@0.12.2", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="], @@ -651,7 +687,7 @@ "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], - "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + "readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], @@ -667,6 +703,8 @@ "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], + "sass": ["sass@1.102.0", "", { "dependencies": { "chokidar": "^5.0.0", "immutable": "^5.1.5", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-NSOyTnaQF7rTAEOtI2fwb386vL+akyiQLBZu8Na7hXCb+umJy0GAqlcMIaqACZ6Z1VgTBS4K9PG6B3IdjHGJsw=="], + "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], "set-cookie-parser": ["set-cookie-parser@3.1.0", "", {}, "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw=="], @@ -771,6 +809,8 @@ "@eslint/plugin-kit/@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], + "@parcel/watcher/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], + "@rollup/plugin-commonjs/is-reference": ["is-reference@1.2.1", "", { "dependencies": { "@types/estree": "*" } }, "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ=="], "@tailwindcss/node/jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], @@ -803,6 +843,8 @@ "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], + "svelte-check/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + "svelte-eslint-parser/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], "unist-util-position/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], @@ -819,6 +861,8 @@ "mdast-util-to-hast/unist-util-visit/unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], + "svelte-check/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + "vfile/vfile-message/unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], "@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], diff --git a/mdsvex.config.js b/mdsvex.config.js index 120d4c39..09810d7a 100644 --- a/mdsvex.config.js +++ b/mdsvex.config.js @@ -33,8 +33,7 @@ export const mdsvexOptions = { highlight: { highlighter }, layout: { _: dirname(fileURLToPath(import.meta.url)) + '/src/templates/page.svelte', - docs: dirname(fileURLToPath(import.meta.url)) + '/src/templates/doc.svelte', - section: dirname(fileURLToPath(import.meta.url)) + '/src/templates/section.svelte', - legacy: dirname(fileURLToPath(import.meta.url)) + '/src/templates/legacy.svelte' + doc_page: dirname(fileURLToPath(import.meta.url)) + '/src/templates/doc-page.svelte', + doc_section: dirname(fileURLToPath(import.meta.url)) + '/src/templates/doc-section.svelte' } }; diff --git a/package.json b/package.json index 6e8769c2..28bd4a14 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,15 @@ "version": "0.1.0", "type": "module", "scripts": { - "dev": "bun run sync-docs && vite dev", - "build": "bun run sync-docs && vite build", - "build-srv": "bun run sync-docs && bun run sync-changelog && bun run sync-robots && vite build", + "dev": "bun run sync-content && vite dev", + "build": "bun run sync-content && vite build", + "build-srv": "bun run sync-content && bun run sync-changelog && bun run sync-robots && vite build", "preview": "vite preview", "prepare": "svelte-kit sync || echo ''", - "sync-docs": "node --experimental-strip-types scripts/sync-docs/index.ts", + "sync-content": "node --experimental-strip-types scripts/mdsvx/index.ts", "sync-changelog": "node --experimental-strip-types scripts/sync-changelog/index.ts", "sync-robots": "node --experimental-strip-types scripts/sync-robots/index.ts", - "check": "bun run sync-docs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check": "bun run sync-content && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "lint": "prettier --check . && eslint .", "format": "prettier --write .", @@ -22,7 +22,7 @@ "devDependencies": { "@eslint/compat": "^2.1.0", "@eslint/js": "^9.39.4", - "@lapikit/repl": "^0.0.0-insiders.fbbe434", + "@lapikit/repl": "^0.0.0-insiders.417ad1f", "@playwright/test": "^1.61.1", "@sveltejs/adapter-node": "^5.5.7", "@sveltejs/enhanced-img": "^0.10.4", @@ -37,11 +37,12 @@ "eslint-config-prettier": "^10.1.8", "eslint-plugin-svelte": "^3.20.0", "globals": "^17.7.0", - "lapikit": "^0.0.0-insiders.bd3f1f3", + "lapikit": "^0.0.0-insiders.b663ce9", "mdsvex": "^0.12.7", "prettier": "^3.9.3", "prettier-plugin-svelte": "^3.5.2", "prettier-plugin-tailwindcss": "^0.7.4", + "sass": "^1.102.0", "svelte": "^5.56.4", "svelte-check": "^4.7.1", "tailwindcss": "^4.3.2", @@ -53,6 +54,7 @@ "dependencies": { "@fontsource-variable/jetbrains-mono": "^5.2.8", "@fontsource-variable/outfit": "^5.2.8", + "chart.js": "^4.5.1", "lucide-svelte": "^1.0.1" } } diff --git a/scripts/mdsvx/frontmatter.ts b/scripts/mdsvx/frontmatter.ts new file mode 100644 index 00000000..a536c1d0 --- /dev/null +++ b/scripts/mdsvx/frontmatter.ts @@ -0,0 +1,127 @@ +import type { FrontmatterData, FrontmatterValue } from './types.ts'; + +const FRONTMATTER_BLOCK = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/; + +export function readFrontmatter(content: string): FrontmatterData { + const match = content.match(FRONTMATTER_BLOCK); + + if (!match || !match[1].trim()) { + return {}; + } + + const lines = match[1].replace(/\r\n/g, '\n').split('\n'); + + return parseObject(lines, { line: 0 }, 0); +} + +function parseObject( + lines: string[], + cursor: { line: number }, + indent: number +): FrontmatterData { + const result: FrontmatterData = {}; + + while (cursor.line < lines.length) { + skipBlank(lines, cursor); + + if (cursor.line >= lines.length || indentOf(lines[cursor.line]) < indent) { + break; + } + + const trimmed = lines[cursor.line].trim(); + const separator = trimmed.indexOf(':'); + + if (separator === -1) { + throw new Error(`Invalid frontmatter line: "${trimmed}"`); + } + + const key = trimmed.slice(0, separator).trim(); + const remainder = trimmed.slice(separator + 1).trim(); + cursor.line += 1; + + if (remainder) { + result[key] = parseScalar(remainder); + continue; + } + + result[key] = parseNested(lines, cursor, indent); + } + + return result; +} + +function parseArray( + lines: string[], + cursor: { line: number }, + indent: number +): FrontmatterValue[] { + const result: FrontmatterValue[] = []; + + while (cursor.line < lines.length) { + skipBlank(lines, cursor); + + if (cursor.line >= lines.length || indentOf(lines[cursor.line]) < indent) { + break; + } + + const trimmed = lines[cursor.line].trim(); + + if (!trimmed.startsWith('-')) { + throw new Error(`Invalid array entry: "${trimmed}"`); + } + + const remainder = trimmed.slice(1).trim(); + cursor.line += 1; + + result.push(remainder ? parseScalar(remainder) : parseNested(lines, cursor, indent)); + } + + return result; +} + +function parseNested( + lines: string[], + cursor: { line: number }, + parentIndent: number +): FrontmatterValue { + skipBlank(lines, cursor); + + if (cursor.line >= lines.length || indentOf(lines[cursor.line]) <= parentIndent) { + return null; + } + + const nestedIndent = indentOf(lines[cursor.line]); + + return lines[cursor.line].trim().startsWith('-') + ? parseArray(lines, cursor, nestedIndent) + : parseObject(lines, cursor, nestedIndent); +} + +function parseScalar(value: string): FrontmatterValue { + if (value === 'null') return null; + if (value === 'true') return true; + if (value === 'false') return false; + if (value === '[]') return []; + if (value === '{}') return {}; + if (/^-?\d+(?:\.\d+)?$/.test(value)) return Number(value); + + if (value.startsWith('"') && value.endsWith('"')) { + return JSON.parse(value) as string; + } + + if (value.startsWith("'") && value.endsWith("'")) { + return value.slice(1, -1).replace(/\\'/g, "'"); + } + + return value; +} + +function skipBlank(lines: string[], cursor: { line: number }) { + while (cursor.line < lines.length && !lines[cursor.line].trim()) { + cursor.line += 1; + } +} + +function indentOf(line: string): number { + return line.length - line.trimStart().length; +} diff --git a/scripts/mdsvx/index.ts b/scripts/mdsvx/index.ts new file mode 100644 index 00000000..e30dca5b --- /dev/null +++ b/scripts/mdsvx/index.ts @@ -0,0 +1,101 @@ +import { readdir, readFile, writeFile } from 'node:fs/promises'; +import { extname, join } from 'node:path'; +import { readFrontmatter } from './frontmatter.ts'; +import { deriveSource } from './source.ts'; +import type { FrontmatterData, ManifestEntry } from './types.ts'; + +const folders = [ + { dir: 'routes', urlPrefix: '' }, + { dir: 'content/docs', urlPrefix: '/docs' } +]; +const extensionsFile = ['md']; +const routesFile = join(process.cwd(), 'src', 'routes', 'routes.json'); +const manifestFile = join(process.cwd(), 'src', 'manifest.json'); + +const entries = [ + ...(await Promise.all(folders.map(collectFolderEntries))).flat(), + ...(await collectManualEntries()) +].sort((left, right) => left.path.pathname.localeCompare(right.path.pathname)); + +assertNoDuplicatePaths(entries); + +await writeFile(manifestFile, `${JSON.stringify(entries, null, 2)}\n`, 'utf8'); + +console.log(`Wrote ${entries.length} entries to src/manifest.json`); + +async function collectFolderEntries({ dir, urlPrefix }: { dir: string; urlPrefix: string }) { + const baseDir = join(process.cwd(), 'src', dir); + const dirEntries = await readdir(baseDir, { withFileTypes: true, recursive: true }); + + const files = dirEntries.filter( + (entry) => entry.isFile() && extensionsFile.includes(extname(entry.name).slice(1)) + ); + + return Promise.all( + files.map(async (entry): Promise => { + const filePath = join(entry.parentPath, entry.name); + const content = await readFile(filePath, 'utf8'); + const frontmatter = readFrontmatter(content); + const path = deriveSource(filePath, baseDir, urlPrefix); + const title = asOptionalTitle(frontmatter.title) ?? fallbackTitle(path.slugSegments); + + return { ...frontmatter, title, path }; + }) + ); +} + +async function collectManualEntries(): Promise { + const content = await readFile(routesFile, 'utf8'); + const routes: Record = JSON.parse(content); + const sourcePath = 'src/routes/routes.json'; + + return Object.entries(routes).map(([pathname, frontmatter]) => { + const slug = pathname === '/' ? '' : pathname.replace(/^\//, ''); + const slugSegments = slug ? slug.split('/') : []; + const title = requireTitle(frontmatter, `${sourcePath} (${pathname})`); + + return { ...frontmatter, title, path: { sourcePath, slug, slugSegments, pathname } }; + }); +} + +function asOptionalTitle(value: FrontmatterData['title']) { + return typeof value === 'string' && value.trim() ? value.trim() : undefined; +} + +function requireTitle(frontmatter: FrontmatterData, source: string) { + const title = asOptionalTitle(frontmatter.title); + + if (!title) { + throw new Error(`Missing "title" in frontmatter: ${source}`); + } + + return title; +} + +function fallbackTitle(slugSegments: string[]) { + const lastSegment = slugSegments.at(-1); + + if (!lastSegment) { + return 'Documentation'; + } + + return lastSegment + .split(/[-_\s]+/) + .filter(Boolean) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(' '); +} + +function assertNoDuplicatePaths(manifestEntries: ManifestEntry[]) { + const seen = new Set(); + + for (const entry of manifestEntries) { + if (seen.has(entry.path.pathname)) { + throw new Error( + `Duplicate path detected in manifest: "${entry.path.pathname}" (${entry.path.sourcePath})` + ); + } + + seen.add(entry.path.pathname); + } +} diff --git a/scripts/mdsvx/source.ts b/scripts/mdsvx/source.ts new file mode 100644 index 00000000..c151b78b --- /dev/null +++ b/scripts/mdsvx/source.ts @@ -0,0 +1,43 @@ +import { relative } from 'node:path'; + +export type SourceMeta = { + sourcePath: string; + slug: string; + slugSegments: string[]; + pathname: string; +}; + +const ROUTE_GROUP = /^\(.*\)$/; + +export function deriveSource(filePath: string, baseDir: string, urlPrefix: string): SourceMeta { + const sourcePath = toPosixPath(relative(process.cwd(), filePath)); + const segments = toPosixPath(relative(baseDir, filePath)).replace(/\.md$/, '').split('/'); + + const slugSegments = segments + .filter((segment) => !ROUTE_GROUP.test(segment)) + .filter((segment, index, all) => index !== all.length - 1 || !isIndexLike(segment)) + .map(slugify) + .filter(Boolean); + + const slug = slugSegments.join('/'); + const pathname = `${urlPrefix}${slug ? `/${slug}` : ''}` || '/'; + + return { sourcePath, slug, slugSegments, pathname }; +} + +function isIndexLike(segment: string) { + return segment === 'index' || segment.startsWith('+'); +} + +function slugify(value: string) { + return value + .normalize('NFKD') + .replace(/[\u0300-\u036f]/g, '') + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, ''); +} + +function toPosixPath(value: string) { + return value.replaceAll('\\', '/'); +} diff --git a/scripts/sync-docs/types.ts b/scripts/mdsvx/types.ts similarity index 55% rename from scripts/sync-docs/types.ts rename to scripts/mdsvx/types.ts index b576a78b..4d7a8e36 100644 --- a/scripts/sync-docs/types.ts +++ b/scripts/mdsvx/types.ts @@ -8,19 +8,14 @@ export type FrontmatterValue = export type FrontmatterData = Record; -export type ParsedMarkdownFile = { - body: string; - frontmatter: FrontmatterData; - hasFrontmatter: boolean; -}; - -export type DerivedDoc = { - id: string; - metadata: FrontmatterData & { title: string }; - path: string; - section?: string; +export type ManifestPath = { + sourcePath: string; slug: string; slugSegments: string[]; - sourcePath: string; + pathname: string; +}; + +export type ManifestEntry = FrontmatterData & { title: string; + path: ManifestPath; }; diff --git a/scripts/sync-docs/config.ts b/scripts/sync-docs/config.ts deleted file mode 100644 index 4ecf90a9..00000000 --- a/scripts/sync-docs/config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { join } from 'node:path'; - -export const projectRoot = process.cwd(); -export const docsContentDir = join(projectRoot, 'src/content/docs'); -export const generatedDir = join(projectRoot, 'src/'); -export const docsMetadataFile = join(generatedDir, 'pages.docs.json'); -export const generatedFrontmatterKeys = new Set(['slug', 'path', 'section', 'order']); diff --git a/scripts/sync-docs/docs.ts b/scripts/sync-docs/docs.ts deleted file mode 100644 index fd3ec8b1..00000000 --- a/scripts/sync-docs/docs.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { basename, relative } from 'node:path'; -import { docsContentDir, projectRoot } from './config.ts'; -import type { DerivedDoc, FrontmatterData, FrontmatterValue } from './types.ts'; - -export function deriveDoc(filePath: string, frontmatter: FrontmatterData): DerivedDoc { - const relativeFilePath = toPosixPath(relative(docsContentDir, filePath)); - const sourcePath = toPosixPath(relative(projectRoot, filePath)); - const pathSegments = relativeFilePath.split('/'); - const fileName = pathSegments.pop(); - - if (!fileName) { - throw new Error(`Invalid documentation file path: ${filePath}`); - } - - const fileBasename = basename(fileName, '.md'); - const slugSegments = pathSegments - .map(slugify) - .concat(fileBasename !== 'index' ? [slugify(fileBasename)] : []) - .filter(Boolean); - const slug = slugSegments.join('/'); - const path = slug ? `/docs/${slug}` : '/docs'; - const fallbackTitle = toTitle(fileBasename !== 'index' ? fileBasename : (pathSegments.at(-1) ?? '')) || 'Documentation'; - const title = asOptionalString(frontmatter.title) ?? fallbackTitle; - - return { - id: relativeFilePath.replace(/\.md$/, ''), - metadata: createDocMetadata(frontmatter, title), - path, - section: slugSegments[0], - slug, - slugSegments, - sourcePath, - title - }; -} - -export function compareDocs(left: DerivedDoc, right: DerivedDoc) { - return left.path.localeCompare(right.path); -} - -export function assertNoDuplicateRoutes(docs: DerivedDoc[]) { - const slugs = new Set(); - const paths = new Set(); - - for (const doc of docs) { - if (slugs.has(doc.slug)) { - throw new Error(`Duplicate documentation slug detected: "${doc.slug}".`); - } - - if (paths.has(doc.path)) { - throw new Error(`Duplicate documentation path detected: "${doc.path}".`); - } - - slugs.add(doc.slug); - paths.add(doc.path); - } -} - -export function createDocsMetadataJson(docs: DerivedDoc[]) { - const docsPayload = docs.map((doc) => ({ - id: doc.id, - sourcePath: doc.sourcePath, - metadata: doc.metadata, - slug: doc.slug, - slugSegments: doc.slugSegments, - path: doc.path, - section: doc.section - })); - - return `${JSON.stringify(docsPayload, null, 2)}\n`; -} - -function createDocMetadata(frontmatter: FrontmatterData, title: string) { - const metadata: FrontmatterData & { title: string } = { - title - }; - - for (const [key, value] of Object.entries(frontmatter)) { - if (key === 'title') { - continue; - } - - metadata[key] = value; - } - - return metadata; -} - -function slugify(value: string) { - return value - .normalize('NFKD') - .replace(/[\u0300-\u036f]/g, '') - .toLowerCase() - .replace(/[^a-z0-9]+/g, '-') - .replace(/^-+|-+$/g, ''); -} - -function toTitle(value: string) { - return value - .split(/[-_\s]+/) - .filter(Boolean) - .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)) - .join(' '); -} - -function asOptionalString(value: FrontmatterValue | undefined) { - return typeof value === 'string' && value.trim() ? value.trim() : undefined; -} - -function toPosixPath(value: string) { - return value.replaceAll('\\', '/'); -} diff --git a/scripts/sync-docs/files.ts b/scripts/sync-docs/files.ts deleted file mode 100644 index fcfcf8aa..00000000 --- a/scripts/sync-docs/files.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { Dirent } from 'node:fs'; -import { readdir, readFile, writeFile } from 'node:fs/promises'; -import { join } from 'node:path'; - -export async function collectMarkdownFiles(directory: string): Promise { - const entries = await readdir(directory, { withFileTypes: true }); - const files = await Promise.all( - entries - .filter((entry: Dirent) => !entry.name.startsWith('.')) - .map(async (entry: Dirent) => { - const fullPath = join(directory, entry.name); - - if (entry.isDirectory()) { - return collectMarkdownFiles(fullPath); - } - - return entry.isFile() && entry.name.endsWith('.md') ? [fullPath] : []; - }) - ); - - return files.flat().sort((left: string, right: string) => left.localeCompare(right)); -} - -export async function readTextFile(filePath: string) { - return readFile(filePath, 'utf8'); -} - -export async function writeTextFile(filePath: string, content: string) { - await writeFile(filePath, content, 'utf8'); -} diff --git a/scripts/sync-docs/frontmatter.ts b/scripts/sync-docs/frontmatter.ts deleted file mode 100644 index a730c8c8..00000000 --- a/scripts/sync-docs/frontmatter.ts +++ /dev/null @@ -1,328 +0,0 @@ -import { generatedFrontmatterKeys } from './config.ts'; -import type { FrontmatterData, FrontmatterValue, ParsedMarkdownFile } from './types.ts'; - -export function parseMarkdownFile(content: string): ParsedMarkdownFile { - const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/); - - if (!match) { - return { - body: content, - frontmatter: {} satisfies FrontmatterData, - hasFrontmatter: false - }; - } - - const parsed = match[1].trim() ? parseFrontmatter(match[1]) : {}; - - return { - body: content.slice(match[0].length), - frontmatter: parsed, - hasFrontmatter: true - }; -} - -export function stripGeneratedFrontmatter(frontmatter: FrontmatterData): FrontmatterData { - return Object.fromEntries( - Object.entries(frontmatter).filter(([key]) => !generatedFrontmatterKeys.has(key)) - ); -} - -export function serializeMarkdownFile( - frontmatter: FrontmatterData, - body: string, - newline: string, - hasFrontmatter: boolean -) { - const normalizedBody = body.replace(/^\r?\n/, ''); - - if (Object.keys(frontmatter).length === 0) { - return hasFrontmatter ? normalizedBody : body; - } - - const serializedFrontmatter = serializeYamlObject(frontmatter).trimEnd(); - - return ['---', serializedFrontmatter, '---', '', normalizedBody] - .join('\n') - .replace(/\n/g, newline); -} - -function serializeYamlObject(value: FrontmatterData, indent = 0): string { - const padding = ' '.repeat(indent); - - return Object.entries(value) - .map(([key, entry]) => serializeYamlEntry(key, entry, padding, indent)) - .join('\n'); -} - -function serializeYamlEntry( - key: string, - value: FrontmatterValue, - padding: string, - indent: number -): string { - if (Array.isArray(value)) { - if (value.length === 0) { - return `${padding}${key}: []`; - } - - const items = value - .map((item) => { - if (isPlainObject(item)) { - const nested = serializeYamlObject(item, indent + 4); - return `${padding} -\n${nested}`; - } - - if (Array.isArray(item)) { - throw new Error(`Nested arrays are not supported in frontmatter key "${key}".`); - } - - return `${padding} - ${serializeYamlScalar(item)}`; - }) - .join('\n'); - - return `${padding}${key}:\n${items}`; - } - - if (isPlainObject(value)) { - const nestedEntries = Object.keys(value); - - if (nestedEntries.length === 0) { - return `${padding}${key}: {}`; - } - - return `${padding}${key}:\n${serializeYamlObject(value, indent + 2)}`; - } - - return `${padding}${key}: ${serializeYamlScalar(value)}`; -} - -function serializeYamlScalar(value: string | number | boolean | null) { - if (value === null) { - return 'null'; - } - - if (typeof value === 'string') { - return JSON.stringify(value); - } - - return String(value); -} - -function parseFrontmatter(source: string): FrontmatterData { - const lines = source.replace(/\r\n/g, '\n').split('\n'); - const { value, nextIndex } = parseObjectBlock(lines, 0, 0, 'frontmatter'); - - skipBlankLines(lines, nextIndex); - - return value; -} - -function parseObjectBlock( - lines: string[], - startIndex: number, - indent: number, - context: string -): { value: FrontmatterData; nextIndex: number } { - const result: FrontmatterData = {}; - let index = skipBlankLines(lines, startIndex); - - while (index < lines.length) { - const line = lines[index]; - const lineIndent = getIndent(line); - const trimmed = line.trim(); - - if (!trimmed) { - index += 1; - continue; - } - - if (lineIndent < indent) { - break; - } - - if (lineIndent !== indent) { - throw new Error(`Unexpected indentation at ${context} line ${index + 1}.`); - } - - if (trimmed.startsWith('-')) { - throw new Error(`Unexpected array item at ${context} line ${index + 1}.`); - } - - const separatorIndex = trimmed.indexOf(':'); - - if (separatorIndex === -1) { - throw new Error(`Invalid frontmatter entry at ${context} line ${index + 1}.`); - } - - const key = trimmed.slice(0, separatorIndex).trim(); - const remainder = trimmed.slice(separatorIndex + 1).trim(); - - if (!key) { - throw new Error(`Missing key at ${context} line ${index + 1}.`); - } - - if (remainder) { - result[key] = parseScalar(remainder, `${context}.${key}`); - index += 1; - continue; - } - - const nextIndex = skipBlankLines(lines, index + 1); - - if (nextIndex >= lines.length || getIndent(lines[nextIndex]) <= indent) { - result[key] = null; - index = nextIndex; - continue; - } - - const nextLine = lines[nextIndex]; - const nextIndent = getIndent(nextLine); - const nextTrimmed = nextLine.trim(); - - if (nextTrimmed.startsWith('-')) { - const arrayResult = parseArrayBlock(lines, nextIndex, nextIndent, `${context}.${key}`); - result[key] = arrayResult.value; - index = arrayResult.nextIndex; - continue; - } - - const objectResult = parseObjectBlock(lines, nextIndex, nextIndent, `${context}.${key}`); - result[key] = objectResult.value; - index = objectResult.nextIndex; - } - - return { value: result, nextIndex: index }; -} - -function parseArrayBlock( - lines: string[], - startIndex: number, - indent: number, - context: string -): { value: FrontmatterValue[]; nextIndex: number } { - const result: FrontmatterValue[] = []; - let index = skipBlankLines(lines, startIndex); - - while (index < lines.length) { - const line = lines[index]; - const lineIndent = getIndent(line); - const trimmed = line.trim(); - - if (!trimmed) { - index += 1; - continue; - } - - if (lineIndent < indent) { - break; - } - - if (lineIndent !== indent || !trimmed.startsWith('-')) { - throw new Error(`Invalid array entry at ${context} line ${index + 1}.`); - } - - const remainder = trimmed.slice(1).trim(); - - if (remainder) { - result.push(parseScalar(remainder, `${context}[${result.length}]`)); - index += 1; - continue; - } - - const nextIndex = skipBlankLines(lines, index + 1); - - if (nextIndex >= lines.length || getIndent(lines[nextIndex]) <= indent) { - result.push(null); - index = nextIndex; - continue; - } - - const nextLine = lines[nextIndex]; - const nextIndent = getIndent(nextLine); - const nextTrimmed = nextLine.trim(); - - if (nextTrimmed.startsWith('-')) { - const arrayResult = parseArrayBlock( - lines, - nextIndex, - nextIndent, - `${context}[${result.length}]` - ); - result.push(arrayResult.value); - index = arrayResult.nextIndex; - continue; - } - - const objectResult = parseObjectBlock( - lines, - nextIndex, - nextIndent, - `${context}[${result.length}]` - ); - result.push(objectResult.value); - index = objectResult.nextIndex; - } - - return { value: result, nextIndex: index }; -} - -function parseScalar(value: string, context: string): FrontmatterValue { - if (value === 'null') { - return null; - } - - if (value === 'true') { - return true; - } - - if (value === 'false') { - return false; - } - - if (value === '[]') { - return []; - } - - if (value === '{}') { - return {}; - } - - if (/^-?\d+(?:\.\d+)?$/.test(value)) { - return Number(value); - } - - if ( - (value.startsWith('"') && value.endsWith('"')) || - (value.startsWith("'") && value.endsWith("'")) - ) { - if (value.startsWith('"')) { - return JSON.parse(value) as string; - } - - return value.slice(1, -1).replace(/\\'/g, "'"); - } - - if (value.includes(': ')) { - throw new Error(`Inline nested YAML is not supported at ${context}.`); - } - - return value; -} - -function skipBlankLines(lines: string[], index: number) { - let nextIndex = index; - - while (nextIndex < lines.length && !lines[nextIndex].trim()) { - nextIndex += 1; - } - - return nextIndex; -} - -function getIndent(line: string) { - return line.length - line.trimStart().length; -} - -function isPlainObject(value: unknown): value is Record { - return Object.prototype.toString.call(value) === '[object Object]'; -} diff --git a/scripts/sync-docs/index.ts b/scripts/sync-docs/index.ts deleted file mode 100644 index c9746c04..00000000 --- a/scripts/sync-docs/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { mkdir } from 'node:fs/promises'; -import { docsContentDir, docsMetadataFile, generatedDir } from './config.ts'; -import { assertNoDuplicateRoutes, compareDocs, createDocsMetadataJson, deriveDoc } from './docs.ts'; -import { collectMarkdownFiles, readTextFile, writeTextFile } from './files.ts'; -import { - parseMarkdownFile, - serializeMarkdownFile, - stripGeneratedFrontmatter -} from './frontmatter.ts'; -import type { DerivedDoc } from './types.ts'; - -await syncDocs(); - -async function syncDocs() { - const docFiles = await collectMarkdownFiles(docsContentDir); - const derivedDocs: DerivedDoc[] = []; - - for (const filePath of docFiles) { - derivedDocs.push(await syncDocFile(filePath)); - } - - const sortedDocs = [...derivedDocs].sort(compareDocs); - assertNoDuplicateRoutes(sortedDocs); - - await mkdir(generatedDir, { recursive: true }); - await writeTextFile(docsMetadataFile, createDocsMetadataJson(sortedDocs)); - - console.log(`Synced ${sortedDocs.length} documentation files.`); -} - -async function syncDocFile(filePath: string): Promise { - const rawContent = await readTextFile(filePath); - const newline = rawContent.includes('\r\n') ? '\r\n' : '\n'; - const { body, frontmatter, hasFrontmatter } = parseMarkdownFile(rawContent); - const nextFrontmatter = stripGeneratedFrontmatter(frontmatter); - const derivedDoc = deriveDoc(filePath, nextFrontmatter); - const nextContent = serializeMarkdownFile(nextFrontmatter, body, newline, hasFrontmatter); - - if (nextContent !== rawContent) { - await writeTextFile(filePath, nextContent); - } - - return derivedDoc; -} diff --git a/src/components/app-bar-global.svelte b/src/components/app-bar-global.svelte new file mode 100644 index 00000000..24f30086 --- /dev/null +++ b/src/components/app-bar-global.svelte @@ -0,0 +1,14 @@ + + + + + + + diff --git a/src/components/app-bar.svelte b/src/components/app-bar.svelte new file mode 100644 index 00000000..67734770 --- /dev/null +++ b/src/components/app-bar.svelte @@ -0,0 +1,113 @@ + + + + nav.toggle()} aria-label="open navigation" icon> + + + + + + + +
+ +
+ +
+ + + {@html discordIcon} + + + + + + {@html githubIcon} + + + + + + (openModal = true)} + color="fg-inverse" + background="bg-inverse" + > + {#snippet prepend()} + + + + {/snippet} + + Install Lapikit + +
+ + + + + diff --git a/src/components/app/bento-counter.svelte b/src/components/app/bento-counter.svelte new file mode 100644 index 00000000..79d017bf --- /dev/null +++ b/src/components/app/bento-counter.svelte @@ -0,0 +1,149 @@ + + +
+ + + + {@html ViteJsIcon} + + + + {@html RollupIcon} + + + + + {@html WebpackIcon} + + + + + Integrations + Use your favorites builder with lapikit + + + + + + Customize + Support themes and design system with global variables + + + + + Components + +
25
+ Optimized for svelte and sveltekit +
+
+ + +
3
+
+ + Hooks and actions + More utils for facility your developpment + +
+ + + + + Open Source + Lapikit is 100% Open Source.
Use license MIT
+
+
+ + + + Low dependencies for long support + + It is developed using 67 external packages 54% fewer than the other major component + libraries to ensure better support and security. + + +
+ + diff --git a/src/components/app/chart-download.svelte b/src/components/app/chart-download.svelte new file mode 100644 index 00000000..f0190043 --- /dev/null +++ b/src/components/app/chart-download.svelte @@ -0,0 +1,106 @@ + + +
+ +
+ + diff --git a/src/components/app/desktop-nav.svelte b/src/components/app/desktop-nav.svelte new file mode 100644 index 00000000..972943cd --- /dev/null +++ b/src/components/app/desktop-nav.svelte @@ -0,0 +1,147 @@ + + + + + diff --git a/src/components/app/discover.svelte b/src/components/app/discover.svelte new file mode 100644 index 00000000..f180f174 --- /dev/null +++ b/src/components/app/discover.svelte @@ -0,0 +1,94 @@ + + +
+
+ + (view = 'components')} active={view === 'components'} + >Components + (view = 'dashboard')} active={view === 'dashboard'} + >Dashboard + + + + {#if view === 'components'} + +
+ + + +
+
+ + + + +
+
+ +
+
+ {/if} +
+
+
+ + diff --git a/src/components/app/drawer-nav.svelte b/src/components/app/drawer-nav.svelte new file mode 100644 index 00000000..2a8d2ac9 --- /dev/null +++ b/src/components/app/drawer-nav.svelte @@ -0,0 +1,188 @@ + + + +{#if open} + +{/if} + + +
+
+ + + + (open = false)} icon> + + + + + + +
+ + + +
+
+ + +
+ + diff --git a/src/components/app/explore-banner.svelte b/src/components/app/explore-banner.svelte new file mode 100644 index 00000000..e415a596 --- /dev/null +++ b/src/components/app/explore-banner.svelte @@ -0,0 +1,55 @@ + + +
+
+

Explore the futur library components on your apps

+ +

+ Simple setup, intuitive and natural use, Svelte-focused optimization with Runes—so, shall we + get started? +

+ +
+ Get started with Lapikit +
+
+
+ + diff --git a/src/components/app/faq.svelte b/src/components/app/faq.svelte new file mode 100644 index 00000000..81de0f86 --- /dev/null +++ b/src/components/app/faq.svelte @@ -0,0 +1,81 @@ + + +
+
+

FAQ

+

+ Explore our documentation and resources to get the most out of Lapikit: +

+ + + {#each faqContext as { question, message }, index (question)} + + {message} + + {/each} + +
+
+ + diff --git a/src/components/app/hero.svelte b/src/components/app/hero.svelte new file mode 100644 index 00000000..0b2c4c98 --- /dev/null +++ b/src/components/app/hero.svelte @@ -0,0 +1,289 @@ + + +
+
+
+ + {#snippet prepend()} + + {/snippet} + This app was made with Lapikit +
+

+
Simple, optimized
+
+ + {possibility} + +
+ +
+ for Svelte +
+

+
+

+ A library of accessible, high-performance, versatile components that let you develop fast, + fully customizable interfaces. svelte and + sveltekit ready. +

+
+ + {#snippet prepend()} + + + + {/snippet} + Discover the power + + + {#snippet prepend()} + + + + {/snippet} + Install Guide + +
+
+ handleCopy(shellCommand)}> + {#snippet prepend()} + + {/snippet} + {shellCommand} + {#snippet append()} + + {#if hasCopied} + + {:else} + + {/if} + + {/snippet} + +
+ (shellCommand = shellList['npm'])} + active={shellList['npm'] === shellCommand} + size="xs" + variant="text" + > + {#snippet prepend()} + + {@html npmIcon} + + {/snippet} + npm + + (shellCommand = shellList['yarn'])} + active={shellList['yarn'] === shellCommand} + size="xs" + variant="text" + > + {#snippet prepend()} + + {@html yarnIcon} + + {/snippet} + yarn + + (shellCommand = shellList['bun'])} + active={shellList['bun'] === shellCommand} + size="xs" + variant="text" + > + {#snippet prepend()} + + {@html bunIcon} + + {/snippet} + bun + +
+
+
+
+
+ + diff --git a/src/components/app/lapikit-components.svelte b/src/components/app/lapikit-components.svelte new file mode 100644 index 00000000..9e8579fb --- /dev/null +++ b/src/components/app/lapikit-components.svelte @@ -0,0 +1,127 @@ + + +
+

More components for more power !

+

Passphrase

+ + {#each components as component} + (selected = component)}> + {component.label} + + {/each} + See more + + + + +
+ {selected.tilte} + {selected.description} +
+ + {selected.cta} + +
+ + {#key selected.label} + + + + {/key} + +
+
+ + diff --git a/src/components/app/modules/actions.svelte b/src/components/app/modules/actions.svelte new file mode 100644 index 00000000..fadaf932 --- /dev/null +++ b/src/components/app/modules/actions.svelte @@ -0,0 +1,20 @@ +
+ console.log('I love Lapikit')}> + Click me + + console.log('I love Lapikit')}> + Click me + + console.log('I love Lapikit')}> + Click me + + console.log('I love Lapikit')}> + Click me + + console.log('I love Lapikit')}> + Click me + + console.log('I love Lapikit')}> + Click me + +
diff --git a/src/components/app/modules/alert.svelte b/src/components/app/modules/alert.svelte new file mode 100644 index 00000000..1d10849f --- /dev/null +++ b/src/components/app/modules/alert.svelte @@ -0,0 +1,32 @@ + + + + {#snippet prepend()} + + + + {/snippet} + +
+

You have new mission

+

Install lapikit on your project

+
+ + {#snippet append()} + Install + {/snippet} +
+ + diff --git a/src/components/app/modules/avatar.svelte b/src/components/app/modules/avatar.svelte new file mode 100644 index 00000000..bb1f2782 --- /dev/null +++ b/src/components/app/modules/avatar.svelte @@ -0,0 +1,18 @@ +
+ + + Paysage + + + Paysage + + + Paysage + +
+ + diff --git a/src/components/app/modules/btn.svelte b/src/components/app/modules/btn.svelte new file mode 100644 index 00000000..79d71a8a --- /dev/null +++ b/src/components/app/modules/btn.svelte @@ -0,0 +1,11 @@ +
+ console.log('I love Lapikit')}> + Click me + + console.log('I love Lapikit')}> + Click me + + console.log('I love Lapikit')}> + Click me + +
diff --git a/src/components/app/modules/card.svelte b/src/components/app/modules/card.svelte new file mode 100644 index 00000000..b4387b7a --- /dev/null +++ b/src/components/app/modules/card.svelte @@ -0,0 +1,42 @@ + + + +
+

Lapikit

+ @lapikit +
+
+ + Building the future of UI for web & mobile.
+ Nycolaide +
+ + 4 + Following + 12 + Followers + +
+ + diff --git a/src/components/app/modules/dropdown.svelte b/src/components/app/modules/dropdown.svelte new file mode 100644 index 00000000..16af8ea7 --- /dev/null +++ b/src/components/app/modules/dropdown.svelte @@ -0,0 +1,40 @@ + + +
+

State*

+ + + {#snippet activator({ open, toggle }: ModelDropdownProps)} + toggle(e.currentTarget as HTMLElement)} + block + > + {select} + + {#snippet append()} + + {#if open} + + {:else} + + {/if} + + {/snippet} + + {/snippet} + + + {#each users as user (user)} + (select = user)}>{user} + {/each} + + +
diff --git a/src/components/app/modules/input.svelte b/src/components/app/modules/input.svelte new file mode 100644 index 00000000..b65ea60c --- /dev/null +++ b/src/components/app/modules/input.svelte @@ -0,0 +1,15 @@ + + +
+

Your email*

+ +
diff --git a/src/components/app/modules/list.svelte b/src/components/app/modules/list.svelte new file mode 100644 index 00000000..504bb8ab --- /dev/null +++ b/src/components/app/modules/list.svelte @@ -0,0 +1,48 @@ + + + + Actions + console.log('I love Lapikit')}> + {#snippet prepend()} + + + + {/snippet} + New file + {#snippet append()} + + + + {/snippet} + + console.log('I love Lapikit')}> + {#snippet prepend()} + + + + {/snippet} + Edit file + {#snippet append()} + + + + {/snippet} + + + Danger zone + console.log('I love Lapikit')}> + {#snippet prepend()} + + + + {/snippet} + Delete file + {#snippet append()} + + + + {/snippet} + + diff --git a/src/components/app/modules/toolbar.svelte b/src/components/app/modules/toolbar.svelte new file mode 100644 index 00000000..16c3266f --- /dev/null +++ b/src/components/app/modules/toolbar.svelte @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/app/preview-footer.svelte b/src/components/app/preview-footer.svelte new file mode 100644 index 00000000..5c4da6aa --- /dev/null +++ b/src/components/app/preview-footer.svelte @@ -0,0 +1,220 @@ + + +
+
+
+
+ + + + +
+

Menu

+ + + {#each routerFooter as { title, child }, index (index)} + + + {#each child as { label, url, path } (label)} + {#if url} + + {label} + + + + + {:else if path} + {label} + {/if} + {/each} + + + {/each} + +
+ +

Theme

+ + + +

Social

+ + + + + {@html discordIcon} + + + + + + {@html githubIcon} + + + + + + {@html npmIcon} + + + +
+
+ {#each routerFooter as { title, child } (title)} + + {title} + {#each child as { label, url, path } (label)} + {#if url} + + {label} + + + + + {:else if path} + {label} + {/if} + {/each} + + {/each} +
+
+
+
+ Lapikit +
+ + +
+ + diff --git a/src/components/app/preview-navbar.svelte b/src/components/app/preview-navbar.svelte new file mode 100644 index 00000000..6419e64d --- /dev/null +++ b/src/components/app/preview-navbar.svelte @@ -0,0 +1,157 @@ + + + + + + + + + + +
+ + {#snippet activator({ open, toggle }: ModelDropdownProps)} + toggle(e.currentTarget as HTMLElement)} + > + About us + {#snippet append()} + + {#if open} + + {:else} + + {/if} + + {/snippet} + + {/snippet} + + + + {#snippet append()} + + {@html githubIcon} + + {/snippet} + + 50.0k + + + + + + {#snippet prepend()} + {@html discordIcon} + {/snippet} + Join to community + + + + + (openDrawer = true)}> + + +
+
+ + + + diff --git a/src/components/app/table-package-format.svelte b/src/components/app/table-package-format.svelte new file mode 100644 index 00000000..7e837073 --- /dev/null +++ b/src/components/app/table-package-format.svelte @@ -0,0 +1,18 @@ + + +{#if value == 'true' || value == 'yes' || (typeof value === 'boolean' && value)} + +{:else if value == 'false' || value == 'no' || (typeof value === 'boolean' && !value)} + +{:else if value == 'partial'} + +{:else if value == 'nothing'} + +{:else} + {@render children?.()} +{/if} diff --git a/src/components/app/table-package.svelte b/src/components/app/table-package.svelte new file mode 100644 index 00000000..c67e7343 --- /dev/null +++ b/src/components/app/table-package.svelte @@ -0,0 +1,482 @@ + + +
+
+
+
+

Why Lapikit ?

+

+ Lorem ipsum, dolor sit amet consectetur adipisicing elit. Animi culpa odit, quisquam + reprehenderit sunt officiis necessitatibus! Aliquid quod odit esse. Perferendis repellat + reiciendis molestias est! Eveniet sint quaerat consequatur facere? +

+ +
    + {#each validArg as argument (argument)} +
  • + + {@html argument} +
  • + {/each} +
+
+
picture ?
+
+
+
+
+ {#each result as { title, logo }, indexResult} +
+
+ {title} +

{title}

+
+
+ {/each} +
+
+
Features
+ {#each result as items, indexResult} +
+ {#if indexResult != 0} + + {#snippet activator({ open, toggle }: ModelDropdownProps)} + toggle(e.currentTarget as HTMLElement)} + rounded="full" + size="sm" + class="hidden_btn" + > + + + {/snippet} + + + {#each result as { title }, index} + {#if index != 0} + (onlyDisplay = index)} + > + {title} + + {/if} + {/each} + + + {/if} +
+ {/each} +
+ + {#each content as { text, subtext, subject }, index} +
+
+

{text}

+ {#if subtext} + + {@html subtext} + + {/if} +
+ {#each result as _, indexResult} +
+ {/each} +
+ + {#each subject as element, indexElement} +
+
{element}
+ {#each result as items, indexResult} + {@const value = items.values[index][indexElement]} +
+ {#if value == 'yes'} + + + + + + {:else if value == 'no'} + + + + + + {:else if value == 'partial'} + + + + + + {:else} + {value} + {/if} +
+ {/each} +
+ {/each} + {/each} + + +
+
+
+ + diff --git a/src/components/app/testimonials.svelte b/src/components/app/testimonials.svelte new file mode 100644 index 00000000..694d45e0 --- /dev/null +++ b/src/components/app/testimonials.svelte @@ -0,0 +1,134 @@ + + +
+
+

Go further with Lapikit

+

+ Explore our documentation and resources to get the most out of Lapikit: +

+ +
+ + + Developer Docs + + Explore all possibilities to create your app with lapikit + + + + Enter in the DDev docs + + + + + Community + + Join the Discord Lapikit server for announcements, events, and discussions with fellow + devs. + + + + Join the server + + + + + Contribute + + Participate to the evolution of Lapikit project with your idea and features + + + + + + {@html githubIcon} + + + + + {@html buymyecoffeeIcon} + + + + +
+
+
+ + diff --git a/src/components/aside/aside.svelte b/src/components/aside/aside.svelte new file mode 100644 index 00000000..1808b80a --- /dev/null +++ b/src/components/aside/aside.svelte @@ -0,0 +1,8 @@ + + + diff --git a/src/components/breadcrumbs.svelte b/src/components/breadcrumbs.svelte index 4264eff4..e15f07f5 100644 --- a/src/components/breadcrumbs.svelte +++ b/src/components/breadcrumbs.svelte @@ -2,7 +2,7 @@ import { resolve } from '$app/paths'; import type { BreadcrumbItem } from '$lib/@types'; import { capitalize } from '$lib/utils'; - import { ChevronRight } from 'lucide-svelte'; + import { ChevronRight, House } from 'lucide-svelte'; let { items = [] }: { items?: BreadcrumbItem[] } = $props(); @@ -15,10 +15,19 @@ {#if index === items.length - 1} {capitalize(item.label)} {:else if item.href} - {capitalize(item.label)} - + {#if item.label === 'Home'} + + + + + + {:else} + {capitalize(item.label)} + + {/if} {:else} {capitalize(item.label)} + {/if} {/each} @@ -27,13 +36,11 @@ {/if} diff --git a/src/components/drawer-release.svelte b/src/components/drawer-release.svelte new file mode 100644 index 00000000..7e863a36 --- /dev/null +++ b/src/components/drawer-release.svelte @@ -0,0 +1,81 @@ + + + + + Release + + {npmState.downloads || 0} + + + + + + {#each releases as release (release.key)} + + {#snippet prepend()} + + {#snippet prepend()} + + + + {/snippet} + {release.label} + + {/snippet} + +
+ {release.version} + published {formatPublishDate(release.publish)} +
+
+ {/each} +
+
+ + diff --git a/src/components/drawer.svelte b/src/components/drawer.svelte index a1980d95..6d80c9c9 100644 --- a/src/components/drawer.svelte +++ b/src/components/drawer.svelte @@ -1,23 +1,31 @@ {#if open} +{/if} diff --git a/src/routes/docs/+page.md b/src/routes/docs/+page.md index 6720a7af..f711a15c 100644 --- a/src/routes/docs/+page.md +++ b/src/routes/docs/+page.md @@ -1,10 +1,11 @@ --- -title: Documentation -description: Browse Lapikit documentation, including components, hooks and theming resources. +title: 'Documentation' +category: 'Lapikit' +head: + title: 'Documentation' + description: 'Browse Lapikit documentation, including components, hooks and theming resources.' --- -# Lapikit documentation - Lapikit documentation is organized as simple HTML pages so search engines and users can discover the project quickly. ## Documentation sections diff --git a/src/routes/docs/[...slug]/+page.ts b/src/routes/docs/[...slug]/+page.ts index cb75c915..98db7817 100644 --- a/src/routes/docs/[...slug]/+page.ts +++ b/src/routes/docs/[...slug]/+page.ts @@ -6,7 +6,7 @@ export const prerender = true; const navPaths = docsNavigation.flatMap((section) => section.pages.map((page) => page.url)); export function entries() { - return docs.map((doc) => ({ slug: doc.slug })); + return docs.map((doc) => ({ slug: doc.path.slug })); } export function load({ params }) { @@ -16,7 +16,7 @@ export function load({ params }) { throw error(404, 'Documentation page not found'); } - const index = navPaths.indexOf(doc.path); + const index = navPaths.indexOf(doc.path.pathname); const prevPath = index > 0 ? navPaths[index - 1] : null; const nextPath = index !== -1 && index < navPaths.length - 1 ? navPaths[index + 1] : null; @@ -26,8 +26,12 @@ export function load({ params }) { return { doc, prevDoc, nextDoc }; } -function toDocLink(path: string) { - const doc = docsByPath.get(path); +function toDocLink(pathname: string) { + const doc = docsByPath.get(pathname); if (!doc) return null; - return { slug: doc.slug, title: doc.metadata.title, path: doc.path }; + return { + slug: doc.path.slug, + title: doc?.head?.title || doc.title, + path: doc.path.pathname + }; } diff --git a/src/routes/layout.css b/src/routes/layout.css index e72407e8..83ce1694 100644 --- a/src/routes/layout.css +++ b/src/routes/layout.css @@ -18,4 +18,8 @@ background-color: var(--kit-color-surface); color: var(--kit-color-text); } + + .lpk-title-2xl { + font-size: calc(var(--kit-font-xl) * 1.25); + } } diff --git a/src/routes/md.css b/src/routes/md.css index 9e9825de..a3b80f8b 100644 --- a/src/routes/md.css +++ b/src/routes/md.css @@ -60,14 +60,22 @@ @layer kit-prose { /* ── Container ────────────────────────────────────── */ .kit-prose { + --md-color-text: var(--kit-color-text); + --md-color-muted: var(--kit-color-text-muted); + --md-color-link: var(--kit-color-accent); + /* --md-color-link-hover: #79b8ff; */ + --md-color-border: var(--kit-color-border); + --md-color-bg: var(--kit-color-surface); + --md-color-surface: var(--kit-color-surface-1); + font-family: var(--md-font-sans); font-size: var(--md-font-size); line-height: var(--md-line-height); color: var(--md-color-text); background: var(--md-color-bg); - max-width: var(--md-max-width); + /* max-width: var(--md-max-width); */ margin: 0 auto; - padding: var(--md-space-xl) var(--md-space-lg); + /* padding: var(--md-space-xl) var(--md-space-lg); */ word-wrap: break-word; overflow-wrap: break-word; } @@ -84,6 +92,26 @@ margin-bottom: 0; } + /* ── Heading ──────────────────────────────────────────────── */ + + .kit-prose header { + margin-bottom: 24px; + } + + .kit-prose .kit-prose-section { + text-transform: uppercase; + color: var(--md-color-muted); + font-size: 13px; + } + + .kit-prose .kit-prose-title { + margin-top: calc(var(--kit-space-comfortable) + var(--kit-space-default)); + } + + .kit-prose .kit-prose-summary { + margin: 24px 0; + } + /* ── Title h1 – h6 ─────────────────────────────────────────── */ .kit-prose h1, .kit-prose h2, @@ -237,7 +265,7 @@ border: 1px solid var(--md-color-border); border-radius: 4px; padding: 0.1em 0.35em; - white-space: nowrap; + /* white-space: nowrap; */ } /* ── Block of code (pre > code) ─────────────────────────────── */ diff --git a/src/routes/page-v2.svelte b/src/routes/page-v2.svelte new file mode 100644 index 00000000..2a411325 --- /dev/null +++ b/src/routes/page-v2.svelte @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/routes/routes.json b/src/routes/routes.json new file mode 100644 index 00000000..26eb8907 --- /dev/null +++ b/src/routes/routes.json @@ -0,0 +1,9 @@ +{ + "/": { + "title": "Lapikit", + "head": { + "title": "Lapikit", + "description": "Lapikit is a fast Svelte component library with reusable components, hooks, theming tools and documentation built for speed and discoverability." + } + } +} diff --git a/src/routes/sitemap.xml/+server.ts b/src/routes/sitemap.xml/+server.ts index 0e1aa4ec..f7077e18 100644 --- a/src/routes/sitemap.xml/+server.ts +++ b/src/routes/sitemap.xml/+server.ts @@ -1,34 +1,17 @@ -import { docsPaths } from '$lib'; +import manifest from '../../manifest.json'; import { PUBLIC_BASE_URL } from '$env/static/public'; export const prerender = true; -const pageModules = import.meta.glob('/src/routes/**/+page.svelte'); -const markdownModules = import.meta.glob('/src/routes/**/+page.md'); - -function toUrl(path: string) { - const route = path - .replace('/src/routes', '') - .replace(/\/\+page\.(svelte|md)$/, '') - .replace(/\/$/, ''); - - return route || '/'; -} - export async function GET() { const baseUrl = PUBLIC_BASE_URL.replace(/\/$/, ''); - - const routes = [...Object.keys(pageModules), ...Object.keys(markdownModules)] - .map(toUrl) - .filter((route) => !route.includes('[')) - .filter((route) => !route.includes('(')) - .concat(docsPaths); - - const uniqueRoutes = [...new Set(routes)].sort(); + const routes = [ + ...new Set((manifest as { path: { pathname: string } }[]).map((entry) => entry.path.pathname)) + ].sort(); const body = ` -${uniqueRoutes +${routes .map( (route) => ` ${baseUrl}${route} diff --git a/src/routes/theme.css b/src/routes/theme.css index 9d7725be..c3e9f1b2 100644 --- a/src/routes/theme.css +++ b/src/routes/theme.css @@ -1,3 +1,18 @@ +:root { + --kit-color-bg-inverse: var(--kit-color-text); + --kit-color-fg-inverse: var(--kit-color-surface); + --kit-color-svelte: hsl(14.02deg 96.06% 49.8%); + --kit-color-svelte-2: hsl(17.14deg 63.64% 97.84%); +} + +.transition-lapikit { + transition: + background 140ms ease, + color 140ms ease, + box-shadow 140ms ease, + translate 140ms ease; +} + html[data-kit-theme='light'] { color-scheme: light; /* --kit-bg: hsl(0 0% 100%); @@ -15,13 +30,13 @@ html[data-kit-theme='light'] { --kit-focus: hsl(35, 90%, 56%); */ /** markdown theme variables */ - --md-color-text: #1a1a1a; + /* --md-color-text: #1a1a1a; --md-color-muted: #555; - --md-color-link: #0969da; + --md-color-link: #0969da; */ --md-color-link-hover: #0550ae; - --md-color-border: #d0d7de; + /* --md-color-border: #d0d7de; --md-color-bg: #ffffff; - --md-color-surface: #f6f8fa; + --md-color-surface: #f6f8fa; */ --md-color-code-bg: #f0f2f5; --md-color-code-text: #c7254e; --md-color-blockquote: #57606a; @@ -34,14 +49,14 @@ html[data-kit-theme='light'] { @media (prefers-color-scheme: dark) { html:not([data-kit-theme='light']) { color-scheme: dark; - --md-color-text: #e6edf3; + /* --md-color-text: #e6edf3; --md-color-muted: #8b949e; - --md-color-link: #58a6ff; + --md-color-link: #58a6ff; */ --md-color-link-hover: #79b8ff; - --md-color-border: #30363d; + /* --md-color-border: #30363d; */ /* --md-color-bg: hsl(222 20% 9%); */ - --md-color-bg: var(--kit-color-surface); - --md-color-surface: hsl(222 18% 15%); + /* --md-color-bg: var(--kit-color-surface); + --md-color-surface: hsl(222 18% 15%); */ --md-color-code-bg: hsl(222 16% 14%); --md-color-code-text: #ff7b72; --md-color-blockquote: #8b949e; @@ -92,13 +107,13 @@ html[data-theme='dark'] div#kit-app { html[data-kit-theme='dark'] { color-scheme: dark; - --md-color-text: #e6edf3; + /* --md-color-text: #e6edf3; --md-color-muted: #8b949e; - --md-color-link: #58a6ff; + --md-color-link: #58a6ff; */ --md-color-link-hover: #79b8ff; - --md-color-border: #30363d; + /* --md-color-border: #30363d; --md-color-bg: var(--kit-color-surface); - --md-color-surface: hsl(222 18% 15%); + --md-color-surface: hsl(222 18% 15%); */ --md-color-code-bg: hsl(222 16% 14%); --md-color-code-text: #ff7b72; --md-color-blockquote: #8b949e; diff --git a/src/templates/doc-page.svelte b/src/templates/doc-page.svelte new file mode 100644 index 00000000..cd1827a3 --- /dev/null +++ b/src/templates/doc-page.svelte @@ -0,0 +1,221 @@ + + + + +
+
+
+ + + {#if data?.doc?.state === 'deprecated'} + + This feature is deprecated and is no longer supported. Check out the new features and + improvements in the documentation. + + {:else} + {#if category} +
{category}
+ {/if} + {#if title} +

+ {capitalize(title)} +

+ {/if} + {/if} +
+ + {#if summary.length > 0} + + + {#snippet activator()} + + + + + On this page + {/snippet} + + + + + {/if} + + {@render children?.()} + + {#if data?.doc?.state !== 'deprecated' && (data.prevDoc || data.nextDoc)} + +
+ {#if data.prevDoc} + + {#snippet prepend()} + + + + {/snippet} + {capitalize(data.prevDoc.title)} + + {/if} + {#if data.nextDoc} + + {#snippet append()} + + + + {/snippet} + {capitalize(data.nextDoc.title)} + + {/if} +
+ {/if} +
+ + {#if summary.length > 0} + + {/if} +
+ + diff --git a/src/templates/doc-section.svelte b/src/templates/doc-section.svelte new file mode 100644 index 00000000..569c733c --- /dev/null +++ b/src/templates/doc-section.svelte @@ -0,0 +1,113 @@ + + +
+
+
+ + + {#if category} +
{category}
+ {/if} + {#if title} +

+ {capitalize(title)} +

+ {/if} +
+ + {@render children?.()} + + {#if data?.doc?.state !== 'deprecated' && (data.prevDoc || data.nextDoc)} + +
+ {#if data.prevDoc} + + {#snippet prepend()} + + + + {/snippet} + {capitalize(data.prevDoc.title)} + + {/if} + {#if data.nextDoc} + + {#snippet append()} + + + + {/snippet} + {capitalize(data.nextDoc.title)} + + {/if} +
+ {/if} +
+
+ + diff --git a/src/templates/doc.svelte b/src/templates/doc.svelte deleted file mode 100644 index 4df3e6e0..00000000 --- a/src/templates/doc.svelte +++ /dev/null @@ -1,217 +0,0 @@ - - - - -
- - - - -
- - (navOpen = true)} aria-label="open navigation"> - {#snippet prepend()} - - - - {/snippet} - - Menu - - - - - - {#snippet activator({ toggle, open }: ModelDropdownProps)} - toggle(e.currentTarget as HTMLElement)}> - On this page - - {#snippet append()} - - {#if open} - - {:else} - - {/if} - - {/snippet} - - {/snippet} - - - - -
- - -
- {@render children?.()} -
- - - {#if data.prevDoc} - - {#snippet prepend()} - - - - {/snippet} - {capitalize(data.prevDoc.title)} - - {/if} - - {#if data.nextDoc} - - {#snippet append()} - - - - {/snippet} - {capitalize(data.nextDoc.title)} - - {/if} - -
- - -
- - -
- - diff --git a/src/templates/legacy.svelte b/src/templates/legacy.svelte deleted file mode 100644 index c7fa47df..00000000 --- a/src/templates/legacy.svelte +++ /dev/null @@ -1,99 +0,0 @@ - - - - -
- - - - -
- - (navOpen = true)} aria-label="open navigation"> - {#snippet prepend()} - - - - {/snippet} - - Menu - - - - - -
- - -
- - This feature is deprecated and is no longer supported. Check out the new features and - improvements in the documentation. - - - {@render children?.()} -
-
-
-
diff --git a/src/templates/page.svelte b/src/templates/page.svelte index 749d5595..5776ab01 100644 --- a/src/templates/page.svelte +++ b/src/templates/page.svelte @@ -1,119 +1,146 @@ - - - - Lapikit - - - - -
- - Documentation - - {@html githubIcon} - -
-
- -
-
- - - - - {#snippet activator({ toggle, open }: ModelDropdownProps)} - toggle(e.currentTarget as HTMLElement)}> - On this page - - {#snippet append()} - - {#if open} - - {:else} - - {/if} - - {/snippet} - - {/snippet} - - - - -
+
+
+
-
- {@render children?.()} -
-
- - -
- - -
+ {#if category} +
{category}
+ {/if} + {#if title} +

+ {capitalize(title)} +

+ {/if} + + + {#if summary.length > 0} + + + {#snippet activator()} + + + + + On this page + {/snippet} + + + + + {/if} + + {@render children?.()} + + + {#if summary.length > 0} + + {/if} + diff --git a/src/templates/section.svelte b/src/templates/section.svelte deleted file mode 100644 index e8c2a96a..00000000 --- a/src/templates/section.svelte +++ /dev/null @@ -1,119 +0,0 @@ - - - - -
- - - - -
- - (navOpen = true)} aria-label="open navigation"> - {#snippet prepend()} - - - - {/snippet} - - Menu - - - - - -
- - -
- {@render children?.()} -
-
- - -
-
- - diff --git a/svelte.config.js b/svelte.config.js index 8ce14236..7be68a89 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -1,5 +1,6 @@ import { mdsvex } from 'mdsvex'; import adapter from '@sveltejs/adapter-node'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; import { mdsvexOptions } from './mdsvex.config.js'; import { lapikitPreprocess } from 'lapikit/preprocess'; @@ -16,7 +17,7 @@ const config = { $examples: 'src/content/examples' } }, - preprocess: [mdsvex(mdsvexOptions), lapikitPreprocess({ plugins: ['repl'] })], + preprocess: [mdsvex(mdsvexOptions), lapikitPreprocess({ plugins: ['repl'] }), vitePreprocess()], extensions: ['.svelte', '.svx', '.md'] };