diff --git a/package.json b/package.json index 93c04b1..a6fc83d 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "@lucide/astro": "^0.488.0", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.14", - "@radix-ui/react-dropdown-menu": "^2.1.6", + "@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-navigation-menu": "^1.2.5", "@radix-ui/react-slot": "^1.2.3", "@scalar/api-reference-react": "^0.6.19", @@ -52,6 +52,8 @@ "rehype-mermaid": "^3.0.0", "remark-directive": "^4.0.0", "remark-toc": "^9.0.0", + "roboto": "link:@types/@fontsource/roboto", + "satori": "^0.18.3", "tailwind-merge": "^3.0.2", "tailwindcss": "^4.0.17", "tw-animate-css": "^1.2.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0ef770b..ca6af17 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,7 +33,7 @@ dependencies: specifier: ^1.1.14 version: 1.1.14(@types/react-dom@19.1.6)(@types/react@19.1.8)(react-dom@19.1.0)(react@19.1.0) '@radix-ui/react-dropdown-menu': - specifier: ^2.1.6 + specifier: ^2.1.15 version: 2.1.15(@types/react-dom@19.1.6)(@types/react@19.1.8)(react-dom@19.1.0)(react@19.1.0) '@radix-ui/react-navigation-menu': specifier: ^1.2.5 @@ -134,6 +134,12 @@ dependencies: remark-toc: specifier: ^9.0.0 version: 9.0.0 + roboto: + specifier: link:@types/@fontsource/roboto + version: link:@types/@fontsource/roboto + satori: + specifier: ^0.18.3 + version: 0.18.3 tailwind-merge: specifier: ^3.0.2 version: 3.3.1 @@ -2597,6 +2603,15 @@ packages: /@shikijs/vscode-textmate@10.0.2: resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + /@shuding/opentype.js@1.4.0-beta.0: + resolution: {integrity: sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==} + engines: {node: '>= 8.0.0'} + hasBin: true + dependencies: + fflate: 0.7.4 + string.prototype.codepointat: 0.2.1 + dev: false + /@swc/helpers@0.5.17: resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} dependencies: @@ -3607,6 +3622,11 @@ packages: resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} dev: false + /base64-js@0.0.8: + resolution: {integrity: sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==} + engines: {node: '>= 0.4'} + dev: false + /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: false @@ -3671,6 +3691,10 @@ packages: engines: {node: '>=16'} dev: false + /camelize@1.0.1: + resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} + dev: false + /caniuse-lite@1.0.30001726: resolution: {integrity: sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==} dev: false @@ -3834,7 +3858,6 @@ packages: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} requiresBuild: true dev: false - optional: true /color-string@1.9.1: resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} @@ -3935,6 +3958,24 @@ packages: uncrypto: 0.1.3 dev: false + /css-background-parser@0.1.0: + resolution: {integrity: sha512-2EZLisiZQ+7m4wwur/qiYJRniHX4K5Tc9w93MT3AS0WS1u5kaZ4FKXlOTBhOjc+CgEgPiGY+fX1yWD8UwpEqUA==} + dev: false + + /css-box-shadow@1.0.0-3: + resolution: {integrity: sha512-9jaqR6e7Ohds+aWwmhe6wILJ99xYQbfmK9QQB9CcMjDbTxPZjwEmUQpU91OG05Xgm8BahT5fW+svbsQGjS/zPg==} + dev: false + + /css-color-keywords@1.0.0: + resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} + engines: {node: '>=4'} + dev: false + + /css-gradient-parser@0.0.17: + resolution: {integrity: sha512-w2Xy9UMMwlKtou0vlRnXvWglPAceXCTtcmVSo8ZBUvqCV5aXEFP/PC6d+I464810I9FT++UACwTD5511bmGPUg==} + engines: {node: '>=16'} + dev: false + /css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} dependencies: @@ -3949,6 +3990,14 @@ packages: resolution: {integrity: sha512-gJMigczVZqYAk0hPVzx/M4Hm1D9QOtqkdQk9005TNzDIUGzo5cnHEDiKUT7jGPximL/oYb+LIitcHFQ4aKupxg==} dev: false + /css-to-react-native@3.2.0: + resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} + dependencies: + camelize: 1.0.1 + css-color-keywords: 1.0.0 + postcss-value-parser: 4.2.0 + dev: false + /css-tree@2.2.1: resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -4447,6 +4496,11 @@ packages: resolution: {integrity: sha512-wObbz/ar3Bc6e4X5vf0iO8xTN8YAjN/tgiAOJLr7yjYFtP9wAjq8Mb5h0yn6kResir+VYx2DXBj9NNobs0ETSA==} dev: false + /emoji-regex-xs@2.0.1: + resolution: {integrity: sha512-1QFuh8l7LqUcKe24LsPUNzjrzJQ7pgRwp1QMcZ5MX6mFplk2zQ08NVCM84++1cveaUUYtcCYHmeFEuNg16sU4g==} + engines: {node: '>=10.0.0'} + dev: false + /emoji-regex@10.4.0: resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} dev: false @@ -4573,6 +4627,10 @@ packages: engines: {node: '>=6'} dev: false + /escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + dev: false + /escape-string-regexp@5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} @@ -4680,6 +4738,10 @@ packages: picomatch: 4.0.2 dev: false + /fflate@0.7.4: + resolution: {integrity: sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==} + dev: false + /flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} dev: false @@ -5157,6 +5219,11 @@ packages: space-separated-tokens: 2.0.2 dev: false + /hex-rgb@4.3.0: + resolution: {integrity: sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==} + engines: {node: '>=6'} + dev: false + /highlight.js@11.11.1: resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} engines: {node: '>=12.0.0'} @@ -5504,6 +5571,13 @@ packages: lightningcss-win32-x64-msvc: 1.30.1 dev: false + /linebreak@1.1.0: + resolution: {integrity: sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==} + dependencies: + base64-js: 0.0.8 + unicode-trie: 2.0.0 + dev: false + /local-pkg@0.5.1: resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} engines: {node: '>=14'} @@ -6467,6 +6541,13 @@ packages: resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} dev: false + /parse-css-color@0.2.1: + resolution: {integrity: sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg==} + dependencies: + color-name: 1.1.4 + hex-rgb: 4.3.0 + dev: false + /parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} dependencies: @@ -7121,6 +7202,23 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: false + /satori@0.18.3: + resolution: {integrity: sha512-T3DzWNmnrfVmk2gCIlAxLRLbGkfp3K7TyRva+Byyojqu83BNvnMeqVeYRdmUw4TKCsyH4RiQ/KuF/I4yEzgR5A==} + engines: {node: '>=16'} + dependencies: + '@shuding/opentype.js': 1.4.0-beta.0 + css-background-parser: 0.1.0 + css-box-shadow: 1.0.0-3 + css-gradient-parser: 0.0.17 + css-to-react-native: 3.2.0 + emoji-regex-xs: 2.0.1 + escape-html: 1.0.3 + linebreak: 1.1.0 + parse-css-color: 0.2.1 + postcss-value-parser: 4.2.0 + yoga-layout: 3.2.1 + dev: false + /sax@1.4.3: resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==} dev: false @@ -7252,6 +7350,10 @@ packages: strip-ansi: 7.1.0 dev: false + /string.prototype.codepointat@0.2.1: + resolution: {integrity: sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==} + dev: false + /stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} dependencies: @@ -8026,6 +8128,10 @@ packages: engines: {node: '>=18'} dev: false + /yoga-layout@3.2.1: + resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} + dev: false + /zhead@2.2.4: resolution: {integrity: sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag==} dev: false diff --git a/src/assets/Montserrat-Medium.ttf b/src/assets/Montserrat-Medium.ttf new file mode 100644 index 0000000..c9a39ea Binary files /dev/null and b/src/assets/Montserrat-Medium.ttf differ diff --git a/src/lib/components/content/thumbnail.tsx b/src/lib/components/content/thumbnail.tsx new file mode 100644 index 0000000..6ed8011 --- /dev/null +++ b/src/lib/components/content/thumbnail.tsx @@ -0,0 +1,124 @@ +import { readFileSync } from "fs"; +import { resolve } from "path"; +import satori from "satori"; + +export function loadFont(name: string) { + return readFileSync(resolve(`src/assets/${name}.ttf`)); +} + +function extractCSSVariables() { + const css = readFileSync("src/assets/css/global.css", "utf8"); + const vars: Record = {}; + const regex = /--([^:]+):\s*([^;]+)/g; + let match; + while ((match = regex.exec(css))) { + vars[`--${match[1]}`] = match[2].trim(); + } + return vars; +} + +function resolveTailwindColor(value: string): string { + if (/^\d+\s+\d+\s+\d+$/.test(value)) { + return `rgb(${value})`; + } + + // Cas: #00dc82 ou hsl() → OK + return value; +} + +const montserrat = loadFont("Montserrat-Medium"); +const cssVars = extractCSSVariables(); + +export async function generateThumbnail( + headline: string, + title: string, + description: string, +) { + const primaryColor = resolveTailwindColor(cssVars["--primary"]); + return satori( +
+ + + + + + + + + + + + + +
+

+ {headline} +

+

+ {title} +

+

+ {description} +

+
+
, + { + width: 960, + height: 540, + fonts: [ + { + name: "Montserrat", + data: montserrat, + weight: 400, + style: "normal", + }, + ], + }, + ); +} diff --git a/src/lib/components/elements/doc-switcher.tsx b/src/lib/components/elements/doc-switcher.tsx new file mode 100644 index 0000000..3941f85 --- /dev/null +++ b/src/lib/components/elements/doc-switcher.tsx @@ -0,0 +1,63 @@ +import { Icon } from "@iconify/react"; +import { Button } from "../ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuTrigger, +} from "../ui/dropdown-menu"; + +type Props = { + current: any; + items: any[]; +}; + +export default function DocSwitcher(props: Props) { + return ( + + + + + + + Documentations + + {props.items.map((element) => ( + setActiveTeam(team)} + className="gap-2 p-2 text-muted" + > +
+ +
+ {element.data.label} +
+ ))} +
+
+ ); +} diff --git a/src/lib/components/ui/dropdown-menu.tsx b/src/lib/components/ui/dropdown-menu.tsx index e27639f..35b99ea 100644 --- a/src/lib/components/ui/dropdown-menu.tsx +++ b/src/lib/components/ui/dropdown-menu.tsx @@ -1,13 +1,13 @@ -import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" -import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react" -import * as React from "react" +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; +import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"; +import * as React from "react"; -import { cn } from "@/utils" +import { cn } from "@/utils"; function DropdownMenu({ ...props }: React.ComponentProps) { - return + return ; } function DropdownMenuPortal({ @@ -15,7 +15,7 @@ function DropdownMenuPortal({ }: React.ComponentProps) { return ( - ) + ); } function DropdownMenuTrigger({ @@ -26,7 +26,7 @@ function DropdownMenuTrigger({ data-slot="dropdown-menu-trigger" {...props} /> - ) + ); } function DropdownMenuContent({ @@ -41,12 +41,12 @@ function DropdownMenuContent({ sideOffset={sideOffset} className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md", - className + className, )} {...props} /> - ) + ); } function DropdownMenuGroup({ @@ -54,7 +54,7 @@ function DropdownMenuGroup({ }: React.ComponentProps) { return ( - ) + ); } function DropdownMenuItem({ @@ -63,8 +63,8 @@ function DropdownMenuItem({ variant = "default", ...props }: React.ComponentProps & { - inset?: boolean - variant?: "default" | "destructive" + inset?: boolean; + variant?: "default" | "destructive"; }) { return ( - ) + ); } function DropdownMenuCheckboxItem({ @@ -91,7 +91,7 @@ function DropdownMenuCheckboxItem({ data-slot="dropdown-menu-checkbox-item" className={cn( "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", - className + className, )} checked={checked} {...props} @@ -103,7 +103,7 @@ function DropdownMenuCheckboxItem({ {children} - ) + ); } function DropdownMenuRadioGroup({ @@ -114,7 +114,7 @@ function DropdownMenuRadioGroup({ data-slot="dropdown-menu-radio-group" {...props} /> - ) + ); } function DropdownMenuRadioItem({ @@ -127,7 +127,7 @@ function DropdownMenuRadioItem({ data-slot="dropdown-menu-radio-item" className={cn( "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", - className + className, )} {...props} > @@ -138,7 +138,7 @@ function DropdownMenuRadioItem({ {children} - ) + ); } function DropdownMenuLabel({ @@ -146,7 +146,7 @@ function DropdownMenuLabel({ inset, ...props }: React.ComponentProps & { - inset?: boolean + inset?: boolean; }) { return ( - ) + ); } function DropdownMenuSeparator({ @@ -171,7 +171,7 @@ function DropdownMenuSeparator({ className={cn("bg-border -mx-1 my-1 h-px", className)} {...props} /> - ) + ); } function DropdownMenuShortcut({ @@ -183,17 +183,17 @@ function DropdownMenuShortcut({ data-slot="dropdown-menu-shortcut" className={cn( "text-muted-foreground ml-auto text-xs tracking-widest", - className + className, )} {...props} /> - ) + ); } function DropdownMenuSub({ ...props }: React.ComponentProps) { - return + return ; } function DropdownMenuSubTrigger({ @@ -202,22 +202,22 @@ function DropdownMenuSubTrigger({ children, ...props }: React.ComponentProps & { - inset?: boolean + inset?: boolean; }) { return ( {children} - ) + ); } function DropdownMenuSubContent({ @@ -229,19 +229,27 @@ function DropdownMenuSubContent({ data-slot="dropdown-menu-sub-content" className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg", - className + className, )} {...props} /> - ) + ); } export { - DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, - DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuPortal, + DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, - DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger -} - + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuTrigger, +}; diff --git a/src/lib/layouts/BaseLayout.astro b/src/lib/layouts/BaseLayout.astro index 898fcc1..a31d0fa 100644 --- a/src/lib/layouts/BaseLayout.astro +++ b/src/lib/layouts/BaseLayout.astro @@ -7,7 +7,7 @@ import "../../assets/css/global.css"; import { useDocumentation } from "@/utils"; import * as astroContent from "astro:content"; -const { title = config.seo.title } = Astro.props; +const { title = config.seo.title, thumbnail, description } = Astro.props; const { load, flattenDocs } = useDocumentation(astroContent); const loaded = await load(); @@ -46,13 +46,16 @@ const searchableDoc = await Promise.all( - - + + - + - + diff --git a/src/lib/layouts/DocsLayout.astro b/src/lib/layouts/DocsLayout.astro index a6b7e89..8ed6b70 100644 --- a/src/lib/layouts/DocsLayout.astro +++ b/src/lib/layouts/DocsLayout.astro @@ -1,5 +1,4 @@ --- -import Separator from "@/components/elements/separator"; import { Icon } from "@iconify/react"; import clsx from "clsx"; import "../../assets/css/global.css"; @@ -8,6 +7,9 @@ import BaseLayout from "./BaseLayout.astro"; import { useDocumentation } from "@/utils"; import * as astroContent from "astro:content"; import Collapsible from "@/components/elements/collapsible"; +import DocSwitcher from "@/components/elements/doc-switcher"; +import { generateThumbnail } from "@/components/content/thumbnail"; +import config from "../../../explainer.config"; const { load, flattenDocs } = useDocumentation(astroContent); const documentations = await load(); @@ -23,9 +25,22 @@ function findFlattenCollection(id: string) { const doc = documentations.find((element) => element.id === id); return flattenDocs(doc.children); } + +const { title, description } = Astro.props; + +const svg = await generateThumbnail( + currentCollection.data.label, + title, + description, +); +const svgUri = `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`; --- - +
@@ -35,54 +50,18 @@ function findFlattenCollection(id: string) { > { documentations.length > 1 && ( - - - -
-
+
+ +
) }
{ currentCollection?.children.map((children: any) => { @@ -126,6 +105,18 @@ function findFlattenCollection(id: string) {