@ciderpress/ui 1.0.0-rc.1

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 (428) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -0
  3. package/dist/0~142.js +2 -0
  4. package/dist/0~164.js +2 -0
  5. package/dist/0~2.js +2 -0
  6. package/dist/0~225.js +2 -0
  7. package/dist/0~288.js +2 -0
  8. package/dist/0~299.js +2 -0
  9. package/dist/0~3.js +2 -0
  10. package/dist/0~301.js +2 -0
  11. package/dist/0~311.js +2 -0
  12. package/dist/0~326.js +2 -0
  13. package/dist/0~40.js +2 -0
  14. package/dist/0~43.js +2 -0
  15. package/dist/0~433.js +2 -0
  16. package/dist/0~453.js +2 -0
  17. package/dist/0~457.js +2 -0
  18. package/dist/0~497.js +2 -0
  19. package/dist/0~511.js +2 -0
  20. package/dist/0~603.js +2 -0
  21. package/dist/0~616.js +2 -0
  22. package/dist/0~659.js +2 -0
  23. package/dist/0~670.js +2 -0
  24. package/dist/0~687.js +2 -0
  25. package/dist/0~706.js +2 -0
  26. package/dist/0~729.js +2 -0
  27. package/dist/0~768.js +2 -0
  28. package/dist/0~819.js +2 -0
  29. package/dist/0~830.js +2 -0
  30. package/dist/0~854.js +2 -0
  31. package/dist/0~863.js +2 -0
  32. package/dist/0~879.js +2 -0
  33. package/dist/0~96.js +2 -0
  34. package/dist/0~97.js +2 -0
  35. package/dist/components/FileTree/FileTree.css +90 -0
  36. package/dist/components/FileTree/FileTree.d.ts +7 -0
  37. package/dist/components/FileTree/FileTree.js +531 -0
  38. package/dist/components/FileTree/FileTreeItem.d.ts +8 -0
  39. package/dist/components/FileTree/RemoteSvgIcon.d.ts +11 -0
  40. package/dist/components/FileTree/index.d.ts +4 -0
  41. package/dist/components/folder-icons.d.ts +32 -0
  42. package/dist/components/languages.d.ts +7 -0
  43. package/dist/components/tree-parser/tree-parser.d.ts +12 -0
  44. package/dist/components/tree-parser/types.d.ts +11 -0
  45. package/dist/head/css/loader-apple.css +1 -0
  46. package/dist/head/css/loader-backdrop.css +1 -0
  47. package/dist/head/css/loader-dots.css +1 -0
  48. package/dist/head/css/themes/amber.css +1 -0
  49. package/dist/head/css/themes/arcade.css +1 -0
  50. package/dist/head/css/themes/grannysmith.css +1 -0
  51. package/dist/head/css/themes/honeycrisp.css +1 -0
  52. package/dist/head/css/themes/midnight.css +1 -0
  53. package/dist/head/css/themes/mulled.css +1 -0
  54. package/dist/head/js/loader-dots.js +1 -0
  55. package/dist/head/js/vscode-nav.js +1 -0
  56. package/dist/index.d.ts +43 -0
  57. package/dist/index.mjs +40 -0
  58. package/dist/node.d.ts +81 -0
  59. package/dist/node.mjs +17762 -0
  60. package/dist/plugins/mermaid/MermaidRenderer.tsx +480 -0
  61. package/dist/plugins/mermaid/mermaid.css +199 -0
  62. package/dist/theme/components/announcement/announcement-bar.css +103 -0
  63. package/dist/theme/components/announcement/announcement-bar.tsx +173 -0
  64. package/dist/theme/components/ask-ai/ask-ai-button.css +52 -0
  65. package/dist/theme/components/ask-ai/ask-ai-button.tsx +56 -0
  66. package/dist/theme/components/content-footer/content-footer-portal.tsx +70 -0
  67. package/dist/theme/components/content-footer/feedback.css +47 -0
  68. package/dist/theme/components/content-footer/feedback.tsx +96 -0
  69. package/dist/theme/components/content-footer/meta-actions.css +25 -0
  70. package/dist/theme/components/content-footer/meta-actions.tsx +57 -0
  71. package/dist/theme/components/content-footer/page-pager.css +61 -0
  72. package/dist/theme/components/content-footer/page-pager.tsx +110 -0
  73. package/dist/theme/components/edit-source-button.tsx +97 -0
  74. package/dist/theme/components/footer/site-footer.css +179 -0
  75. package/dist/theme/components/footer/site-footer.tsx +115 -0
  76. package/dist/theme/components/home/cta.css +67 -0
  77. package/dist/theme/components/home/cta.tsx +77 -0
  78. package/dist/theme/components/home/feature-card.css +52 -0
  79. package/dist/theme/components/home/feature-card.tsx +131 -0
  80. package/dist/theme/components/home/feature.tsx +79 -0
  81. package/dist/theme/components/home/hero-demo.css +66 -0
  82. package/dist/theme/components/home/hero-demo.tsx +46 -0
  83. package/dist/theme/components/home/hero.css +96 -0
  84. package/dist/theme/components/home/hero.tsx +111 -0
  85. package/dist/theme/components/home/layout.tsx +183 -0
  86. package/dist/theme/components/home/page-rail.css +21 -0
  87. package/dist/theme/components/home/page-rail.tsx +19 -0
  88. package/dist/theme/components/home/split.css +148 -0
  89. package/dist/theme/components/home/split.tsx +120 -0
  90. package/dist/theme/components/home/trust-strip.css +33 -0
  91. package/dist/theme/components/home/trust-strip.tsx +43 -0
  92. package/dist/theme/components/home/workspaces.tsx +71 -0
  93. package/dist/theme/components/nav/branch-tag.css +54 -0
  94. package/dist/theme/components/nav/branch-tag.tsx +101 -0
  95. package/dist/theme/components/nav/ciderpress-doc-layout.tsx +247 -0
  96. package/dist/theme/components/nav/ciderpress-docs-bar.css +505 -0
  97. package/dist/theme/components/nav/ciderpress-docs-bar.tsx +426 -0
  98. package/dist/theme/components/nav/ciderpress-header.css +172 -0
  99. package/dist/theme/components/nav/ciderpress-header.tsx +113 -0
  100. package/dist/theme/components/nav/ciderpress-layout-context.tsx +49 -0
  101. package/dist/theme/components/nav/ciderpress-nav-hamburger.css +193 -0
  102. package/dist/theme/components/nav/ciderpress-nav-hamburger.tsx +112 -0
  103. package/dist/theme/components/nav/ciderpress-nav-menu.css +130 -0
  104. package/dist/theme/components/nav/ciderpress-nav-menu.tsx +372 -0
  105. package/dist/theme/components/nav/ciderpress-nav-search.css +73 -0
  106. package/dist/theme/components/nav/ciderpress-nav-search.tsx +55 -0
  107. package/dist/theme/components/nav/ciderpress-nav-social-links.css +35 -0
  108. package/dist/theme/components/nav/ciderpress-nav-social-links.tsx +87 -0
  109. package/dist/theme/components/nav/floating-branch-indicator.css +106 -0
  110. package/dist/theme/components/nav/floating-branch-indicator.tsx +109 -0
  111. package/dist/theme/components/nav/layout.tsx +268 -0
  112. package/dist/theme/components/nav/mobile-nav-cta.tsx +79 -0
  113. package/dist/theme/components/nav/nav-divider.css +15 -0
  114. package/dist/theme/components/nav/nav-divider.tsx +19 -0
  115. package/dist/theme/components/nav/nav-logo.css +42 -0
  116. package/dist/theme/components/nav/nav-logo.tsx +262 -0
  117. package/dist/theme/components/nav/theme-switcher.css +92 -0
  118. package/dist/theme/components/nav/theme-switcher.tsx +298 -0
  119. package/dist/theme/components/nav/topbar-cta.css +45 -0
  120. package/dist/theme/components/nav/topbar-cta.tsx +39 -0
  121. package/dist/theme/components/nav/variant-toggle.css +70 -0
  122. package/dist/theme/components/nav/variant-toggle.tsx +83 -0
  123. package/dist/theme/components/nav/version-chip.css +31 -0
  124. package/dist/theme/components/nav/version-chip.tsx +45 -0
  125. package/dist/theme/components/nav/vscode-tag.css +32 -0
  126. package/dist/theme/components/nav/vscode-tag.tsx +20 -0
  127. package/dist/theme/components/openapi/code-sample.tsx +341 -0
  128. package/dist/theme/components/openapi/copy-markdown-button.tsx +41 -0
  129. package/dist/theme/components/openapi/icons.tsx +45 -0
  130. package/dist/theme/components/openapi/index.ts +6 -0
  131. package/dist/theme/components/openapi/method-badge.tsx +24 -0
  132. package/dist/theme/components/openapi/openapi.css +665 -0
  133. package/dist/theme/components/openapi/operation.tsx +200 -0
  134. package/dist/theme/components/openapi/overview.tsx +207 -0
  135. package/dist/theme/components/openapi/parameters-table.tsx +149 -0
  136. package/dist/theme/components/openapi/request-body.tsx +90 -0
  137. package/dist/theme/components/openapi/response-list.tsx +112 -0
  138. package/dist/theme/components/openapi/schema-viewer.tsx +277 -0
  139. package/dist/theme/components/openapi/security-badges.tsx +63 -0
  140. package/dist/theme/components/openapi/spec-utils.ts +122 -0
  141. package/dist/theme/components/shared/accordion.css +118 -0
  142. package/dist/theme/components/shared/accordion.stories.tsx +96 -0
  143. package/dist/theme/components/shared/accordion.tsx +191 -0
  144. package/dist/theme/components/shared/brand-logo.stories.tsx +49 -0
  145. package/dist/theme/components/shared/brand-mark.stories.tsx +53 -0
  146. package/dist/theme/components/shared/browser-window.css +1 -0
  147. package/dist/theme/components/shared/browser-window.stories.tsx +100 -0
  148. package/dist/theme/components/shared/browser-window.tsx +2 -0
  149. package/dist/theme/components/shared/card.stories.tsx +50 -0
  150. package/dist/theme/components/shared/card.tsx +35 -0
  151. package/dist/theme/components/shared/ciderpress-logo.tsx +93 -0
  152. package/dist/theme/components/shared/ciderpress-mark.tsx +77 -0
  153. package/dist/theme/components/shared/color.css +56 -0
  154. package/dist/theme/components/shared/color.stories.tsx +47 -0
  155. package/dist/theme/components/shared/color.tsx +69 -0
  156. package/dist/theme/components/shared/columns.css +37 -0
  157. package/dist/theme/components/shared/columns.stories.tsx +91 -0
  158. package/dist/theme/components/shared/columns.tsx +50 -0
  159. package/dist/theme/components/shared/desktop-window.css +339 -0
  160. package/dist/theme/components/shared/desktop-window.tsx +535 -0
  161. package/dist/theme/components/shared/field.css +158 -0
  162. package/dist/theme/components/shared/field.stories.tsx +100 -0
  163. package/dist/theme/components/shared/field.tsx +186 -0
  164. package/dist/theme/components/shared/frame.css +39 -0
  165. package/dist/theme/components/shared/frame.stories.tsx +49 -0
  166. package/dist/theme/components/shared/frame.tsx +76 -0
  167. package/dist/theme/components/shared/icon.stories.tsx +90 -0
  168. package/dist/theme/components/shared/icon.tsx +41 -0
  169. package/dist/theme/components/shared/prompt.css +178 -0
  170. package/dist/theme/components/shared/prompt.stories.tsx +57 -0
  171. package/dist/theme/components/shared/prompt.tsx +369 -0
  172. package/dist/theme/components/shared/resolve-card-icon.ts +28 -0
  173. package/dist/theme/components/shared/section-card.stories.tsx +51 -0
  174. package/dist/theme/components/shared/section-card.tsx +44 -0
  175. package/dist/theme/components/shared/section-grid.stories.tsx +46 -0
  176. package/dist/theme/components/shared/section-grid.tsx +15 -0
  177. package/dist/theme/components/shared/status-badge.css +37 -0
  178. package/dist/theme/components/shared/status-badge.stories.tsx +61 -0
  179. package/dist/theme/components/shared/status-badge.tsx +74 -0
  180. package/dist/theme/components/shared/steps.css +88 -0
  181. package/dist/theme/components/shared/steps.stories.tsx +68 -0
  182. package/dist/theme/components/shared/steps.tsx +100 -0
  183. package/dist/theme/components/shared/tech-icon-table.stories.tsx +29 -0
  184. package/dist/theme/components/shared/tech-icon-table.tsx +71 -0
  185. package/dist/theme/components/shared/tech-tag.stories.tsx +40 -0
  186. package/dist/theme/components/shared/tech-tag.tsx +31 -0
  187. package/dist/theme/components/shared/theme-color-palette.css +80 -0
  188. package/dist/theme/components/shared/theme-color-palette.stories.tsx +52 -0
  189. package/dist/theme/components/shared/theme-color-palette.tsx +275 -0
  190. package/dist/theme/components/shared/tooltip.css +69 -0
  191. package/dist/theme/components/shared/tooltip.stories.tsx +72 -0
  192. package/dist/theme/components/shared/tooltip.tsx +77 -0
  193. package/dist/theme/components/sidebar/framework-picker.css +77 -0
  194. package/dist/theme/components/sidebar/framework-picker.tsx +78 -0
  195. package/dist/theme/components/sidebar/sidebar-filter.test.ts +140 -0
  196. package/dist/theme/components/sidebar/sidebar-filter.ts +79 -0
  197. package/dist/theme/components/sidebar/sidebar-links.css +102 -0
  198. package/dist/theme/components/sidebar/sidebar-links.tsx +122 -0
  199. package/dist/theme/components/sidebar/sidebar-promo.css +42 -0
  200. package/dist/theme/components/sidebar/sidebar-promo.tsx +52 -0
  201. package/dist/theme/components/sidebar/sidebar-scope.tsx +151 -0
  202. package/dist/theme/components/sidebar/sidebar-toggle.css +181 -0
  203. package/dist/theme/components/sidebar/sidebar-toggle.tsx +127 -0
  204. package/dist/theme/components/theme-provider.tsx +637 -0
  205. package/dist/theme/components/workspaces/card.css +151 -0
  206. package/dist/theme/components/workspaces/card.tsx +166 -0
  207. package/dist/theme/components/workspaces/grid.tsx +62 -0
  208. package/dist/theme/css.d.ts +1 -0
  209. package/dist/theme/env.d.ts +12 -0
  210. package/dist/theme/fonts/GeistMono-Variable.woff2 +0 -0
  211. package/dist/theme/fonts/GeistPixel-Square.woff2 +0 -0
  212. package/dist/theme/fonts/GeistSans-Variable.woff2 +0 -0
  213. package/dist/theme/hooks/use-ciderpress.ts +61 -0
  214. package/dist/theme/hooks/use-nav-items.ts +76 -0
  215. package/dist/theme/icons/index.ts +2 -0
  216. package/dist/theme/icons/tech-map.ts +228 -0
  217. package/dist/theme/index.tsx +180 -0
  218. package/dist/theme/lib/read-social-links.ts +27 -0
  219. package/dist/theme/lib/route-link.tsx +126 -0
  220. package/dist/theme/lib/safe-url.test.ts +0 -0
  221. package/dist/theme/lib/safe-url.ts +68 -0
  222. package/dist/theme/lib/theme-favicon.ts +125 -0
  223. package/dist/theme/lib/user-config-stub.ts +12 -0
  224. package/dist/theme/styles/layers.css +32 -0
  225. package/dist/theme/styles/overrides/badge.css +26 -0
  226. package/dist/theme/styles/overrides/content-footer.css +80 -0
  227. package/dist/theme/styles/overrides/details.css +61 -0
  228. package/dist/theme/styles/overrides/fonts.css +32 -0
  229. package/dist/theme/styles/overrides/home-card.css +133 -0
  230. package/dist/theme/styles/overrides/home.css +243 -0
  231. package/dist/theme/styles/overrides/rail.css +337 -0
  232. package/dist/theme/styles/overrides/rspress.css +62 -0
  233. package/dist/theme/styles/overrides/scrollbar.css +25 -0
  234. package/dist/theme/styles/overrides/section-card.css +114 -0
  235. package/dist/theme/styles/overrides/sidebar.css +144 -0
  236. package/dist/theme/styles/overrides/tokens.css +288 -0
  237. package/dist/theme/styles/overrides/vscode.css +88 -0
  238. package/dist/theme/styles/themes/amber.css +545 -0
  239. package/dist/theme/styles/themes/arcade-fx.css +366 -0
  240. package/dist/theme/styles/themes/arcade.css +273 -0
  241. package/dist/theme/styles/themes/grannysmith.css +545 -0
  242. package/dist/theme/styles/themes/honeycrisp.css +545 -0
  243. package/dist/theme/styles/themes/midnight.css +273 -0
  244. package/dist/theme/styles/themes/mulled.css +817 -0
  245. package/package.json +103 -0
  246. package/src/theme/components/announcement/announcement-bar.css +103 -0
  247. package/src/theme/components/announcement/announcement-bar.tsx +173 -0
  248. package/src/theme/components/ask-ai/ask-ai-button.css +52 -0
  249. package/src/theme/components/ask-ai/ask-ai-button.tsx +56 -0
  250. package/src/theme/components/content-footer/content-footer-portal.tsx +70 -0
  251. package/src/theme/components/content-footer/feedback.css +47 -0
  252. package/src/theme/components/content-footer/feedback.tsx +96 -0
  253. package/src/theme/components/content-footer/meta-actions.css +25 -0
  254. package/src/theme/components/content-footer/meta-actions.tsx +57 -0
  255. package/src/theme/components/content-footer/page-pager.css +61 -0
  256. package/src/theme/components/content-footer/page-pager.tsx +110 -0
  257. package/src/theme/components/edit-source-button.tsx +97 -0
  258. package/src/theme/components/footer/site-footer.css +179 -0
  259. package/src/theme/components/footer/site-footer.tsx +115 -0
  260. package/src/theme/components/home/cta.css +67 -0
  261. package/src/theme/components/home/cta.tsx +77 -0
  262. package/src/theme/components/home/feature-card.css +52 -0
  263. package/src/theme/components/home/feature-card.tsx +131 -0
  264. package/src/theme/components/home/feature.tsx +79 -0
  265. package/src/theme/components/home/hero-demo.css +66 -0
  266. package/src/theme/components/home/hero-demo.tsx +46 -0
  267. package/src/theme/components/home/hero.css +96 -0
  268. package/src/theme/components/home/hero.tsx +111 -0
  269. package/src/theme/components/home/layout.tsx +183 -0
  270. package/src/theme/components/home/page-rail.css +21 -0
  271. package/src/theme/components/home/page-rail.tsx +19 -0
  272. package/src/theme/components/home/split.css +148 -0
  273. package/src/theme/components/home/split.tsx +120 -0
  274. package/src/theme/components/home/trust-strip.css +33 -0
  275. package/src/theme/components/home/trust-strip.tsx +43 -0
  276. package/src/theme/components/home/workspaces.tsx +71 -0
  277. package/src/theme/components/nav/branch-tag.css +54 -0
  278. package/src/theme/components/nav/branch-tag.tsx +101 -0
  279. package/src/theme/components/nav/ciderpress-doc-layout.tsx +247 -0
  280. package/src/theme/components/nav/ciderpress-docs-bar.css +505 -0
  281. package/src/theme/components/nav/ciderpress-docs-bar.tsx +426 -0
  282. package/src/theme/components/nav/ciderpress-header.css +172 -0
  283. package/src/theme/components/nav/ciderpress-header.tsx +113 -0
  284. package/src/theme/components/nav/ciderpress-layout-context.tsx +49 -0
  285. package/src/theme/components/nav/ciderpress-nav-hamburger.css +193 -0
  286. package/src/theme/components/nav/ciderpress-nav-hamburger.tsx +112 -0
  287. package/src/theme/components/nav/ciderpress-nav-menu.css +130 -0
  288. package/src/theme/components/nav/ciderpress-nav-menu.tsx +372 -0
  289. package/src/theme/components/nav/ciderpress-nav-search.css +73 -0
  290. package/src/theme/components/nav/ciderpress-nav-search.tsx +55 -0
  291. package/src/theme/components/nav/ciderpress-nav-social-links.css +35 -0
  292. package/src/theme/components/nav/ciderpress-nav-social-links.tsx +87 -0
  293. package/src/theme/components/nav/floating-branch-indicator.css +106 -0
  294. package/src/theme/components/nav/floating-branch-indicator.tsx +109 -0
  295. package/src/theme/components/nav/layout.tsx +268 -0
  296. package/src/theme/components/nav/mobile-nav-cta.tsx +79 -0
  297. package/src/theme/components/nav/nav-divider.css +15 -0
  298. package/src/theme/components/nav/nav-divider.tsx +19 -0
  299. package/src/theme/components/nav/nav-logo.css +42 -0
  300. package/src/theme/components/nav/nav-logo.tsx +262 -0
  301. package/src/theme/components/nav/theme-switcher.css +92 -0
  302. package/src/theme/components/nav/theme-switcher.tsx +298 -0
  303. package/src/theme/components/nav/topbar-cta.css +45 -0
  304. package/src/theme/components/nav/topbar-cta.tsx +39 -0
  305. package/src/theme/components/nav/variant-toggle.css +70 -0
  306. package/src/theme/components/nav/variant-toggle.tsx +83 -0
  307. package/src/theme/components/nav/version-chip.css +31 -0
  308. package/src/theme/components/nav/version-chip.tsx +45 -0
  309. package/src/theme/components/nav/vscode-tag.css +32 -0
  310. package/src/theme/components/nav/vscode-tag.tsx +20 -0
  311. package/src/theme/components/openapi/code-sample.tsx +341 -0
  312. package/src/theme/components/openapi/copy-markdown-button.tsx +41 -0
  313. package/src/theme/components/openapi/icons.tsx +45 -0
  314. package/src/theme/components/openapi/index.ts +6 -0
  315. package/src/theme/components/openapi/method-badge.tsx +24 -0
  316. package/src/theme/components/openapi/openapi.css +665 -0
  317. package/src/theme/components/openapi/operation.tsx +200 -0
  318. package/src/theme/components/openapi/overview.tsx +207 -0
  319. package/src/theme/components/openapi/parameters-table.tsx +149 -0
  320. package/src/theme/components/openapi/request-body.tsx +90 -0
  321. package/src/theme/components/openapi/response-list.tsx +112 -0
  322. package/src/theme/components/openapi/schema-viewer.tsx +277 -0
  323. package/src/theme/components/openapi/security-badges.tsx +63 -0
  324. package/src/theme/components/openapi/spec-utils.ts +122 -0
  325. package/src/theme/components/shared/accordion.css +118 -0
  326. package/src/theme/components/shared/accordion.stories.tsx +96 -0
  327. package/src/theme/components/shared/accordion.tsx +191 -0
  328. package/src/theme/components/shared/brand-logo.stories.tsx +49 -0
  329. package/src/theme/components/shared/brand-mark.stories.tsx +53 -0
  330. package/src/theme/components/shared/browser-window.css +1 -0
  331. package/src/theme/components/shared/browser-window.stories.tsx +100 -0
  332. package/src/theme/components/shared/browser-window.tsx +2 -0
  333. package/src/theme/components/shared/card.stories.tsx +50 -0
  334. package/src/theme/components/shared/card.tsx +35 -0
  335. package/src/theme/components/shared/ciderpress-logo.tsx +93 -0
  336. package/src/theme/components/shared/ciderpress-mark.tsx +77 -0
  337. package/src/theme/components/shared/color.css +56 -0
  338. package/src/theme/components/shared/color.stories.tsx +47 -0
  339. package/src/theme/components/shared/color.tsx +69 -0
  340. package/src/theme/components/shared/columns.css +37 -0
  341. package/src/theme/components/shared/columns.stories.tsx +91 -0
  342. package/src/theme/components/shared/columns.tsx +50 -0
  343. package/src/theme/components/shared/desktop-window.css +339 -0
  344. package/src/theme/components/shared/desktop-window.tsx +535 -0
  345. package/src/theme/components/shared/field.css +158 -0
  346. package/src/theme/components/shared/field.stories.tsx +100 -0
  347. package/src/theme/components/shared/field.tsx +186 -0
  348. package/src/theme/components/shared/frame.css +39 -0
  349. package/src/theme/components/shared/frame.stories.tsx +49 -0
  350. package/src/theme/components/shared/frame.tsx +76 -0
  351. package/src/theme/components/shared/icon.stories.tsx +90 -0
  352. package/src/theme/components/shared/icon.tsx +41 -0
  353. package/src/theme/components/shared/prompt.css +178 -0
  354. package/src/theme/components/shared/prompt.stories.tsx +57 -0
  355. package/src/theme/components/shared/prompt.tsx +369 -0
  356. package/src/theme/components/shared/resolve-card-icon.ts +28 -0
  357. package/src/theme/components/shared/section-card.stories.tsx +51 -0
  358. package/src/theme/components/shared/section-card.tsx +44 -0
  359. package/src/theme/components/shared/section-grid.stories.tsx +46 -0
  360. package/src/theme/components/shared/section-grid.tsx +15 -0
  361. package/src/theme/components/shared/status-badge.css +37 -0
  362. package/src/theme/components/shared/status-badge.stories.tsx +61 -0
  363. package/src/theme/components/shared/status-badge.tsx +74 -0
  364. package/src/theme/components/shared/steps.css +88 -0
  365. package/src/theme/components/shared/steps.stories.tsx +68 -0
  366. package/src/theme/components/shared/steps.tsx +100 -0
  367. package/src/theme/components/shared/tech-icon-table.stories.tsx +29 -0
  368. package/src/theme/components/shared/tech-icon-table.tsx +71 -0
  369. package/src/theme/components/shared/tech-tag.stories.tsx +40 -0
  370. package/src/theme/components/shared/tech-tag.tsx +31 -0
  371. package/src/theme/components/shared/theme-color-palette.css +80 -0
  372. package/src/theme/components/shared/theme-color-palette.stories.tsx +52 -0
  373. package/src/theme/components/shared/theme-color-palette.tsx +275 -0
  374. package/src/theme/components/shared/tooltip.css +69 -0
  375. package/src/theme/components/shared/tooltip.stories.tsx +72 -0
  376. package/src/theme/components/shared/tooltip.tsx +77 -0
  377. package/src/theme/components/sidebar/framework-picker.css +77 -0
  378. package/src/theme/components/sidebar/framework-picker.tsx +78 -0
  379. package/src/theme/components/sidebar/sidebar-filter.test.ts +140 -0
  380. package/src/theme/components/sidebar/sidebar-filter.ts +79 -0
  381. package/src/theme/components/sidebar/sidebar-links.css +102 -0
  382. package/src/theme/components/sidebar/sidebar-links.tsx +122 -0
  383. package/src/theme/components/sidebar/sidebar-promo.css +42 -0
  384. package/src/theme/components/sidebar/sidebar-promo.tsx +52 -0
  385. package/src/theme/components/sidebar/sidebar-scope.tsx +151 -0
  386. package/src/theme/components/sidebar/sidebar-toggle.css +181 -0
  387. package/src/theme/components/sidebar/sidebar-toggle.tsx +127 -0
  388. package/src/theme/components/theme-provider.tsx +637 -0
  389. package/src/theme/components/workspaces/card.css +151 -0
  390. package/src/theme/components/workspaces/card.tsx +166 -0
  391. package/src/theme/components/workspaces/grid.tsx +62 -0
  392. package/src/theme/css.d.ts +1 -0
  393. package/src/theme/env.d.ts +12 -0
  394. package/src/theme/fonts/GeistMono-Variable.woff2 +0 -0
  395. package/src/theme/fonts/GeistPixel-Square.woff2 +0 -0
  396. package/src/theme/fonts/GeistSans-Variable.woff2 +0 -0
  397. package/src/theme/hooks/use-ciderpress.ts +61 -0
  398. package/src/theme/hooks/use-nav-items.ts +76 -0
  399. package/src/theme/icons/index.ts +2 -0
  400. package/src/theme/icons/tech-map.ts +228 -0
  401. package/src/theme/index.tsx +180 -0
  402. package/src/theme/lib/read-social-links.ts +27 -0
  403. package/src/theme/lib/route-link.tsx +126 -0
  404. package/src/theme/lib/safe-url.test.ts +0 -0
  405. package/src/theme/lib/safe-url.ts +68 -0
  406. package/src/theme/lib/theme-favicon.ts +125 -0
  407. package/src/theme/lib/user-config-stub.ts +12 -0
  408. package/src/theme/styles/layers.css +32 -0
  409. package/src/theme/styles/overrides/badge.css +26 -0
  410. package/src/theme/styles/overrides/content-footer.css +80 -0
  411. package/src/theme/styles/overrides/details.css +61 -0
  412. package/src/theme/styles/overrides/fonts.css +32 -0
  413. package/src/theme/styles/overrides/home-card.css +133 -0
  414. package/src/theme/styles/overrides/home.css +243 -0
  415. package/src/theme/styles/overrides/rail.css +337 -0
  416. package/src/theme/styles/overrides/rspress.css +62 -0
  417. package/src/theme/styles/overrides/scrollbar.css +25 -0
  418. package/src/theme/styles/overrides/section-card.css +114 -0
  419. package/src/theme/styles/overrides/sidebar.css +144 -0
  420. package/src/theme/styles/overrides/tokens.css +288 -0
  421. package/src/theme/styles/overrides/vscode.css +88 -0
  422. package/src/theme/styles/themes/amber.css +545 -0
  423. package/src/theme/styles/themes/arcade-fx.css +366 -0
  424. package/src/theme/styles/themes/arcade.css +273 -0
  425. package/src/theme/styles/themes/grannysmith.css +545 -0
  426. package/src/theme/styles/themes/honeycrisp.css +545 -0
  427. package/src/theme/styles/themes/midnight.css +273 -0
  428. package/src/theme/styles/themes/mulled.css +817 -0
@@ -0,0 +1,372 @@
1
+ import { useLocation } from '@rspress/core/runtime'
2
+ import { clsx } from 'clsx'
3
+ import { match } from 'massaman/match'
4
+ import { useEffect, useMemo, useRef, useState } from 'react'
5
+ import type React from 'react'
6
+
7
+ import { RouteLink } from '../../lib/route-link.tsx'
8
+ import { Icon } from '../shared/icon.tsx'
9
+
10
+ import './ciderpress-nav-menu.css'
11
+
12
+ /**
13
+ * Width reserved at the end of the menu strip for the overflow toggle
14
+ * (the "More ▾" button). Subtracted from the available width when
15
+ * deciding how many items can fit inline.
16
+ */
17
+ const OVERFLOW_TOGGLE_WIDTH = 96
18
+
19
+ /**
20
+ * Inter-item gap matching `--cp-nav-menu-gap`. Used during width
21
+ * accounting so we don't have to query computed style on every frame.
22
+ */
23
+ const DEFAULT_GAP_PX = 16
24
+
25
+ /**
26
+ * Single primary-nav entry — matches the shape of `site.nav[*]` in
27
+ * `ciderpress.config.ts`.
28
+ */
29
+ export interface CiderpressNavMenuItem {
30
+ readonly text: string
31
+ readonly link: string
32
+ }
33
+
34
+ /**
35
+ * Props for `<CiderpressNavMenu />`.
36
+ */
37
+ export interface CiderpressNavMenuProps {
38
+ readonly items: readonly CiderpressNavMenuItem[]
39
+ }
40
+
41
+ /**
42
+ * Primary nav menu with overflow → dropdown handling. Renders all items
43
+ * inline when they fit; when the container is too narrow, the trailing
44
+ * items collapse into a "More ▾" popover so the strip never pushes off
45
+ * screen.
46
+ *
47
+ * Sizing is measured with a `ResizeObserver` on the wrapper plus a
48
+ * hidden measurement row that always renders every item at full width.
49
+ * The hidden row gives us a stable per-item width to walk against the
50
+ * live available width.
51
+ *
52
+ * @param props - Item list
53
+ * @returns Nav element
54
+ */
55
+ export function CiderpressNavMenu(props: CiderpressNavMenuProps): React.ReactElement {
56
+ // Pull items from props when configured, otherwise fall back to scraping
57
+ // Rspress's hidden `.rp-nav-menu` (it's the source of truth at runtime
58
+ // when the `nav` field isn't exposed on the Rspress site typings).
59
+ const [scrapedItems, setScrapedItems] = useState<readonly CiderpressNavMenuItem[]>([])
60
+ useEffect(() => {
61
+ if (props.items.length > 0) {
62
+ return
63
+ }
64
+ const initial = scrapeNavItems()
65
+ if (initial.length > 0) {
66
+ setScrapedItems(initial)
67
+ return
68
+ }
69
+ const observer = new MutationObserver(() => {
70
+ const fresh = scrapeNavItems()
71
+ if (fresh.length > 0) {
72
+ setScrapedItems(fresh)
73
+ observer.disconnect()
74
+ }
75
+ })
76
+ observer.observe(document.body, { childList: true, subtree: true })
77
+ return () => {
78
+ observer.disconnect()
79
+ }
80
+ }, [props.items])
81
+ const items = useMemo<readonly CiderpressNavMenuItem[]>(
82
+ () =>
83
+ match(props.items.length > 0)
84
+ .with(true, () => props.items)
85
+ .otherwise(() => scrapedItems),
86
+ [props.items, scrapedItems]
87
+ )
88
+
89
+ const containerRef = useRef<HTMLDivElement>(null)
90
+ const measureRef = useRef<HTMLDivElement>(null)
91
+ const [visibleCount, setVisibleCount] = useState(items.length)
92
+ const [overflowOpen, setOverflowOpen] = useState(false)
93
+ const overflowRef = useRef<HTMLDivElement>(null)
94
+ const { pathname } = useLocation()
95
+
96
+ useEffect(() => {
97
+ const container = containerRef.current
98
+ const measure = measureRef.current
99
+ if (container === null || measure === null) {
100
+ return
101
+ }
102
+
103
+ // We observe the menu's PARENT (the wrap), not the menu itself.
104
+ // Measuring the menu would self-collapse: when items hide, the
105
+ // menu shrinks, clientWidth drops, the budget shrinks, more items
106
+ // hide. Observing the parent gives us a width that's a function of
107
+ // the layout (not of how many items we currently render).
108
+ const parent = container.parentElement
109
+ if (parent === null) {
110
+ return
111
+ }
112
+
113
+ const observer = new ResizeObserver(() => {
114
+ setVisibleCount(computeMenuFit({ measure, parent, container }))
115
+ })
116
+ observer.observe(parent)
117
+ const observedChildren = [...parent.children].filter(
118
+ (child) => child !== container && inLayoutFlow(child)
119
+ )
120
+ observedChildren.map((child) => observer.observe(child))
121
+ setVisibleCount(computeMenuFit({ measure, parent, container }))
122
+ return () => {
123
+ observer.disconnect()
124
+ }
125
+ }, [items])
126
+
127
+ useEffect(() => {
128
+ if (!overflowOpen) {
129
+ return
130
+ }
131
+ function onDocClick(event: MouseEvent): void {
132
+ const target = event.target as Node | null
133
+ if (
134
+ target !== null &&
135
+ overflowRef.current !== null &&
136
+ !overflowRef.current.contains(target)
137
+ ) {
138
+ setOverflowOpen(false)
139
+ }
140
+ }
141
+ document.addEventListener('mousedown', onDocClick)
142
+ return () => {
143
+ document.removeEventListener('mousedown', onDocClick)
144
+ }
145
+ }, [overflowOpen])
146
+
147
+ const visible = items.slice(0, visibleCount)
148
+ const overflow = items.slice(visibleCount)
149
+ const hasOverflow = overflow.length > 0
150
+
151
+ return (
152
+ <>
153
+ <div ref={measureRef} className="cp-nav-menu-measure" aria-hidden="true">
154
+ {items.map((item) => (
155
+ <span key={item.link} data-cp-menu-item className="cp-nav-menu__item">
156
+ {item.text}
157
+ </span>
158
+ ))}
159
+ </div>
160
+
161
+ <nav ref={containerRef} className="cp-nav-menu" aria-label="Primary">
162
+ {visible.map((item) => (
163
+ <RouteLink
164
+ key={item.link}
165
+ href={item.link}
166
+ className={clsx('cp-nav-menu__item', {
167
+ 'cp-nav-menu__item--active': isActive(pathname, item.link),
168
+ })}
169
+ >
170
+ {item.text}
171
+ </RouteLink>
172
+ ))}
173
+ {hasOverflow && (
174
+ <div ref={overflowRef} className="cp-nav-menu__overflow">
175
+ <button
176
+ type="button"
177
+ className="cp-nav-menu__overflow-toggle"
178
+ onClick={() => setOverflowOpen((open) => !open)}
179
+ aria-haspopup="menu"
180
+ aria-expanded={overflowOpen}
181
+ >
182
+ <span>More</span>
183
+ <Icon icon="pixelarticons:chevron-down" width={12} height={12} />
184
+ </button>
185
+ {overflowOpen && (
186
+ <ul className="cp-nav-menu__overflow-popover" role="menu">
187
+ {overflow.map((item) => (
188
+ <li key={item.link} role="none">
189
+ <RouteLink
190
+ href={item.link}
191
+ role="menuitem"
192
+ className={clsx('cp-nav-menu__overflow-item', {
193
+ 'cp-nav-menu__overflow-item--active': isActive(pathname, item.link),
194
+ })}
195
+ onClick={() => setOverflowOpen(false)}
196
+ >
197
+ {item.text}
198
+ </RouteLink>
199
+ </li>
200
+ ))}
201
+ </ul>
202
+ )}
203
+ </div>
204
+ )}
205
+ </nav>
206
+ </>
207
+ )
208
+ }
209
+
210
+ export { CiderpressNavMenu as default }
211
+
212
+ /**
213
+ * Walk the per-item widths left-to-right, accumulating until we'd
214
+ * exceed the available width. When the accumulator already exceeds the
215
+ * cheap-fit budget, reserve space for the overflow toggle so the toggle
216
+ * itself doesn't push the last visible item out.
217
+ *
218
+ * @private
219
+ */
220
+ function computeVisibleCount(params: {
221
+ readonly widths: readonly number[]
222
+ readonly available: number
223
+ readonly gap: number
224
+ readonly overflowReserve: number
225
+ }): number {
226
+ const { widths, available, gap, overflowReserve } = params
227
+ // No items, or the container hasn't been laid out yet — assume every
228
+ // item fits. The ResizeObserver will recompute as soon as the
229
+ // container has a real width.
230
+ if (widths.length === 0 || available <= 0) {
231
+ return widths.length
232
+ }
233
+
234
+ const fullTotal = widths.reduce((acc, w, i) => acc + w + gapAt(i, gap), 0)
235
+ if (fullTotal <= available) {
236
+ return widths.length
237
+ }
238
+
239
+ // Otherwise we know we need overflow; reserve space for the toggle.
240
+ const budget = available - overflowReserve
241
+ const walked = widths.reduce<{
242
+ readonly running: number
243
+ readonly count: number
244
+ readonly done: boolean
245
+ }>(
246
+ (state, width, index) => {
247
+ if (state.done) {
248
+ return state
249
+ }
250
+ const next = state.running + width + gapAt(index, gap)
251
+ if (next > budget) {
252
+ return { ...state, done: true }
253
+ }
254
+ return { running: next, count: state.count + 1, done: false }
255
+ },
256
+ { running: 0, count: 0, done: false }
257
+ )
258
+ return walked.count
259
+ }
260
+
261
+ /**
262
+ * Gap to add before the item at `index` when summing widths. Index 0
263
+ * has no leading gap; subsequent items get the menu's inter-item gap.
264
+ *
265
+ * @private
266
+ * @param index - Position of the item in the width array.
267
+ * @param gap - Inter-item gap in pixels.
268
+ * @returns Leading gap to add before this item.
269
+ */
270
+ function gapAt(index: number, gap: number): number {
271
+ if (index === 0) {
272
+ return 0
273
+ }
274
+ return gap
275
+ }
276
+
277
+ /**
278
+ * Read every anchor under Rspress's hidden `.rp-nav-menu` and project
279
+ * it into a `{ text, link }` item. Anchors with empty text or `href`
280
+ * are dropped so we never surface a placeholder entry.
281
+ *
282
+ * @private
283
+ * @returns Nav items currently in the DOM (empty array when not mounted).
284
+ */
285
+ function scrapeNavItems(): readonly CiderpressNavMenuItem[] {
286
+ const anchors = document.querySelectorAll<HTMLAnchorElement>('.rp-nav-menu .rp-nav-menu__item a')
287
+ return [...anchors]
288
+ .map((anchor) => ({
289
+ text: readAnchorText(anchor),
290
+ link: anchor.getAttribute('href') ?? '',
291
+ }))
292
+ .filter((item) => item.text !== '' && item.link !== '')
293
+ }
294
+
295
+ /**
296
+ * Pull the trimmed text content from an anchor. Returns an empty
297
+ * string when `textContent` is missing — callers treat empty as "skip
298
+ * this anchor".
299
+ *
300
+ * @private
301
+ * @param anchor - Anchor element to read.
302
+ * @returns Trimmed inner text, or empty string when absent.
303
+ */
304
+ function readAnchorText(anchor: HTMLAnchorElement): string {
305
+ const text = anchor.textContent
306
+ if (text === null) {
307
+ return ''
308
+ }
309
+ return text.trim()
310
+ }
311
+
312
+ /**
313
+ * Exclude elements removed from the layout — the hidden measure layer
314
+ * is `position: absolute`, anything `display: none` is gone, anything
315
+ * `visibility: hidden` still occupies space so it stays counted. We
316
+ * only want flex-contributing siblings.
317
+ *
318
+ * @private
319
+ * @param child - Sibling element to test.
320
+ * @returns True when the element contributes to the parent's flex layout.
321
+ */
322
+ function inLayoutFlow(child: Element): boolean {
323
+ const cs = globalThis.getComputedStyle(child)
324
+ return cs.display !== 'none' && cs.position !== 'absolute' && cs.position !== 'fixed'
325
+ }
326
+
327
+ /**
328
+ * Compute how many items fit inline given the live layout. Measures
329
+ * each item's full-width footprint from the hidden measure row, then
330
+ * subtracts every layout sibling's width (plus inter-flex gaps) from
331
+ * the parent's clientWidth to derive the budget for the menu strip.
332
+ *
333
+ * @private
334
+ * @param params - Refs to the measure row, container, and parent.
335
+ * @returns Number of items that fit inline.
336
+ */
337
+ function computeMenuFit(params: {
338
+ readonly measure: HTMLDivElement
339
+ readonly parent: HTMLElement
340
+ readonly container: HTMLDivElement
341
+ }): number {
342
+ const { measure, parent, container } = params
343
+ const itemElements = measure.querySelectorAll<HTMLElement>('[data-cp-menu-item]')
344
+ const widths = [...itemElements].map((el) => el.getBoundingClientRect().width)
345
+ const parentWidth = parent.clientWidth
346
+ const parentGap = parseFloat(globalThis.getComputedStyle(parent).columnGap || '0') || 0
347
+ const layoutSiblings = [...parent.children].filter(
348
+ (child) => child !== container && inLayoutFlow(child)
349
+ )
350
+ const siblingsWidth = layoutSiblings.reduce(
351
+ (sum, child) => sum + child.getBoundingClientRect().width,
352
+ 0
353
+ )
354
+ const gapCount = Math.max(0, layoutSiblings.length + 1 - 1)
355
+ const available = Math.max(0, parentWidth - siblingsWidth - gapCount * parentGap)
356
+ return computeVisibleCount({
357
+ widths,
358
+ available,
359
+ gap: DEFAULT_GAP_PX,
360
+ overflowReserve: OVERFLOW_TOGGLE_WIDTH,
361
+ })
362
+ }
363
+
364
+ /**
365
+ * @private
366
+ */
367
+ function isActive(pathname: string, link: string): boolean {
368
+ if (link === '/') {
369
+ return pathname === '/'
370
+ }
371
+ return pathname.startsWith(link)
372
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * CiderpressNavSearch — pill-shaped trigger that opens Rspress's
3
+ * search modal via a synthetic Cmd+K event.
4
+ *
5
+ * Theme-author API:
6
+ * --cp-nav-search-bg background colour
7
+ * --cp-nav-search-border border colour (idle)
8
+ * --cp-nav-search-border-hover border colour on hover
9
+ * --cp-nav-search-color text colour
10
+ * --cp-nav-search-width total button width (default 200px)
11
+ * --cp-nav-search-radius pill radius (default 999px)
12
+ * --cp-nav-search-padding horizontal padding (default 12px)
13
+ */
14
+ .cp-nav-search {
15
+ display: inline-flex;
16
+ align-items: center;
17
+ gap: 8px;
18
+ width: var(--cp-nav-search-width, 160px);
19
+ height: 32px;
20
+ padding: 0 var(--cp-nav-search-padding, 12px);
21
+ background: var(--cp-nav-search-bg, var(--cp-c-bg-soft));
22
+ border: 1px solid var(--cp-nav-search-border, var(--cp-c-border));
23
+ border-radius: var(--cp-nav-search-radius, 8px);
24
+ color: var(--cp-nav-search-color, var(--cp-c-text-3));
25
+ font-family: var(--cp-font-family-base);
26
+ font-size: 13px;
27
+ font-weight: 500;
28
+ cursor: pointer;
29
+ transition:
30
+ border-color 0.15s ease,
31
+ color 0.15s ease;
32
+ }
33
+
34
+ .cp-nav-search:hover {
35
+ border-color: var(--cp-nav-search-border-hover, var(--cp-c-brand-1));
36
+ color: var(--cp-c-text-1);
37
+ }
38
+
39
+ .cp-nav-search__icon {
40
+ flex-shrink: 0;
41
+ color: var(--cp-c-text-3);
42
+ }
43
+
44
+ .cp-nav-search__label {
45
+ flex: 1 1 auto;
46
+ text-align: left;
47
+ }
48
+
49
+ .cp-nav-search__hotkey {
50
+ display: inline-flex;
51
+ align-items: center;
52
+ gap: 1px;
53
+ flex-shrink: 0;
54
+ padding: 1px 5px;
55
+ background: var(--cp-c-bg-elv, rgba(255, 255, 255, 0.08));
56
+ border: 1px solid var(--cp-c-border);
57
+ border-radius: 4px;
58
+ font-family: inherit;
59
+ font-size: 11px;
60
+ line-height: 1.2;
61
+ color: var(--cp-c-text-3);
62
+ }
63
+
64
+ @media (max-width: 640px) {
65
+ .cp-nav-search__label,
66
+ .cp-nav-search__hotkey {
67
+ display: none;
68
+ }
69
+ .cp-nav-search {
70
+ width: auto;
71
+ padding: 0 10px;
72
+ }
73
+ }
@@ -0,0 +1,55 @@
1
+ import type React from 'react'
2
+
3
+ import { Icon } from '../shared/icon.tsx'
4
+
5
+ import './ciderpress-nav-search.css'
6
+
7
+ /**
8
+ * Search trigger button. Click dispatches a synthetic `Cmd+K` keyboard
9
+ * event so Rspress's underlying search modal opens — same shortcut the
10
+ * user can press globally. We don't reach into Rspress's search
11
+ * internals; the keyboard handler is the public contract.
12
+ *
13
+ * @returns Search trigger button
14
+ */
15
+ export function CiderpressNavSearch(): React.ReactElement {
16
+ return (
17
+ <button
18
+ type="button"
19
+ className="cp-nav-search"
20
+ onClick={dispatchSearchHotkey}
21
+ aria-label="Search docs"
22
+ >
23
+ <Icon icon="pixelarticons:search" width={16} height={16} className="cp-nav-search__icon" />
24
+ <span className="cp-nav-search__label">Search</span>
25
+ <kbd className="cp-nav-search__hotkey" aria-hidden="true">
26
+ <span>⌘</span>
27
+ <span>K</span>
28
+ </kbd>
29
+ </button>
30
+ )
31
+ }
32
+
33
+ /**
34
+ * Dispatch a synthetic `Cmd+K` (mac) or `Ctrl+K` (everything else)
35
+ * `keydown` so Rspress's global search modal opens. Modifier choice is
36
+ * driven off `navigator.platform`, which is the same heuristic
37
+ * Rspress's own shortcut handler uses.
38
+ *
39
+ * @private
40
+ */
41
+ function dispatchSearchHotkey(): void {
42
+ const isMac = typeof navigator !== 'undefined' && navigator.platform.toLowerCase().includes('mac')
43
+ const event = new KeyboardEvent('keydown', {
44
+ key: 'k',
45
+ code: 'KeyK',
46
+ keyCode: 75,
47
+ metaKey: isMac,
48
+ ctrlKey: !isMac,
49
+ bubbles: true,
50
+ cancelable: true,
51
+ })
52
+ document.dispatchEvent(event)
53
+ }
54
+
55
+ export { CiderpressNavSearch as default }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * CiderpressNavSocialLinks — icon cluster in the topbar right zone.
3
+ *
4
+ * Theme-author API:
5
+ * --cp-nav-social-gap spacing between icons (default 4px)
6
+ * --cp-nav-social-color icon colour (idle)
7
+ * --cp-nav-social-color-hover icon colour on hover
8
+ * --cp-nav-social-size clickable area (default 32px)
9
+ */
10
+ .cp-nav-social {
11
+ display: inline-flex;
12
+ align-items: center;
13
+ gap: var(--cp-nav-social-gap, 4px);
14
+ flex-shrink: 0;
15
+ }
16
+
17
+ .cp-nav-social__item {
18
+ display: inline-flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ width: var(--cp-nav-social-size, 32px);
22
+ height: var(--cp-nav-social-size, 32px);
23
+ border-radius: 6px;
24
+ color: var(--cp-nav-social-color, var(--cp-c-text-2));
25
+ text-decoration: none;
26
+ transition:
27
+ color 0.15s ease,
28
+ background 0.15s ease;
29
+ }
30
+
31
+ .cp-nav-social__item:hover {
32
+ background: var(--cp-c-bg-soft);
33
+ color: var(--cp-nav-social-color-hover, var(--cp-c-brand-1));
34
+ text-decoration: none;
35
+ }
@@ -0,0 +1,87 @@
1
+ import type React from 'react'
2
+
3
+ import { safeUrl } from '../../lib/safe-url.ts'
4
+ import { Icon } from '../shared/icon.tsx'
5
+
6
+ import './ciderpress-nav-social-links.css'
7
+
8
+ /**
9
+ * Single social-link entry — matches the shape of `socialLinks` in
10
+ * `ciderpress.config.ts`.
11
+ */
12
+ export interface CiderpressSocialLink {
13
+ readonly icon: string
14
+ readonly mode: string
15
+ readonly content: string
16
+ readonly label?: string
17
+ }
18
+
19
+ export interface CiderpressNavSocialLinksProps {
20
+ readonly links: readonly CiderpressSocialLink[]
21
+ }
22
+
23
+ /**
24
+ * Maps Rspress's social-link `icon` slugs to pixelarticons icon ids.
25
+ * Fallback for unmapped slugs is the generic `link` glyph.
26
+ */
27
+ const ICON_MAP: Readonly<Record<string, string>> = Object.freeze({
28
+ github: 'pixelarticons:github',
29
+ npm: 'pixelarticons:package',
30
+ twitter: 'pixelarticons:twitter',
31
+ x: 'pixelarticons:twitter',
32
+ discord: 'pixelarticons:chat',
33
+ youtube: 'pixelarticons:play',
34
+ bluesky: 'pixelarticons:bluesky',
35
+ mastodon: 'pixelarticons:user',
36
+ })
37
+
38
+ /**
39
+ * Renders the configured social links as a cluster of icon buttons in
40
+ * the topbar. Returns `null` when no links are configured.
41
+ *
42
+ * @param props - List of social links from `site.socialLinks`
43
+ * @returns Cluster of icon links, or `null`
44
+ */
45
+ export function CiderpressNavSocialLinks(
46
+ props: CiderpressNavSocialLinksProps
47
+ ): React.ReactElement | null {
48
+ if (props.links.length === 0) {
49
+ return null
50
+ }
51
+
52
+ const safeLinks = props.links
53
+ .map((link) => ({ link, href: safeUrl(link.content) }))
54
+ .filter((entry): entry is { readonly link: CiderpressSocialLink; readonly href: string } => {
55
+ if (entry.href === null) {
56
+ return false
57
+ }
58
+ return entry.href.length > 0
59
+ })
60
+
61
+ if (safeLinks.length === 0) {
62
+ return null
63
+ }
64
+
65
+ return (
66
+ <div className="cp-nav-social">
67
+ {safeLinks.map(({ link, href }) => (
68
+ <a
69
+ key={link.content}
70
+ href={href}
71
+ target="_blank"
72
+ rel="noopener noreferrer"
73
+ className="cp-nav-social__item"
74
+ aria-label={link.label ?? link.icon}
75
+ >
76
+ <Icon
77
+ icon={ICON_MAP[link.icon.toLowerCase()] ?? 'pixelarticons:link'}
78
+ width={20}
79
+ height={20}
80
+ />
81
+ </a>
82
+ ))}
83
+ </div>
84
+ )
85
+ }
86
+
87
+ export { CiderpressNavSocialLinks as default }