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.
Files changed (431) hide show
  1. package/LICENSE +7 -0
  2. package/bin/cli.js +24 -71
  3. package/dist/cli/asset-rewrite.d.ts +40 -0
  4. package/dist/cli/asset-rewrite.d.ts.map +1 -0
  5. package/dist/cli/asset-rewrite.js +67 -0
  6. package/dist/cli/asset-rewrite.js.map +1 -0
  7. package/dist/cli/build.d.ts +45 -15
  8. package/dist/cli/build.d.ts.map +1 -1
  9. package/dist/cli/build.js +186 -101
  10. package/dist/cli/build.js.map +1 -1
  11. package/dist/cli/dev.d.ts +26 -14
  12. package/dist/cli/dev.d.ts.map +1 -1
  13. package/dist/cli/dev.js +116 -159
  14. package/dist/cli/dev.js.map +1 -1
  15. package/dist/cli/pagefind-indexer.d.ts +15 -0
  16. package/dist/cli/pagefind-indexer.d.ts.map +1 -0
  17. package/dist/cli/pagefind-indexer.js +68 -0
  18. package/dist/cli/pagefind-indexer.js.map +1 -0
  19. package/dist/cli/paths.d.ts +26 -0
  20. package/dist/cli/paths.d.ts.map +1 -0
  21. package/dist/cli/paths.js +32 -0
  22. package/dist/cli/paths.js.map +1 -0
  23. package/dist/cli/ssg-runner.d.ts +58 -0
  24. package/dist/cli/ssg-runner.d.ts.map +1 -0
  25. package/dist/cli/ssg-runner.js +126 -0
  26. package/dist/cli/ssg-runner.js.map +1 -0
  27. package/dist/config/assets-loader.d.ts +11 -0
  28. package/dist/config/assets-loader.d.ts.map +1 -0
  29. package/dist/config/assets-loader.js +46 -0
  30. package/dist/config/assets-loader.js.map +1 -0
  31. package/dist/config/language-loader.d.ts +11 -0
  32. package/dist/config/language-loader.d.ts.map +1 -0
  33. package/dist/config/language-loader.js +61 -0
  34. package/dist/config/language-loader.js.map +1 -0
  35. package/dist/config/language.d.ts +11 -0
  36. package/dist/config/language.d.ts.map +1 -0
  37. package/dist/config/language.js +61 -0
  38. package/dist/config/language.js.map +1 -0
  39. package/dist/config/sidenav-loader.d.ts +14 -0
  40. package/dist/config/sidenav-loader.d.ts.map +1 -0
  41. package/dist/config/sidenav-loader.js +78 -0
  42. package/dist/config/sidenav-loader.js.map +1 -0
  43. package/dist/config/site-loader.d.ts +11 -0
  44. package/dist/config/site-loader.d.ts.map +1 -0
  45. package/dist/config/site-loader.js +72 -0
  46. package/dist/config/site-loader.js.map +1 -0
  47. package/dist/config/site.d.ts +11 -0
  48. package/dist/config/site.d.ts.map +1 -0
  49. package/dist/config/site.js +72 -0
  50. package/dist/config/site.js.map +1 -0
  51. package/dist/config/topnav-loader.d.ts +10 -0
  52. package/dist/config/topnav-loader.d.ts.map +1 -0
  53. package/dist/config/topnav-loader.js +78 -0
  54. package/dist/config/topnav-loader.js.map +1 -0
  55. package/dist/config/translations-loader.d.ts +7 -0
  56. package/dist/config/translations-loader.d.ts.map +1 -0
  57. package/dist/config/translations-loader.js +51 -0
  58. package/dist/config/translations-loader.js.map +1 -0
  59. package/dist/content/content-loader.d.ts +16 -0
  60. package/dist/content/content-loader.d.ts.map +1 -0
  61. package/dist/content/content-loader.js +126 -0
  62. package/dist/content/content-loader.js.map +1 -0
  63. package/dist/content/content-parser.d.ts +12 -0
  64. package/dist/content/content-parser.d.ts.map +1 -0
  65. package/dist/content/content-parser.js +54 -0
  66. package/dist/content/content-parser.js.map +1 -0
  67. package/dist/content/content-transformer.d.ts +17 -0
  68. package/dist/content/content-transformer.d.ts.map +1 -0
  69. package/dist/content/content-transformer.js +94 -0
  70. package/dist/content/content-transformer.js.map +1 -0
  71. package/dist/content/markdoc-config.d.ts +13 -0
  72. package/dist/content/markdoc-config.d.ts.map +1 -0
  73. package/dist/content/markdoc-config.js +210 -0
  74. package/dist/content/markdoc-config.js.map +1 -0
  75. package/dist/content/slug.d.ts +26 -0
  76. package/dist/content/slug.d.ts.map +1 -0
  77. package/dist/content/slug.js +31 -0
  78. package/dist/content/slug.js.map +1 -0
  79. package/dist/pipeline.d.ts +61 -0
  80. package/dist/pipeline.d.ts.map +1 -0
  81. package/dist/pipeline.js +123 -0
  82. package/dist/pipeline.js.map +1 -0
  83. package/dist/types/assets.d.ts +15 -0
  84. package/dist/types/assets.d.ts.map +1 -0
  85. package/dist/types/assets.js +2 -0
  86. package/dist/types/assets.js.map +1 -0
  87. package/dist/types/content.d.ts +114 -32
  88. package/dist/types/content.d.ts.map +1 -1
  89. package/dist/types/content.js +1 -6
  90. package/dist/types/content.js.map +1 -1
  91. package/dist/types/language.d.ts +9 -0
  92. package/dist/types/language.d.ts.map +1 -0
  93. package/dist/types/language.js +2 -0
  94. package/dist/types/language.js.map +1 -0
  95. package/dist/types/navigation.d.ts +24 -36
  96. package/dist/types/navigation.d.ts.map +1 -1
  97. package/dist/types/navigation.js +1 -6
  98. package/dist/types/navigation.js.map +1 -1
  99. package/dist/types/pipeline.d.ts +70 -0
  100. package/dist/types/pipeline.d.ts.map +1 -0
  101. package/dist/types/pipeline.js +2 -0
  102. package/dist/types/pipeline.js.map +1 -0
  103. package/dist/types/site.d.ts +20 -0
  104. package/dist/types/site.d.ts.map +1 -0
  105. package/dist/types/site.js +2 -0
  106. package/dist/types/site.js.map +1 -0
  107. package/dist/utils/lang-utils.d.ts +23 -0
  108. package/dist/utils/lang-utils.d.ts.map +1 -0
  109. package/dist/utils/lang-utils.js +40 -0
  110. package/dist/utils/lang-utils.js.map +1 -0
  111. package/dist/utils/paths.d.ts +25 -0
  112. package/dist/utils/paths.d.ts.map +1 -0
  113. package/dist/utils/paths.js +31 -0
  114. package/dist/utils/paths.js.map +1 -0
  115. package/dist/vite/plugin.d.ts +93 -0
  116. package/dist/vite/plugin.d.ts.map +1 -0
  117. package/dist/vite/plugin.js +435 -0
  118. package/dist/vite/plugin.js.map +1 -0
  119. package/package.json +46 -61
  120. package/src/app/App.module.css +82 -0
  121. package/src/app/App.tsx +95 -0
  122. package/src/app/content/Content.module.css +357 -0
  123. package/src/app/content/Content.tsx +212 -0
  124. package/src/app/content/callout/Callout.module.css +110 -0
  125. package/src/app/content/callout/Callout.tsx +82 -0
  126. package/src/app/content/card/Card.module.css +146 -0
  127. package/src/app/content/card/Card.tsx +110 -0
  128. package/src/app/content/card/CardGrid.module.css +22 -0
  129. package/src/app/content/card/CardGrid.tsx +34 -0
  130. package/src/app/content/code-block/CodeBlock.module.css +237 -0
  131. package/src/app/content/code-block/CodeBlock.tsx +123 -0
  132. package/src/app/content/heading/Heading.module.css +52 -0
  133. package/src/app/content/heading/Heading.tsx +75 -0
  134. package/src/app/content/image/Image.module.css +113 -0
  135. package/src/app/content/image/Image.tsx +98 -0
  136. package/src/app/content/link/ContentLink.tsx +46 -0
  137. package/src/app/data-context.tsx +39 -0
  138. package/src/app/entry-server.tsx +58 -0
  139. package/src/app/footer/Footer.module.css +36 -0
  140. package/src/app/footer/Footer.tsx +28 -0
  141. package/src/app/globals.d.ts +40 -0
  142. package/src/app/header/Header.module.css +205 -0
  143. package/src/app/header/Header.tsx +188 -0
  144. package/src/app/header/LanguagePicker.module.css +97 -0
  145. package/src/app/header/LanguagePicker.tsx +174 -0
  146. package/src/app/header/SearchBar.module.css +92 -0
  147. package/src/app/header/SearchBar.tsx +37 -0
  148. package/src/app/header/SearchModal.module.css +185 -0
  149. package/src/app/header/SearchModal.tsx +245 -0
  150. package/src/app/header/Topnav.module.css +37 -0
  151. package/src/app/header/Topnav.tsx +30 -0
  152. package/src/app/index.css +119 -0
  153. package/src/app/index.html +13 -0
  154. package/src/app/landing/LandingPage.module.css +14 -0
  155. package/src/app/landing/LandingPage.tsx +62 -0
  156. package/src/app/landing/button/Button.module.css +37 -0
  157. package/src/app/landing/button/Button.tsx +50 -0
  158. package/src/app/landing/hero/Hero.module.css +108 -0
  159. package/src/app/landing/hero/Hero.tsx +81 -0
  160. package/src/app/landing/section/Section.module.css +100 -0
  161. package/src/app/landing/section/Section.tsx +61 -0
  162. package/src/app/lang-utils.ts +11 -0
  163. package/src/app/main.tsx +67 -0
  164. package/src/app/not-found/NotFound.module.css +92 -0
  165. package/src/app/not-found/NotFound.tsx +34 -0
  166. package/src/app/routes.tsx +86 -0
  167. package/src/app/sidenav/Sidenav.module.css +239 -0
  168. package/src/app/sidenav/Sidenav.tsx +182 -0
  169. package/src/app/skeleton/Skeleton.module.css +100 -0
  170. package/src/app/skeleton/Skeleton.tsx +75 -0
  171. package/src/app/toc/Toc.module.css +59 -0
  172. package/src/app/toc/Toc.tsx +77 -0
  173. package/src/app/tsconfig.json +18 -0
  174. package/src/app/virtual-modules.d.ts +74 -0
  175. package/dist/assets/fonts/inter/Inter-Italic-VariableFont_opsz,wght.ttf +0 -0
  176. package/dist/assets/fonts/inter/Inter-VariableFont_opsz,wght.ttf +0 -0
  177. package/dist/assets/fonts/inter/OFL.txt +0 -93
  178. package/dist/build/Builder.d.ts +0 -92
  179. package/dist/build/Builder.d.ts.map +0 -1
  180. package/dist/build/Builder.js +0 -394
  181. package/dist/build/Builder.js.map +0 -1
  182. package/dist/build/ClientBundler.d.ts +0 -48
  183. package/dist/build/ClientBundler.d.ts.map +0 -1
  184. package/dist/build/ClientBundler.js +0 -169
  185. package/dist/build/ClientBundler.js.map +0 -1
  186. package/dist/build/HtmlGenerator.d.ts +0 -46
  187. package/dist/build/HtmlGenerator.d.ts.map +0 -1
  188. package/dist/build/HtmlGenerator.js +0 -190
  189. package/dist/build/HtmlGenerator.js.map +0 -1
  190. package/dist/build/SearchIndexer.d.ts +0 -31
  191. package/dist/build/SearchIndexer.d.ts.map +0 -1
  192. package/dist/build/SearchIndexer.js +0 -116
  193. package/dist/build/SearchIndexer.js.map +0 -1
  194. package/dist/bundles/client.js +0 -126
  195. package/dist/cli/create-app.d.ts +0 -14
  196. package/dist/cli/create-app.d.ts.map +0 -1
  197. package/dist/cli/create-app.js +0 -241
  198. package/dist/cli/create-app.js.map +0 -1
  199. package/dist/client/App.d.ts +0 -17
  200. package/dist/client/App.d.ts.map +0 -1
  201. package/dist/client/App.js +0 -91
  202. package/dist/client/App.js.map +0 -1
  203. package/dist/client/contexts/ConfigContext.d.ts +0 -22
  204. package/dist/client/contexts/ConfigContext.d.ts.map +0 -1
  205. package/dist/client/contexts/ConfigContext.js +0 -27
  206. package/dist/client/contexts/ConfigContext.js.map +0 -1
  207. package/dist/client/contexts/ContentContext.d.ts +0 -31
  208. package/dist/client/contexts/ContentContext.d.ts.map +0 -1
  209. package/dist/client/contexts/ContentContext.js +0 -46
  210. package/dist/client/contexts/ContentContext.js.map +0 -1
  211. package/dist/client/contexts/SearchContext.d.ts +0 -38
  212. package/dist/client/contexts/SearchContext.d.ts.map +0 -1
  213. package/dist/client/contexts/SearchContext.js +0 -185
  214. package/dist/client/contexts/SearchContext.js.map +0 -1
  215. package/dist/client/contexts/ThemeContext.d.ts +0 -23
  216. package/dist/client/contexts/ThemeContext.d.ts.map +0 -1
  217. package/dist/client/contexts/ThemeContext.js +0 -53
  218. package/dist/client/contexts/ThemeContext.js.map +0 -1
  219. package/dist/client/hooks/useContent.d.ts +0 -12
  220. package/dist/client/hooks/useContent.d.ts.map +0 -1
  221. package/dist/client/hooks/useContent.js +0 -74
  222. package/dist/client/hooks/useContent.js.map +0 -1
  223. package/dist/client/hooks/useNavigation.d.ts +0 -15
  224. package/dist/client/hooks/useNavigation.d.ts.map +0 -1
  225. package/dist/client/hooks/useNavigation.js +0 -101
  226. package/dist/client/hooks/useNavigation.js.map +0 -1
  227. package/dist/client/hooks/useSearch.d.ts +0 -22
  228. package/dist/client/hooks/useSearch.d.ts.map +0 -1
  229. package/dist/client/hooks/useSearch.js +0 -64
  230. package/dist/client/hooks/useSearch.js.map +0 -1
  231. package/dist/client/main.d.ts +0 -5
  232. package/dist/client/main.d.ts.map +0 -1
  233. package/dist/client/main.js +0 -66
  234. package/dist/client/main.js.map +0 -1
  235. package/dist/components/Callout.d.ts +0 -9
  236. package/dist/components/Callout.d.ts.map +0 -1
  237. package/dist/components/Callout.js +0 -24
  238. package/dist/components/Callout.js.map +0 -1
  239. package/dist/components/Card.d.ts +0 -10
  240. package/dist/components/Card.d.ts.map +0 -1
  241. package/dist/components/Card.js +0 -15
  242. package/dist/components/Card.js.map +0 -1
  243. package/dist/components/CardGrid.d.ts +0 -8
  244. package/dist/components/CardGrid.d.ts.map +0 -1
  245. package/dist/components/CardGrid.js +0 -9
  246. package/dist/components/CardGrid.js.map +0 -1
  247. package/dist/components/CodeBlock.d.ts +0 -28
  248. package/dist/components/CodeBlock.d.ts.map +0 -1
  249. package/dist/components/CodeBlock.js +0 -80
  250. package/dist/components/CodeBlock.js.map +0 -1
  251. package/dist/components/ContentPage.d.ts +0 -9
  252. package/dist/components/ContentPage.d.ts.map +0 -1
  253. package/dist/components/ContentPage.js +0 -102
  254. package/dist/components/ContentPage.js.map +0 -1
  255. package/dist/components/Header.d.ts +0 -14
  256. package/dist/components/Header.d.ts.map +0 -1
  257. package/dist/components/Header.js +0 -30
  258. package/dist/components/Header.js.map +0 -1
  259. package/dist/components/Heading.d.ts +0 -16
  260. package/dist/components/Heading.d.ts.map +0 -1
  261. package/dist/components/Heading.js +0 -31
  262. package/dist/components/Heading.js.map +0 -1
  263. package/dist/components/Image.d.ts +0 -8
  264. package/dist/components/Image.d.ts.map +0 -1
  265. package/dist/components/Image.js +0 -24
  266. package/dist/components/Image.js.map +0 -1
  267. package/dist/components/Layout.d.ts +0 -14
  268. package/dist/components/Layout.d.ts.map +0 -1
  269. package/dist/components/Layout.js +0 -32
  270. package/dist/components/Layout.js.map +0 -1
  271. package/dist/components/MarkdocRenderer.d.ts +0 -15
  272. package/dist/components/MarkdocRenderer.d.ts.map +0 -1
  273. package/dist/components/MarkdocRenderer.js +0 -73
  274. package/dist/components/MarkdocRenderer.js.map +0 -1
  275. package/dist/components/MobileMenu.d.ts +0 -14
  276. package/dist/components/MobileMenu.d.ts.map +0 -1
  277. package/dist/components/MobileMenu.js +0 -45
  278. package/dist/components/MobileMenu.js.map +0 -1
  279. package/dist/components/PageNavigation.d.ts +0 -9
  280. package/dist/components/PageNavigation.d.ts.map +0 -1
  281. package/dist/components/PageNavigation.js +0 -23
  282. package/dist/components/PageNavigation.js.map +0 -1
  283. package/dist/components/SearchModal.d.ts +0 -9
  284. package/dist/components/SearchModal.d.ts.map +0 -1
  285. package/dist/components/SearchModal.js +0 -74
  286. package/dist/components/SearchModal.js.map +0 -1
  287. package/dist/components/SideNav.d.ts +0 -9
  288. package/dist/components/SideNav.d.ts.map +0 -1
  289. package/dist/components/SideNav.js +0 -66
  290. package/dist/components/SideNav.js.map +0 -1
  291. package/dist/components/TableOfContents.d.ts +0 -10
  292. package/dist/components/TableOfContents.d.ts.map +0 -1
  293. package/dist/components/TableOfContents.js +0 -151
  294. package/dist/components/TableOfContents.js.map +0 -1
  295. package/dist/config/ConfigLoader.d.ts +0 -50
  296. package/dist/config/ConfigLoader.d.ts.map +0 -1
  297. package/dist/config/ConfigLoader.js +0 -214
  298. package/dist/config/ConfigLoader.js.map +0 -1
  299. package/dist/config/StyleCompiler.d.ts +0 -17
  300. package/dist/config/StyleCompiler.d.ts.map +0 -1
  301. package/dist/config/StyleCompiler.js +0 -116
  302. package/dist/config/StyleCompiler.js.map +0 -1
  303. package/dist/config/ThemeGenerator.d.ts +0 -14
  304. package/dist/config/ThemeGenerator.d.ts.map +0 -1
  305. package/dist/config/ThemeGenerator.js +0 -129
  306. package/dist/config/ThemeGenerator.js.map +0 -1
  307. package/dist/content/ContentLoader.d.ts +0 -70
  308. package/dist/content/ContentLoader.d.ts.map +0 -1
  309. package/dist/content/ContentLoader.js +0 -146
  310. package/dist/content/ContentLoader.js.map +0 -1
  311. package/dist/content/ContentProcessor.d.ts +0 -84
  312. package/dist/content/ContentProcessor.d.ts.map +0 -1
  313. package/dist/content/ContentProcessor.js +0 -380
  314. package/dist/content/ContentProcessor.js.map +0 -1
  315. package/dist/content/RouteManager.d.ts +0 -69
  316. package/dist/content/RouteManager.d.ts.map +0 -1
  317. package/dist/content/RouteManager.js +0 -143
  318. package/dist/content/RouteManager.js.map +0 -1
  319. package/dist/styles/components/callout.d.ts +0 -11
  320. package/dist/styles/components/callout.d.ts.map +0 -1
  321. package/dist/styles/components/callout.js +0 -87
  322. package/dist/styles/components/callout.js.map +0 -1
  323. package/dist/styles/components/card.d.ts +0 -11
  324. package/dist/styles/components/card.d.ts.map +0 -1
  325. package/dist/styles/components/card.js +0 -179
  326. package/dist/styles/components/card.js.map +0 -1
  327. package/dist/styles/components/codeblock.d.ts +0 -11
  328. package/dist/styles/components/codeblock.d.ts.map +0 -1
  329. package/dist/styles/components/codeblock.js +0 -251
  330. package/dist/styles/components/codeblock.js.map +0 -1
  331. package/dist/styles/components/content.d.ts +0 -11
  332. package/dist/styles/components/content.d.ts.map +0 -1
  333. package/dist/styles/components/content.js +0 -197
  334. package/dist/styles/components/content.js.map +0 -1
  335. package/dist/styles/components/fonts.d.ts +0 -11
  336. package/dist/styles/components/fonts.d.ts.map +0 -1
  337. package/dist/styles/components/fonts.js +0 -34
  338. package/dist/styles/components/fonts.js.map +0 -1
  339. package/dist/styles/components/header.d.ts +0 -11
  340. package/dist/styles/components/header.d.ts.map +0 -1
  341. package/dist/styles/components/header.js +0 -293
  342. package/dist/styles/components/header.js.map +0 -1
  343. package/dist/styles/components/heading.d.ts +0 -11
  344. package/dist/styles/components/heading.d.ts.map +0 -1
  345. package/dist/styles/components/heading.js +0 -115
  346. package/dist/styles/components/heading.js.map +0 -1
  347. package/dist/styles/components/layout.d.ts +0 -11
  348. package/dist/styles/components/layout.d.ts.map +0 -1
  349. package/dist/styles/components/layout.js +0 -79
  350. package/dist/styles/components/layout.js.map +0 -1
  351. package/dist/styles/components/mobilemenu.d.ts +0 -11
  352. package/dist/styles/components/mobilemenu.d.ts.map +0 -1
  353. package/dist/styles/components/mobilemenu.js +0 -112
  354. package/dist/styles/components/mobilemenu.js.map +0 -1
  355. package/dist/styles/components/reset.d.ts +0 -11
  356. package/dist/styles/components/reset.d.ts.map +0 -1
  357. package/dist/styles/components/reset.js +0 -131
  358. package/dist/styles/components/reset.js.map +0 -1
  359. package/dist/styles/components/searchmodal.d.ts +0 -11
  360. package/dist/styles/components/searchmodal.d.ts.map +0 -1
  361. package/dist/styles/components/searchmodal.js +0 -333
  362. package/dist/styles/components/searchmodal.js.map +0 -1
  363. package/dist/styles/components/sidenav.d.ts +0 -11
  364. package/dist/styles/components/sidenav.d.ts.map +0 -1
  365. package/dist/styles/components/sidenav.js +0 -212
  366. package/dist/styles/components/sidenav.js.map +0 -1
  367. package/dist/styles/components/toc.d.ts +0 -11
  368. package/dist/styles/components/toc.d.ts.map +0 -1
  369. package/dist/styles/components/toc.js +0 -120
  370. package/dist/styles/components/toc.js.map +0 -1
  371. package/dist/styles/components/typography.d.ts +0 -11
  372. package/dist/styles/components/typography.d.ts.map +0 -1
  373. package/dist/styles/components/typography.js +0 -248
  374. package/dist/styles/components/typography.js.map +0 -1
  375. package/dist/styles/components/utility.d.ts +0 -11
  376. package/dist/styles/components/utility.d.ts.map +0 -1
  377. package/dist/styles/components/utility.js +0 -231
  378. package/dist/styles/components/utility.js.map +0 -1
  379. package/dist/styles/types.d.ts +0 -79
  380. package/dist/styles/types.d.ts.map +0 -1
  381. package/dist/styles/types.js +0 -7
  382. package/dist/styles/types.js.map +0 -1
  383. package/dist/styles/utils.d.ts +0 -21
  384. package/dist/styles/utils.d.ts.map +0 -1
  385. package/dist/styles/utils.js +0 -50
  386. package/dist/styles/utils.js.map +0 -1
  387. package/dist/styles/variables/main.d.ts +0 -15
  388. package/dist/styles/variables/main.d.ts.map +0 -1
  389. package/dist/styles/variables/main.js +0 -116
  390. package/dist/styles/variables/main.js.map +0 -1
  391. package/dist/types/config.d.ts +0 -43
  392. package/dist/types/config.d.ts.map +0 -1
  393. package/dist/types/config.js +0 -7
  394. package/dist/types/config.js.map +0 -1
  395. package/dist/utils/language-utils.d.ts +0 -41
  396. package/dist/utils/language-utils.d.ts.map +0 -1
  397. package/dist/utils/language-utils.js +0 -79
  398. package/dist/utils/language-utils.js.map +0 -1
  399. package/dist/utils/slugify.d.ts +0 -20
  400. package/dist/utils/slugify.d.ts.map +0 -1
  401. package/dist/utils/slugify.js +0 -44
  402. package/dist/utils/slugify.js.map +0 -1
  403. package/templates/default/config/favicon.ico +0 -0
  404. package/templates/default/config/logo-dark.png +0 -0
  405. package/templates/default/config/logo.png +0 -0
  406. package/templates/default/config/sidenav.yaml +0 -36
  407. package/templates/default/config/site.json +0 -16
  408. package/templates/default/config/styles/main.json +0 -6
  409. package/templates/default/config/styles/typography.json +0 -6
  410. package/templates/default/content/en/changelog.md +0 -35
  411. package/templates/default/content/en/faq.md +0 -50
  412. package/templates/default/content/en/field-manual/engines.md +0 -59
  413. package/templates/default/content/en/field-manual/relativity.md +0 -46
  414. package/templates/default/content/en/field-manual/safety.md +0 -44
  415. package/templates/default/content/en/field-manual.md +0 -22
  416. package/templates/default/content/en/flight-school/navigation.md +0 -61
  417. package/templates/default/content/en/flight-school/primer.md +0 -64
  418. package/templates/default/content/en/flight-school.md +0 -48
  419. package/templates/default/content/en/index.md +0 -83
  420. package/templates/default/content/en/ship-systems/flux-sails.md +0 -33
  421. package/templates/default/content/en/ship-systems/photonic-core.md +0 -39
  422. package/templates/default/content/en/ship-systems.md +0 -25
  423. package/templates/default/package.json +0 -21
  424. package/templates/default/public/icons/academy.svg +0 -15
  425. package/templates/default/public/icons/manual.svg +0 -16
  426. package/templates/default/public/icons/relativity.svg +0 -16
  427. package/templates/default/public/icons/systems.svg +0 -14
  428. package/templates/default/public/icons/warning.svg +0 -14
  429. package/templates/default/public/images/flux-sails.svg +0 -33
  430. package/templates/default/public/images/photonic-core.svg +0 -44
  431. 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
+ }