mordoc 0.1.13 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/bin/cli.js +24 -71
- package/dist/cli/asset-rewrite.d.ts +40 -0
- package/dist/cli/asset-rewrite.d.ts.map +1 -0
- package/dist/cli/asset-rewrite.js +67 -0
- package/dist/cli/asset-rewrite.js.map +1 -0
- package/dist/cli/build.d.ts +45 -15
- package/dist/cli/build.d.ts.map +1 -1
- package/dist/cli/build.js +186 -101
- package/dist/cli/build.js.map +1 -1
- package/dist/cli/dev.d.ts +26 -14
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/dev.js +116 -159
- package/dist/cli/dev.js.map +1 -1
- package/dist/cli/pagefind-indexer.d.ts +15 -0
- package/dist/cli/pagefind-indexer.d.ts.map +1 -0
- package/dist/cli/pagefind-indexer.js +68 -0
- package/dist/cli/pagefind-indexer.js.map +1 -0
- package/dist/cli/paths.d.ts +26 -0
- package/dist/cli/paths.d.ts.map +1 -0
- package/dist/cli/paths.js +32 -0
- package/dist/cli/paths.js.map +1 -0
- package/dist/cli/ssg-runner.d.ts +58 -0
- package/dist/cli/ssg-runner.d.ts.map +1 -0
- package/dist/cli/ssg-runner.js +126 -0
- package/dist/cli/ssg-runner.js.map +1 -0
- package/dist/config/assets-loader.d.ts +11 -0
- package/dist/config/assets-loader.d.ts.map +1 -0
- package/dist/config/assets-loader.js +46 -0
- package/dist/config/assets-loader.js.map +1 -0
- package/dist/config/language-loader.d.ts +11 -0
- package/dist/config/language-loader.d.ts.map +1 -0
- package/dist/config/language-loader.js +61 -0
- package/dist/config/language-loader.js.map +1 -0
- package/dist/config/language.d.ts +11 -0
- package/dist/config/language.d.ts.map +1 -0
- package/dist/config/language.js +61 -0
- package/dist/config/language.js.map +1 -0
- package/dist/config/sidenav-loader.d.ts +14 -0
- package/dist/config/sidenav-loader.d.ts.map +1 -0
- package/dist/config/sidenav-loader.js +78 -0
- package/dist/config/sidenav-loader.js.map +1 -0
- package/dist/config/site-loader.d.ts +11 -0
- package/dist/config/site-loader.d.ts.map +1 -0
- package/dist/config/site-loader.js +72 -0
- package/dist/config/site-loader.js.map +1 -0
- package/dist/config/site.d.ts +11 -0
- package/dist/config/site.d.ts.map +1 -0
- package/dist/config/site.js +72 -0
- package/dist/config/site.js.map +1 -0
- package/dist/config/topnav-loader.d.ts +10 -0
- package/dist/config/topnav-loader.d.ts.map +1 -0
- package/dist/config/topnav-loader.js +78 -0
- package/dist/config/topnav-loader.js.map +1 -0
- package/dist/config/translations-loader.d.ts +7 -0
- package/dist/config/translations-loader.d.ts.map +1 -0
- package/dist/config/translations-loader.js +51 -0
- package/dist/config/translations-loader.js.map +1 -0
- package/dist/content/content-loader.d.ts +16 -0
- package/dist/content/content-loader.d.ts.map +1 -0
- package/dist/content/content-loader.js +126 -0
- package/dist/content/content-loader.js.map +1 -0
- package/dist/content/content-parser.d.ts +12 -0
- package/dist/content/content-parser.d.ts.map +1 -0
- package/dist/content/content-parser.js +54 -0
- package/dist/content/content-parser.js.map +1 -0
- package/dist/content/content-transformer.d.ts +17 -0
- package/dist/content/content-transformer.d.ts.map +1 -0
- package/dist/content/content-transformer.js +94 -0
- package/dist/content/content-transformer.js.map +1 -0
- package/dist/content/markdoc-config.d.ts +13 -0
- package/dist/content/markdoc-config.d.ts.map +1 -0
- package/dist/content/markdoc-config.js +210 -0
- package/dist/content/markdoc-config.js.map +1 -0
- package/dist/content/slug.d.ts +26 -0
- package/dist/content/slug.d.ts.map +1 -0
- package/dist/content/slug.js +31 -0
- package/dist/content/slug.js.map +1 -0
- package/dist/pipeline.d.ts +61 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +123 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/types/assets.d.ts +15 -0
- package/dist/types/assets.d.ts.map +1 -0
- package/dist/types/assets.js +2 -0
- package/dist/types/assets.js.map +1 -0
- package/dist/types/content.d.ts +114 -32
- package/dist/types/content.d.ts.map +1 -1
- package/dist/types/content.js +1 -6
- package/dist/types/content.js.map +1 -1
- package/dist/types/language.d.ts +9 -0
- package/dist/types/language.d.ts.map +1 -0
- package/dist/types/language.js +2 -0
- package/dist/types/language.js.map +1 -0
- package/dist/types/navigation.d.ts +24 -36
- package/dist/types/navigation.d.ts.map +1 -1
- package/dist/types/navigation.js +1 -6
- package/dist/types/navigation.js.map +1 -1
- package/dist/types/pipeline.d.ts +70 -0
- package/dist/types/pipeline.d.ts.map +1 -0
- package/dist/types/pipeline.js +2 -0
- package/dist/types/pipeline.js.map +1 -0
- package/dist/types/site.d.ts +20 -0
- package/dist/types/site.d.ts.map +1 -0
- package/dist/types/site.js +2 -0
- package/dist/types/site.js.map +1 -0
- package/dist/utils/lang-utils.d.ts +23 -0
- package/dist/utils/lang-utils.d.ts.map +1 -0
- package/dist/utils/lang-utils.js +40 -0
- package/dist/utils/lang-utils.js.map +1 -0
- package/dist/utils/paths.d.ts +25 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +31 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/vite/plugin.d.ts +93 -0
- package/dist/vite/plugin.d.ts.map +1 -0
- package/dist/vite/plugin.js +435 -0
- package/dist/vite/plugin.js.map +1 -0
- package/package.json +46 -61
- package/src/app/App.module.css +82 -0
- package/src/app/App.tsx +95 -0
- package/src/app/content/Content.module.css +357 -0
- package/src/app/content/Content.tsx +212 -0
- package/src/app/content/callout/Callout.module.css +110 -0
- package/src/app/content/callout/Callout.tsx +82 -0
- package/src/app/content/card/Card.module.css +146 -0
- package/src/app/content/card/Card.tsx +110 -0
- package/src/app/content/card/CardGrid.module.css +22 -0
- package/src/app/content/card/CardGrid.tsx +34 -0
- package/src/app/content/code-block/CodeBlock.module.css +237 -0
- package/src/app/content/code-block/CodeBlock.tsx +123 -0
- package/src/app/content/heading/Heading.module.css +52 -0
- package/src/app/content/heading/Heading.tsx +75 -0
- package/src/app/content/image/Image.module.css +113 -0
- package/src/app/content/image/Image.tsx +98 -0
- package/src/app/content/link/ContentLink.tsx +46 -0
- package/src/app/data-context.tsx +39 -0
- package/src/app/entry-server.tsx +58 -0
- package/src/app/footer/Footer.module.css +36 -0
- package/src/app/footer/Footer.tsx +28 -0
- package/src/app/globals.d.ts +40 -0
- package/src/app/header/Header.module.css +205 -0
- package/src/app/header/Header.tsx +188 -0
- package/src/app/header/LanguagePicker.module.css +97 -0
- package/src/app/header/LanguagePicker.tsx +174 -0
- package/src/app/header/SearchBar.module.css +92 -0
- package/src/app/header/SearchBar.tsx +37 -0
- package/src/app/header/SearchModal.module.css +185 -0
- package/src/app/header/SearchModal.tsx +245 -0
- package/src/app/header/Topnav.module.css +37 -0
- package/src/app/header/Topnav.tsx +30 -0
- package/src/app/index.css +119 -0
- package/src/app/index.html +13 -0
- package/src/app/landing/LandingPage.module.css +14 -0
- package/src/app/landing/LandingPage.tsx +62 -0
- package/src/app/landing/button/Button.module.css +37 -0
- package/src/app/landing/button/Button.tsx +50 -0
- package/src/app/landing/hero/Hero.module.css +108 -0
- package/src/app/landing/hero/Hero.tsx +81 -0
- package/src/app/landing/section/Section.module.css +100 -0
- package/src/app/landing/section/Section.tsx +61 -0
- package/src/app/lang-utils.ts +11 -0
- package/src/app/main.tsx +67 -0
- package/src/app/not-found/NotFound.module.css +92 -0
- package/src/app/not-found/NotFound.tsx +34 -0
- package/src/app/routes.tsx +86 -0
- package/src/app/sidenav/Sidenav.module.css +239 -0
- package/src/app/sidenav/Sidenav.tsx +182 -0
- package/src/app/skeleton/Skeleton.module.css +100 -0
- package/src/app/skeleton/Skeleton.tsx +75 -0
- package/src/app/toc/Toc.module.css +59 -0
- package/src/app/toc/Toc.tsx +77 -0
- package/src/app/tsconfig.json +18 -0
- package/src/app/virtual-modules.d.ts +74 -0
- package/dist/assets/fonts/inter/Inter-Italic-VariableFont_opsz,wght.ttf +0 -0
- package/dist/assets/fonts/inter/Inter-VariableFont_opsz,wght.ttf +0 -0
- package/dist/assets/fonts/inter/OFL.txt +0 -93
- package/dist/build/Builder.d.ts +0 -92
- package/dist/build/Builder.d.ts.map +0 -1
- package/dist/build/Builder.js +0 -394
- package/dist/build/Builder.js.map +0 -1
- package/dist/build/ClientBundler.d.ts +0 -48
- package/dist/build/ClientBundler.d.ts.map +0 -1
- package/dist/build/ClientBundler.js +0 -169
- package/dist/build/ClientBundler.js.map +0 -1
- package/dist/build/HtmlGenerator.d.ts +0 -46
- package/dist/build/HtmlGenerator.d.ts.map +0 -1
- package/dist/build/HtmlGenerator.js +0 -190
- package/dist/build/HtmlGenerator.js.map +0 -1
- package/dist/build/SearchIndexer.d.ts +0 -31
- package/dist/build/SearchIndexer.d.ts.map +0 -1
- package/dist/build/SearchIndexer.js +0 -116
- package/dist/build/SearchIndexer.js.map +0 -1
- package/dist/bundles/client.js +0 -126
- package/dist/cli/create-app.d.ts +0 -14
- package/dist/cli/create-app.d.ts.map +0 -1
- package/dist/cli/create-app.js +0 -241
- package/dist/cli/create-app.js.map +0 -1
- package/dist/client/App.d.ts +0 -17
- package/dist/client/App.d.ts.map +0 -1
- package/dist/client/App.js +0 -91
- package/dist/client/App.js.map +0 -1
- package/dist/client/contexts/ConfigContext.d.ts +0 -22
- package/dist/client/contexts/ConfigContext.d.ts.map +0 -1
- package/dist/client/contexts/ConfigContext.js +0 -27
- package/dist/client/contexts/ConfigContext.js.map +0 -1
- package/dist/client/contexts/ContentContext.d.ts +0 -31
- package/dist/client/contexts/ContentContext.d.ts.map +0 -1
- package/dist/client/contexts/ContentContext.js +0 -46
- package/dist/client/contexts/ContentContext.js.map +0 -1
- package/dist/client/contexts/SearchContext.d.ts +0 -38
- package/dist/client/contexts/SearchContext.d.ts.map +0 -1
- package/dist/client/contexts/SearchContext.js +0 -185
- package/dist/client/contexts/SearchContext.js.map +0 -1
- package/dist/client/contexts/ThemeContext.d.ts +0 -23
- package/dist/client/contexts/ThemeContext.d.ts.map +0 -1
- package/dist/client/contexts/ThemeContext.js +0 -53
- package/dist/client/contexts/ThemeContext.js.map +0 -1
- package/dist/client/hooks/useContent.d.ts +0 -12
- package/dist/client/hooks/useContent.d.ts.map +0 -1
- package/dist/client/hooks/useContent.js +0 -74
- package/dist/client/hooks/useContent.js.map +0 -1
- package/dist/client/hooks/useNavigation.d.ts +0 -15
- package/dist/client/hooks/useNavigation.d.ts.map +0 -1
- package/dist/client/hooks/useNavigation.js +0 -101
- package/dist/client/hooks/useNavigation.js.map +0 -1
- package/dist/client/hooks/useSearch.d.ts +0 -22
- package/dist/client/hooks/useSearch.d.ts.map +0 -1
- package/dist/client/hooks/useSearch.js +0 -64
- package/dist/client/hooks/useSearch.js.map +0 -1
- package/dist/client/main.d.ts +0 -5
- package/dist/client/main.d.ts.map +0 -1
- package/dist/client/main.js +0 -66
- package/dist/client/main.js.map +0 -1
- package/dist/components/Callout.d.ts +0 -9
- package/dist/components/Callout.d.ts.map +0 -1
- package/dist/components/Callout.js +0 -24
- package/dist/components/Callout.js.map +0 -1
- package/dist/components/Card.d.ts +0 -10
- package/dist/components/Card.d.ts.map +0 -1
- package/dist/components/Card.js +0 -15
- package/dist/components/Card.js.map +0 -1
- package/dist/components/CardGrid.d.ts +0 -8
- package/dist/components/CardGrid.d.ts.map +0 -1
- package/dist/components/CardGrid.js +0 -9
- package/dist/components/CardGrid.js.map +0 -1
- package/dist/components/CodeBlock.d.ts +0 -28
- package/dist/components/CodeBlock.d.ts.map +0 -1
- package/dist/components/CodeBlock.js +0 -80
- package/dist/components/CodeBlock.js.map +0 -1
- package/dist/components/ContentPage.d.ts +0 -9
- package/dist/components/ContentPage.d.ts.map +0 -1
- package/dist/components/ContentPage.js +0 -102
- package/dist/components/ContentPage.js.map +0 -1
- package/dist/components/Header.d.ts +0 -14
- package/dist/components/Header.d.ts.map +0 -1
- package/dist/components/Header.js +0 -30
- package/dist/components/Header.js.map +0 -1
- package/dist/components/Heading.d.ts +0 -16
- package/dist/components/Heading.d.ts.map +0 -1
- package/dist/components/Heading.js +0 -31
- package/dist/components/Heading.js.map +0 -1
- package/dist/components/Image.d.ts +0 -8
- package/dist/components/Image.d.ts.map +0 -1
- package/dist/components/Image.js +0 -24
- package/dist/components/Image.js.map +0 -1
- package/dist/components/Layout.d.ts +0 -14
- package/dist/components/Layout.d.ts.map +0 -1
- package/dist/components/Layout.js +0 -32
- package/dist/components/Layout.js.map +0 -1
- package/dist/components/MarkdocRenderer.d.ts +0 -15
- package/dist/components/MarkdocRenderer.d.ts.map +0 -1
- package/dist/components/MarkdocRenderer.js +0 -73
- package/dist/components/MarkdocRenderer.js.map +0 -1
- package/dist/components/MobileMenu.d.ts +0 -14
- package/dist/components/MobileMenu.d.ts.map +0 -1
- package/dist/components/MobileMenu.js +0 -45
- package/dist/components/MobileMenu.js.map +0 -1
- package/dist/components/PageNavigation.d.ts +0 -9
- package/dist/components/PageNavigation.d.ts.map +0 -1
- package/dist/components/PageNavigation.js +0 -23
- package/dist/components/PageNavigation.js.map +0 -1
- package/dist/components/SearchModal.d.ts +0 -9
- package/dist/components/SearchModal.d.ts.map +0 -1
- package/dist/components/SearchModal.js +0 -74
- package/dist/components/SearchModal.js.map +0 -1
- package/dist/components/SideNav.d.ts +0 -9
- package/dist/components/SideNav.d.ts.map +0 -1
- package/dist/components/SideNav.js +0 -66
- package/dist/components/SideNav.js.map +0 -1
- package/dist/components/TableOfContents.d.ts +0 -10
- package/dist/components/TableOfContents.d.ts.map +0 -1
- package/dist/components/TableOfContents.js +0 -151
- package/dist/components/TableOfContents.js.map +0 -1
- package/dist/config/ConfigLoader.d.ts +0 -50
- package/dist/config/ConfigLoader.d.ts.map +0 -1
- package/dist/config/ConfigLoader.js +0 -214
- package/dist/config/ConfigLoader.js.map +0 -1
- package/dist/config/StyleCompiler.d.ts +0 -17
- package/dist/config/StyleCompiler.d.ts.map +0 -1
- package/dist/config/StyleCompiler.js +0 -116
- package/dist/config/StyleCompiler.js.map +0 -1
- package/dist/config/ThemeGenerator.d.ts +0 -14
- package/dist/config/ThemeGenerator.d.ts.map +0 -1
- package/dist/config/ThemeGenerator.js +0 -129
- package/dist/config/ThemeGenerator.js.map +0 -1
- package/dist/content/ContentLoader.d.ts +0 -70
- package/dist/content/ContentLoader.d.ts.map +0 -1
- package/dist/content/ContentLoader.js +0 -146
- package/dist/content/ContentLoader.js.map +0 -1
- package/dist/content/ContentProcessor.d.ts +0 -84
- package/dist/content/ContentProcessor.d.ts.map +0 -1
- package/dist/content/ContentProcessor.js +0 -380
- package/dist/content/ContentProcessor.js.map +0 -1
- package/dist/content/RouteManager.d.ts +0 -69
- package/dist/content/RouteManager.d.ts.map +0 -1
- package/dist/content/RouteManager.js +0 -143
- package/dist/content/RouteManager.js.map +0 -1
- package/dist/styles/components/callout.d.ts +0 -11
- package/dist/styles/components/callout.d.ts.map +0 -1
- package/dist/styles/components/callout.js +0 -87
- package/dist/styles/components/callout.js.map +0 -1
- package/dist/styles/components/card.d.ts +0 -11
- package/dist/styles/components/card.d.ts.map +0 -1
- package/dist/styles/components/card.js +0 -179
- package/dist/styles/components/card.js.map +0 -1
- package/dist/styles/components/codeblock.d.ts +0 -11
- package/dist/styles/components/codeblock.d.ts.map +0 -1
- package/dist/styles/components/codeblock.js +0 -251
- package/dist/styles/components/codeblock.js.map +0 -1
- package/dist/styles/components/content.d.ts +0 -11
- package/dist/styles/components/content.d.ts.map +0 -1
- package/dist/styles/components/content.js +0 -197
- package/dist/styles/components/content.js.map +0 -1
- package/dist/styles/components/fonts.d.ts +0 -11
- package/dist/styles/components/fonts.d.ts.map +0 -1
- package/dist/styles/components/fonts.js +0 -34
- package/dist/styles/components/fonts.js.map +0 -1
- package/dist/styles/components/header.d.ts +0 -11
- package/dist/styles/components/header.d.ts.map +0 -1
- package/dist/styles/components/header.js +0 -293
- package/dist/styles/components/header.js.map +0 -1
- package/dist/styles/components/heading.d.ts +0 -11
- package/dist/styles/components/heading.d.ts.map +0 -1
- package/dist/styles/components/heading.js +0 -115
- package/dist/styles/components/heading.js.map +0 -1
- package/dist/styles/components/layout.d.ts +0 -11
- package/dist/styles/components/layout.d.ts.map +0 -1
- package/dist/styles/components/layout.js +0 -79
- package/dist/styles/components/layout.js.map +0 -1
- package/dist/styles/components/mobilemenu.d.ts +0 -11
- package/dist/styles/components/mobilemenu.d.ts.map +0 -1
- package/dist/styles/components/mobilemenu.js +0 -112
- package/dist/styles/components/mobilemenu.js.map +0 -1
- package/dist/styles/components/reset.d.ts +0 -11
- package/dist/styles/components/reset.d.ts.map +0 -1
- package/dist/styles/components/reset.js +0 -131
- package/dist/styles/components/reset.js.map +0 -1
- package/dist/styles/components/searchmodal.d.ts +0 -11
- package/dist/styles/components/searchmodal.d.ts.map +0 -1
- package/dist/styles/components/searchmodal.js +0 -333
- package/dist/styles/components/searchmodal.js.map +0 -1
- package/dist/styles/components/sidenav.d.ts +0 -11
- package/dist/styles/components/sidenav.d.ts.map +0 -1
- package/dist/styles/components/sidenav.js +0 -212
- package/dist/styles/components/sidenav.js.map +0 -1
- package/dist/styles/components/toc.d.ts +0 -11
- package/dist/styles/components/toc.d.ts.map +0 -1
- package/dist/styles/components/toc.js +0 -120
- package/dist/styles/components/toc.js.map +0 -1
- package/dist/styles/components/typography.d.ts +0 -11
- package/dist/styles/components/typography.d.ts.map +0 -1
- package/dist/styles/components/typography.js +0 -248
- package/dist/styles/components/typography.js.map +0 -1
- package/dist/styles/components/utility.d.ts +0 -11
- package/dist/styles/components/utility.d.ts.map +0 -1
- package/dist/styles/components/utility.js +0 -231
- package/dist/styles/components/utility.js.map +0 -1
- package/dist/styles/types.d.ts +0 -79
- package/dist/styles/types.d.ts.map +0 -1
- package/dist/styles/types.js +0 -7
- package/dist/styles/types.js.map +0 -1
- package/dist/styles/utils.d.ts +0 -21
- package/dist/styles/utils.d.ts.map +0 -1
- package/dist/styles/utils.js +0 -50
- package/dist/styles/utils.js.map +0 -1
- package/dist/styles/variables/main.d.ts +0 -15
- package/dist/styles/variables/main.d.ts.map +0 -1
- package/dist/styles/variables/main.js +0 -116
- package/dist/styles/variables/main.js.map +0 -1
- package/dist/types/config.d.ts +0 -43
- package/dist/types/config.d.ts.map +0 -1
- package/dist/types/config.js +0 -7
- package/dist/types/config.js.map +0 -1
- package/dist/utils/language-utils.d.ts +0 -41
- package/dist/utils/language-utils.d.ts.map +0 -1
- package/dist/utils/language-utils.js +0 -79
- package/dist/utils/language-utils.js.map +0 -1
- package/dist/utils/slugify.d.ts +0 -20
- package/dist/utils/slugify.d.ts.map +0 -1
- package/dist/utils/slugify.js +0 -44
- package/dist/utils/slugify.js.map +0 -1
- package/templates/default/config/favicon.ico +0 -0
- package/templates/default/config/logo-dark.png +0 -0
- package/templates/default/config/logo.png +0 -0
- package/templates/default/config/sidenav.yaml +0 -36
- package/templates/default/config/site.json +0 -16
- package/templates/default/config/styles/main.json +0 -6
- package/templates/default/config/styles/typography.json +0 -6
- package/templates/default/content/en/changelog.md +0 -35
- package/templates/default/content/en/faq.md +0 -50
- package/templates/default/content/en/field-manual/engines.md +0 -59
- package/templates/default/content/en/field-manual/relativity.md +0 -46
- package/templates/default/content/en/field-manual/safety.md +0 -44
- package/templates/default/content/en/field-manual.md +0 -22
- package/templates/default/content/en/flight-school/navigation.md +0 -61
- package/templates/default/content/en/flight-school/primer.md +0 -64
- package/templates/default/content/en/flight-school.md +0 -48
- package/templates/default/content/en/index.md +0 -83
- package/templates/default/content/en/ship-systems/flux-sails.md +0 -33
- package/templates/default/content/en/ship-systems/photonic-core.md +0 -39
- package/templates/default/content/en/ship-systems.md +0 -25
- package/templates/default/package.json +0 -21
- package/templates/default/public/icons/academy.svg +0 -15
- package/templates/default/public/icons/manual.svg +0 -16
- package/templates/default/public/icons/relativity.svg +0 -16
- package/templates/default/public/icons/systems.svg +0 -14
- package/templates/default/public/icons/warning.svg +0 -14
- package/templates/default/public/images/flux-sails.svg +0 -33
- package/templates/default/public/images/photonic-core.svg +0 -44
- package/templates/default/public/images/starwake.svg +0 -49
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { Link, useLoaderData, useLocation } from 'react-router';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import Markdoc from '@markdoc/markdoc';
|
|
5
|
+
import { useMordocData } from '../data-context.js';
|
|
6
|
+
import { Toc } from '../toc/Toc.js';
|
|
7
|
+
import { Footer } from '../footer/Footer.js';
|
|
8
|
+
import { detectCurrentLang, buildLangPrefix, stripLangPrefix, resolveLabel } from '../lang-utils.js';
|
|
9
|
+
import type { PageData } from '../../types/content.js';
|
|
10
|
+
import type { SidenavConfig } from '../../types/navigation.js';
|
|
11
|
+
import { CodeBlock } from './code-block/CodeBlock.js';
|
|
12
|
+
import { Image } from './image/Image.js';
|
|
13
|
+
import { Callout } from './callout/Callout.js';
|
|
14
|
+
import { Card } from './card/Card.js';
|
|
15
|
+
import { CardGrid } from './card/CardGrid.js';
|
|
16
|
+
import { ContentLink } from './link/ContentLink.js';
|
|
17
|
+
import { Heading } from './heading/Heading.js';
|
|
18
|
+
import { Button } from '../landing/button/Button.js';
|
|
19
|
+
import styles from './Content.module.css';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Renders a single content page.
|
|
23
|
+
*
|
|
24
|
+
* The route's `loader` has already resolved the lazy
|
|
25
|
+
* `virtual:mordoc/page/<routePath>` module, so `useLoaderData()` returns
|
|
26
|
+
* the full `PageData` synchronously at render time.
|
|
27
|
+
*
|
|
28
|
+
* CJS interop: `@markdoc/markdoc` is CommonJS; the default-import shape
|
|
29
|
+
* is required. Destructured named imports like `{ renderers }` fail at
|
|
30
|
+
* runtime under Node/Vite's ESM loader even though the `.d.ts` permits
|
|
31
|
+
* them. Same rule as `markdoc-config.ts` on the Node side.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
interface BreadcrumbEntry {
|
|
35
|
+
label: string;
|
|
36
|
+
path?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function findBreadcrumb(
|
|
40
|
+
items: SidenavConfig,
|
|
41
|
+
targetPath: string,
|
|
42
|
+
ancestors: BreadcrumbEntry[],
|
|
43
|
+
): BreadcrumbEntry[] | null {
|
|
44
|
+
for (const item of items) {
|
|
45
|
+
const current: BreadcrumbEntry = { label: item.label, path: item.path };
|
|
46
|
+
if (item.path === targetPath) {
|
|
47
|
+
return [...ancestors, current];
|
|
48
|
+
}
|
|
49
|
+
if (item.children) {
|
|
50
|
+
const found = findBreadcrumb(item.children, targetPath, [...ancestors, current]);
|
|
51
|
+
if (found) return found;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function resolveActiveSidenavRaw(
|
|
58
|
+
navigation: ReturnType<typeof useMordocData>['navigation'],
|
|
59
|
+
contentPath: string,
|
|
60
|
+
): { sectionLabel: string | null; sectionPath: string | null; sidenav: SidenavConfig } {
|
|
61
|
+
if (navigation.kind === 'sidenav') {
|
|
62
|
+
return { sectionLabel: null, sectionPath: null, sidenav: navigation.sidenav };
|
|
63
|
+
}
|
|
64
|
+
const match = navigation.topnav
|
|
65
|
+
.filter((item) => contentPath === item.path || contentPath.startsWith(item.path + '/'))
|
|
66
|
+
.sort((a, b) => b.path.length - a.path.length)[0];
|
|
67
|
+
return {
|
|
68
|
+
sectionLabel: match?.label ?? null,
|
|
69
|
+
sectionPath: match?.path ?? null,
|
|
70
|
+
sidenav: match?.sidenav ?? [],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function applyLangToSidenav(
|
|
75
|
+
items: SidenavConfig,
|
|
76
|
+
prefix: string,
|
|
77
|
+
lang: string,
|
|
78
|
+
defaultLanguage: string,
|
|
79
|
+
translations: Record<string, Record<string, string>>,
|
|
80
|
+
): SidenavConfig {
|
|
81
|
+
return items.map((item) => ({
|
|
82
|
+
...item,
|
|
83
|
+
label: resolveLabel(item.label, lang, defaultLanguage, translations),
|
|
84
|
+
path: item.path !== undefined ? `${prefix}${item.path}` : undefined,
|
|
85
|
+
children: item.children
|
|
86
|
+
? applyLangToSidenav(item.children, prefix, lang, defaultLanguage, translations)
|
|
87
|
+
: undefined,
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function estimateReadTime(renderable: unknown): number {
|
|
92
|
+
const text = JSON.stringify(renderable);
|
|
93
|
+
const wordCount = text.split(/\s+/).length;
|
|
94
|
+
return Math.max(1, Math.round(wordCount / 200));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function BreadcrumbSep() {
|
|
98
|
+
return (
|
|
99
|
+
<span className={styles.breadcrumbSep} aria-hidden="true">
|
|
100
|
+
<svg
|
|
101
|
+
width="16"
|
|
102
|
+
height="16"
|
|
103
|
+
viewBox="0 0 24 24"
|
|
104
|
+
fill="none"
|
|
105
|
+
stroke="currentColor"
|
|
106
|
+
strokeWidth="2"
|
|
107
|
+
strokeLinecap="round"
|
|
108
|
+
strokeLinejoin="round"
|
|
109
|
+
>
|
|
110
|
+
<path d="m9 18 6-6-6-6" />
|
|
111
|
+
</svg>
|
|
112
|
+
</span>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function Breadcrumb({ entries }: { entries: BreadcrumbEntry[] }) {
|
|
117
|
+
if (entries.length === 0) return null;
|
|
118
|
+
const lastIndex = entries.length - 1;
|
|
119
|
+
return (
|
|
120
|
+
<nav className={styles.breadcrumb} aria-label="Breadcrumb">
|
|
121
|
+
{entries.map((entry, i) => {
|
|
122
|
+
const isCurrent = i === lastIndex;
|
|
123
|
+
const isLink = !isCurrent && entry.path !== undefined;
|
|
124
|
+
return (
|
|
125
|
+
<span key={i} className={styles.breadcrumbItem}>
|
|
126
|
+
{i > 0 && <BreadcrumbSep />}
|
|
127
|
+
{isCurrent ? (
|
|
128
|
+
<span className={styles.breadcrumbCurrent} aria-current="page">
|
|
129
|
+
{entry.label}
|
|
130
|
+
</span>
|
|
131
|
+
) : isLink ? (
|
|
132
|
+
<Link to={entry.path!} className={styles.breadcrumbLink}>
|
|
133
|
+
{entry.label}
|
|
134
|
+
</Link>
|
|
135
|
+
) : (
|
|
136
|
+
<span className={styles.breadcrumbMuted}>{entry.label}</span>
|
|
137
|
+
)}
|
|
138
|
+
</span>
|
|
139
|
+
);
|
|
140
|
+
})}
|
|
141
|
+
</nav>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function Content() {
|
|
146
|
+
const pageData = useLoaderData() as PageData;
|
|
147
|
+
const { site, navigation, language, translations } = useMordocData();
|
|
148
|
+
const { pathname } = useLocation();
|
|
149
|
+
|
|
150
|
+
const currentLang = detectCurrentLang(pathname, language, site.defaultLanguage);
|
|
151
|
+
const contentPath = stripLangPrefix(pathname, currentLang, site.defaultLanguage);
|
|
152
|
+
const prefix = buildLangPrefix(currentLang, site.defaultLanguage);
|
|
153
|
+
|
|
154
|
+
const { sectionLabel, sectionPath, sidenav } = resolveActiveSidenavRaw(navigation, contentPath);
|
|
155
|
+
const processedSidenav = applyLangToSidenav(sidenav, prefix, currentLang, site.defaultLanguage, translations);
|
|
156
|
+
|
|
157
|
+
const rawBreadcrumb = findBreadcrumb(processedSidenav, pathname, []) ?? [];
|
|
158
|
+
const resolvedSectionLabel = sectionLabel
|
|
159
|
+
? resolveLabel(sectionLabel, currentLang, site.defaultLanguage, translations)
|
|
160
|
+
: null;
|
|
161
|
+
const breadcrumb: BreadcrumbEntry[] = [
|
|
162
|
+
{ label: 'Home', path: prefix || '/' },
|
|
163
|
+
...(resolvedSectionLabel && sectionPath
|
|
164
|
+
? [{ label: resolvedSectionLabel, path: `${prefix}${sectionPath}` }]
|
|
165
|
+
: []),
|
|
166
|
+
...rawBreadcrumb,
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
const readTime = estimateReadTime(pageData.renderable);
|
|
170
|
+
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
const pageTitle = pageData.frontmatter.title;
|
|
173
|
+
document.title = pageTitle ? `${pageTitle} — ${site.name}` : site.name;
|
|
174
|
+
}, [pageData.frontmatter.title, site.name]);
|
|
175
|
+
|
|
176
|
+
const rendered = Markdoc.renderers.react(pageData.renderable, React, {
|
|
177
|
+
components: { CodeBlock, Image, Callout, Card, CardGrid, ContentLink, Heading, Button },
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<div className={styles.contentAreaGrid}>
|
|
182
|
+
{/* Article area */}
|
|
183
|
+
<div className={styles.articleArea}>
|
|
184
|
+
<article className={styles.article} data-pagefind-body>
|
|
185
|
+
<div className={styles.articleMeta} data-pagefind-ignore>
|
|
186
|
+
<Breadcrumb entries={breadcrumb} />
|
|
187
|
+
</div>
|
|
188
|
+
<header className={styles.articleHeader}>
|
|
189
|
+
<h1 className={styles.title} data-pagefind-meta="title">{pageData.frontmatter.title}</h1>
|
|
190
|
+
{typeof pageData.frontmatter.description === 'string' && (
|
|
191
|
+
<p className={styles.description}>{pageData.frontmatter.description}</p>
|
|
192
|
+
)}
|
|
193
|
+
<div className={styles.metaRow} data-pagefind-ignore>
|
|
194
|
+
<span className={styles.readTime}>{readTime} MIN READ</span>
|
|
195
|
+
</div>
|
|
196
|
+
</header>
|
|
197
|
+
<div className={styles.prose}>{rendered}</div>
|
|
198
|
+
</article>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
{/* TOC area */}
|
|
202
|
+
<aside className={styles.tocArea}>
|
|
203
|
+
<Toc items={pageData.toc} />
|
|
204
|
+
</aside>
|
|
205
|
+
|
|
206
|
+
{/* Footer area — spans only under article column, not TOC */}
|
|
207
|
+
<div className={styles.footerArea}>
|
|
208
|
+
<Footer />
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
);
|
|
212
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
CALLOUT TOKENS
|
|
3
|
+
============================================================ */
|
|
4
|
+
:root {
|
|
5
|
+
--callout-note-accent: #47a3d1;
|
|
6
|
+
--callout-warning-accent: #e6911a;
|
|
7
|
+
--callout-danger-accent: #d22d2d;
|
|
8
|
+
--callout-tip-accent: #22a569;
|
|
9
|
+
|
|
10
|
+
/* Subtle per-type background tints (light mode) */
|
|
11
|
+
--callout-note-bg: color-mix(in oklch, #47a3d1 8%, white);
|
|
12
|
+
--callout-warning-bg: color-mix(in oklch, #e6911a 8%, white);
|
|
13
|
+
--callout-danger-bg: color-mix(in oklch, #d22d2d 8%, white);
|
|
14
|
+
--callout-tip-bg: color-mix(in oklch, #22a569 8%, white);
|
|
15
|
+
|
|
16
|
+
--callout-title-color: #0f172a;
|
|
17
|
+
--callout-body-color: #475569;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
:global(.dark) {
|
|
21
|
+
/* Subtle per-type background tints (dark mode) */
|
|
22
|
+
--callout-note-bg: color-mix(in oklch, #47a3d1 10%, #1a1a1a);
|
|
23
|
+
--callout-warning-bg: color-mix(in oklch, #e6911a 10%, #1a1a1a);
|
|
24
|
+
--callout-danger-bg: color-mix(in oklch, #d22d2d 10%, #1a1a1a);
|
|
25
|
+
--callout-tip-bg: color-mix(in oklch, #22a569 10%, #1a1a1a);
|
|
26
|
+
|
|
27
|
+
--callout-title-color: #f1f5f9;
|
|
28
|
+
--callout-body-color: #94a3b8;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* ── Base callout ────────────────────────────────────────────── */
|
|
32
|
+
|
|
33
|
+
.callout {
|
|
34
|
+
display: flex;
|
|
35
|
+
gap: 0.75rem;
|
|
36
|
+
margin: 1.25rem 0;
|
|
37
|
+
padding: 1rem 1.25rem;
|
|
38
|
+
border-radius: var(--radius-md);
|
|
39
|
+
border-left: 4px solid transparent;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* ── Type variants ───────────────────────────────────────────── */
|
|
43
|
+
|
|
44
|
+
.note {
|
|
45
|
+
background-color: var(--callout-note-bg);
|
|
46
|
+
border-left-color: var(--callout-note-accent);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.warning {
|
|
50
|
+
background-color: var(--callout-warning-bg);
|
|
51
|
+
border-left-color: var(--callout-warning-accent);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.danger {
|
|
55
|
+
background-color: var(--callout-danger-bg);
|
|
56
|
+
border-left-color: var(--callout-danger-accent);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.tip {
|
|
60
|
+
background-color: var(--callout-tip-bg);
|
|
61
|
+
border-left-color: var(--callout-tip-accent);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* ── Icon ────────────────────────────────────────────────────── */
|
|
65
|
+
|
|
66
|
+
.iconWrap {
|
|
67
|
+
flex-shrink: 0;
|
|
68
|
+
margin-top: 1px;
|
|
69
|
+
display: flex;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Icon color matches the type accent so the variant is immediately obvious */
|
|
73
|
+
.note .iconWrap { color: var(--callout-note-accent); }
|
|
74
|
+
.warning .iconWrap { color: var(--callout-warning-accent); }
|
|
75
|
+
.danger .iconWrap { color: var(--callout-danger-accent); }
|
|
76
|
+
.tip .iconWrap { color: var(--callout-tip-accent); }
|
|
77
|
+
|
|
78
|
+
/* ── Body ────────────────────────────────────────────────────── */
|
|
79
|
+
|
|
80
|
+
.body {
|
|
81
|
+
flex: 1;
|
|
82
|
+
min-width: 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.title {
|
|
86
|
+
font-weight: 600;
|
|
87
|
+
font-size: 0.9375rem;
|
|
88
|
+
line-height: 1.4;
|
|
89
|
+
margin-bottom: 0.25rem;
|
|
90
|
+
color: var(--callout-title-color);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.content {
|
|
94
|
+
font-size: 0.9375rem;
|
|
95
|
+
line-height: 1.625;
|
|
96
|
+
color: var(--callout-body-color);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* Markdoc wraps callout body text in <p> tags — remove bottom margin on the
|
|
100
|
+
last paragraph so the callout padding provides the visual spacing instead */
|
|
101
|
+
.content :global(p) {
|
|
102
|
+
margin-bottom: 0;
|
|
103
|
+
color: inherit;
|
|
104
|
+
font-size: inherit;
|
|
105
|
+
line-height: inherit;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.content :global(p + p) {
|
|
109
|
+
margin-top: 0.5rem;
|
|
110
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callout — block-level callout box for note, warning, danger, and tip messages.
|
|
3
|
+
*
|
|
4
|
+
* Registered as a Markdoc tag (not a node), so authors use the
|
|
5
|
+
* {% callout type="note" title="..." %}...{% /callout %} syntax.
|
|
6
|
+
* Children are rendered Markdoc content (React nodes) passed through
|
|
7
|
+
* Markdoc.renderers.react — no special handling needed.
|
|
8
|
+
*
|
|
9
|
+
* Wired via config.tags in markdoc-config.ts, registered in
|
|
10
|
+
* Content.tsx's components map.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import React from 'react';
|
|
14
|
+
import styles from './Callout.module.css';
|
|
15
|
+
|
|
16
|
+
interface CalloutProps {
|
|
17
|
+
type?: 'note' | 'warning' | 'danger' | 'tip';
|
|
18
|
+
title?: string;
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function NoteIcon() {
|
|
23
|
+
return (
|
|
24
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
25
|
+
<circle cx="12" cy="12" r="10" />
|
|
26
|
+
<path d="M12 16v-4" />
|
|
27
|
+
<path d="M12 8h.01" />
|
|
28
|
+
</svg>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function WarningIcon() {
|
|
33
|
+
return (
|
|
34
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
35
|
+
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" />
|
|
36
|
+
<path d="M12 9v4" />
|
|
37
|
+
<path d="M12 17h.01" />
|
|
38
|
+
</svg>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function DangerIcon() {
|
|
43
|
+
return (
|
|
44
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
45
|
+
<circle cx="12" cy="12" r="10" />
|
|
46
|
+
<path d="m15 9-6 6" />
|
|
47
|
+
<path d="m9 9 6 6" />
|
|
48
|
+
</svg>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function TipIcon() {
|
|
53
|
+
return (
|
|
54
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
55
|
+
<path d="M12 2a7 7 0 0 1 7 7c0 2.5-1.3 4.7-3.3 6l-.7 3H9l-.7-3A7 7 0 0 1 5 9a7 7 0 0 1 7-7Z" />
|
|
56
|
+
<path d="M9 21h6" />
|
|
57
|
+
</svg>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const icons = {
|
|
62
|
+
note: <NoteIcon />,
|
|
63
|
+
warning: <WarningIcon />,
|
|
64
|
+
danger: <DangerIcon />,
|
|
65
|
+
tip: <TipIcon />,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export function Callout({ type = 'note', title, children }: CalloutProps) {
|
|
69
|
+
return (
|
|
70
|
+
<div className={`${styles.callout} ${styles[type]}`} data-type={type}>
|
|
71
|
+
<div className={styles.iconWrap}>
|
|
72
|
+
{icons[type]}
|
|
73
|
+
</div>
|
|
74
|
+
<div className={styles.body}>
|
|
75
|
+
{title && <div className={styles.title}>{title}</div>}
|
|
76
|
+
<div className={styles.content}>{children}</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export default Callout;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
CARD TOKENS
|
|
3
|
+
============================================================ */
|
|
4
|
+
:root {
|
|
5
|
+
--card-bg: var(--color-bg);
|
|
6
|
+
--card-border: var(--color-border);
|
|
7
|
+
--card-border-hover: var(--accent);
|
|
8
|
+
--card-shadow: var(--shadow-sm);
|
|
9
|
+
--card-shadow-hover: var(--shadow-md);
|
|
10
|
+
--card-title-color: var(--color-fg);
|
|
11
|
+
--card-desc-color: var(--color-fg-muted);
|
|
12
|
+
--card-tag-bg: var(--accent-subtle);
|
|
13
|
+
--card-tag-color: var(--accent-emphasis);
|
|
14
|
+
--card-arrow-color: var(--color-fg-muted);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* ── Base card ───────────────────────────────────────────────── */
|
|
18
|
+
|
|
19
|
+
.card {
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-direction: column;
|
|
22
|
+
background: var(--card-bg);
|
|
23
|
+
border: 1px solid var(--card-border);
|
|
24
|
+
border-radius: var(--radius-lg);
|
|
25
|
+
box-shadow: var(--card-shadow);
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
text-decoration: none;
|
|
28
|
+
color: inherit;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.linked {
|
|
32
|
+
cursor: pointer;
|
|
33
|
+
transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.linked:hover {
|
|
37
|
+
border-color: var(--card-border-hover);
|
|
38
|
+
box-shadow: var(--card-shadow-hover);
|
|
39
|
+
transform: translateY(-1px);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.linked:hover .arrow {
|
|
43
|
+
color: var(--accent-emphasis);
|
|
44
|
+
transform: translateX(3px);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* ── Image variant ───────────────────────────────────────────── */
|
|
48
|
+
|
|
49
|
+
.imageWrap {
|
|
50
|
+
width: 100%;
|
|
51
|
+
aspect-ratio: 16 / 9;
|
|
52
|
+
overflow: hidden;
|
|
53
|
+
background: var(--color-surface);
|
|
54
|
+
flex-shrink: 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.cardImage {
|
|
58
|
+
width: 100%;
|
|
59
|
+
height: 100%;
|
|
60
|
+
object-fit: cover;
|
|
61
|
+
display: block;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* ── Body ─────────────────────────────────────────────────────── */
|
|
65
|
+
|
|
66
|
+
.body {
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-direction: column;
|
|
69
|
+
padding: 1.25rem;
|
|
70
|
+
flex: 1;
|
|
71
|
+
gap: 0.5rem;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Image cards get slightly less top padding since the image already provides visual separation */
|
|
75
|
+
.imageCard .body {
|
|
76
|
+
padding-top: 1rem;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* ── Tag badge ───────────────────────────────────────────────── */
|
|
80
|
+
|
|
81
|
+
.tag {
|
|
82
|
+
display: inline-flex;
|
|
83
|
+
align-self: flex-start;
|
|
84
|
+
padding: 0.2em 0.6em;
|
|
85
|
+
border-radius: var(--radius-sm);
|
|
86
|
+
font-size: 0.6875rem;
|
|
87
|
+
font-weight: 600;
|
|
88
|
+
letter-spacing: 0.04em;
|
|
89
|
+
text-transform: uppercase;
|
|
90
|
+
background: var(--card-tag-bg);
|
|
91
|
+
color: var(--card-tag-color);
|
|
92
|
+
line-height: 1.5;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* ── Icon ─────────────────────────────────────────────────────── */
|
|
96
|
+
|
|
97
|
+
.icon {
|
|
98
|
+
width: 1.75rem;
|
|
99
|
+
height: 1.75rem;
|
|
100
|
+
object-fit: contain;
|
|
101
|
+
display: block;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* ── Title row ───────────────────────────────────────────────── */
|
|
105
|
+
|
|
106
|
+
.titleRow {
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: center;
|
|
109
|
+
justify-content: space-between;
|
|
110
|
+
gap: 0.5rem;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.title {
|
|
114
|
+
font-size: 0.9375rem;
|
|
115
|
+
font-weight: 600;
|
|
116
|
+
line-height: 1.4;
|
|
117
|
+
color: var(--card-title-color);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* ── Arrow ───────────────────────────────────────────────────── */
|
|
121
|
+
|
|
122
|
+
.arrow {
|
|
123
|
+
flex-shrink: 0;
|
|
124
|
+
color: var(--card-arrow-color);
|
|
125
|
+
transition: color 0.15s ease, transform 0.15s ease;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* ── Description ─────────────────────────────────────────────── */
|
|
129
|
+
|
|
130
|
+
.description {
|
|
131
|
+
font-size: 0.875rem;
|
|
132
|
+
line-height: 1.6;
|
|
133
|
+
color: var(--card-desc-color);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* Markdoc wraps card body text in <p> tags — reset them so card padding governs spacing */
|
|
137
|
+
.description :global(p) {
|
|
138
|
+
margin: 0;
|
|
139
|
+
color: inherit;
|
|
140
|
+
font-size: inherit;
|
|
141
|
+
line-height: inherit;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.description :global(p + p) {
|
|
145
|
+
margin-top: 0.375rem;
|
|
146
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Card — flexible content card supporting icon, image, and plain variants.
|
|
3
|
+
*
|
|
4
|
+
* Variant is inferred from props: `image` → image card, `icon` → icon card,
|
|
5
|
+
* neither → plain. Compact mode activates automatically when no body text is
|
|
6
|
+
* provided (self-closing tag). When `path` is set the entire card is a link;
|
|
7
|
+
* internal paths (starting with "/") use React Router Link for SPA navigation,
|
|
8
|
+
* external URLs open in a new tab.
|
|
9
|
+
*
|
|
10
|
+
* Registered as a Markdoc tag (`card`) in markdoc-config.ts and added to
|
|
11
|
+
* Content.tsx's components map. Intended to be used inside {% cardGrid %}.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { Link } from 'react-router';
|
|
15
|
+
import React from 'react';
|
|
16
|
+
import styles from './Card.module.css';
|
|
17
|
+
|
|
18
|
+
interface CardProps {
|
|
19
|
+
title: string;
|
|
20
|
+
path?: string;
|
|
21
|
+
icon?: string;
|
|
22
|
+
image?: string;
|
|
23
|
+
tag?: string;
|
|
24
|
+
children?: React.ReactNode;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function ArrowIcon() {
|
|
28
|
+
return (
|
|
29
|
+
<svg
|
|
30
|
+
className={styles.arrow}
|
|
31
|
+
width="15"
|
|
32
|
+
height="15"
|
|
33
|
+
viewBox="0 0 24 24"
|
|
34
|
+
fill="none"
|
|
35
|
+
stroke="currentColor"
|
|
36
|
+
strokeWidth="2.5"
|
|
37
|
+
strokeLinecap="round"
|
|
38
|
+
strokeLinejoin="round"
|
|
39
|
+
aria-hidden="true"
|
|
40
|
+
>
|
|
41
|
+
<path d="M5 12h14" />
|
|
42
|
+
<path d="m12 5 7 7-7 7" />
|
|
43
|
+
</svg>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function CardInner({ title, path, icon, image, tag, children }: CardProps) {
|
|
48
|
+
const hasImage = Boolean(image);
|
|
49
|
+
const hasIcon = Boolean(icon) && !hasImage;
|
|
50
|
+
const hasBody = Boolean(children);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<>
|
|
54
|
+
{hasImage && (
|
|
55
|
+
<div className={styles.imageWrap}>
|
|
56
|
+
<img src={image} alt="" className={styles.cardImage} />
|
|
57
|
+
</div>
|
|
58
|
+
)}
|
|
59
|
+
<div className={styles.body}>
|
|
60
|
+
{tag && <span className={styles.tag}>{tag}</span>}
|
|
61
|
+
{hasIcon && (
|
|
62
|
+
<img src={icon} alt="" className={styles.icon} />
|
|
63
|
+
)}
|
|
64
|
+
<div className={styles.titleRow}>
|
|
65
|
+
<span className={styles.title}>{title}</span>
|
|
66
|
+
{path && <ArrowIcon />}
|
|
67
|
+
</div>
|
|
68
|
+
{hasBody && <div className={styles.description}>{children}</div>}
|
|
69
|
+
</div>
|
|
70
|
+
</>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function isExternal(path: string) {
|
|
75
|
+
return path.startsWith('http://') || path.startsWith('https://') || path.startsWith('//');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function Card(props: CardProps) {
|
|
79
|
+
const { path } = props;
|
|
80
|
+
const hasImage = Boolean(props.image);
|
|
81
|
+
|
|
82
|
+
const className = [
|
|
83
|
+
styles.card,
|
|
84
|
+
hasImage ? styles.imageCard : '',
|
|
85
|
+
path ? styles.linked : '',
|
|
86
|
+
].filter(Boolean).join(' ');
|
|
87
|
+
|
|
88
|
+
if (path) {
|
|
89
|
+
if (isExternal(path)) {
|
|
90
|
+
return (
|
|
91
|
+
<a href={path} className={className} target="_blank" rel="noopener noreferrer">
|
|
92
|
+
<CardInner {...props} />
|
|
93
|
+
</a>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
return (
|
|
97
|
+
<Link to={path} className={className}>
|
|
98
|
+
<CardInner {...props} />
|
|
99
|
+
</Link>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<div className={className}>
|
|
105
|
+
<CardInner {...props} />
|
|
106
|
+
</div>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export default Card;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* ── CardGrid ─────────────────────────────────────────────────── */
|
|
2
|
+
|
|
3
|
+
.cardGrid {
|
|
4
|
+
display: grid;
|
|
5
|
+
grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr));
|
|
6
|
+
gap: 1rem;
|
|
7
|
+
margin: 1.5rem 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* Collapse to 2 columns on medium viewports */
|
|
11
|
+
@media (max-width: 960px) {
|
|
12
|
+
.cardGrid {
|
|
13
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* Collapse to single column on small viewports */
|
|
18
|
+
@media (max-width: 600px) {
|
|
19
|
+
.cardGrid {
|
|
20
|
+
grid-template-columns: 1fr;
|
|
21
|
+
}
|
|
22
|
+
}
|