@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,178 @@
1
+ /**
2
+ * Prompt — AI prompt block with action buttons and dropdown.
3
+ */
4
+
5
+ .cp-prompt {
6
+ display: flex;
7
+ flex-direction: column;
8
+ margin: var(--cp-s-16) 0;
9
+ border: 1px solid var(--cp-c-border);
10
+ border-radius: var(--rp-radius);
11
+ overflow: visible;
12
+ position: relative;
13
+ }
14
+
15
+ .cp-prompt__header {
16
+ display: flex;
17
+ align-items: center;
18
+ gap: var(--cp-s-10);
19
+ padding: var(--cp-s-10) var(--cp-s-14);
20
+ background: var(--cp-c-bg-elv);
21
+ border-radius: var(--rp-radius);
22
+ }
23
+
24
+ .cp-prompt__header:has(+ .cp-prompt__code) {
25
+ border-radius: var(--rp-radius) var(--rp-radius) 0 0;
26
+ }
27
+
28
+ .cp-prompt__icon {
29
+ display: flex;
30
+ align-items: center;
31
+ justify-content: center;
32
+ flex-shrink: 0;
33
+ width: var(--cp-size-prompt-icon);
34
+ height: var(--cp-size-prompt-icon);
35
+ color: var(--cp-c-brand-1);
36
+ }
37
+
38
+ .cp-prompt__description {
39
+ flex: 1;
40
+ min-width: 0;
41
+ font-size: var(--cp-fs-prompt-desc);
42
+ line-height: var(--cp-lh-base);
43
+ color: var(--cp-c-text-1);
44
+ }
45
+
46
+ .cp-prompt__feedback {
47
+ font-size: var(--cp-fs-prompt-feedback);
48
+ font-weight: 500;
49
+ color: var(--cp-c-brand-1);
50
+ white-space: nowrap;
51
+ }
52
+
53
+ .cp-prompt__actions {
54
+ display: inline-flex;
55
+ align-items: center;
56
+ gap: var(--cp-s-4);
57
+ flex-shrink: 0;
58
+ }
59
+
60
+ /* ── Buttons ──────────────────────────────────────────────── */
61
+
62
+ .cp-prompt__btn {
63
+ display: inline-flex;
64
+ align-items: center;
65
+ justify-content: center;
66
+ gap: var(--cp-s-6);
67
+ padding: 0;
68
+ border: 1px solid var(--cp-c-border);
69
+ border-radius: var(--rp-radius);
70
+ background: var(--cp-c-bg);
71
+ color: var(--cp-c-text-2);
72
+ font-family: var(--cp-font-family-base);
73
+ font-size: var(--cp-fs-prompt-btn);
74
+ font-weight: 500;
75
+ cursor: pointer;
76
+ transition:
77
+ color var(--cp-duration-fast),
78
+ border-color var(--cp-duration-fast),
79
+ background var(--cp-duration-fast);
80
+ }
81
+
82
+ .cp-prompt__btn:hover {
83
+ color: var(--cp-c-brand-1);
84
+ border-color: var(--cp-c-brand-1);
85
+ }
86
+
87
+ .cp-prompt__btn--primary {
88
+ padding: var(--cp-s-4) var(--cp-s-12);
89
+ height: var(--cp-size-prompt-btn);
90
+ }
91
+
92
+ .cp-prompt__btn--icon {
93
+ width: var(--cp-size-prompt-btn);
94
+ height: var(--cp-size-prompt-btn);
95
+ }
96
+
97
+ /* ── Dropdown ─────────────────────────────────────────────── */
98
+
99
+ .cp-prompt__dropdown {
100
+ position: relative;
101
+ }
102
+
103
+ .cp-prompt__menu {
104
+ position: absolute;
105
+ top: calc(100% + var(--cp-s-6));
106
+ right: 0;
107
+ z-index: var(--cp-z-dropdown);
108
+ min-width: var(--cp-size-menu-min);
109
+ padding: var(--cp-s-4);
110
+ background: var(--cp-c-bg);
111
+ border: 1px solid var(--cp-c-border);
112
+ border-radius: var(--rp-radius);
113
+ box-shadow: var(--cp-shadow-menu);
114
+ }
115
+
116
+ .cp-prompt__menu-item {
117
+ display: flex;
118
+ align-items: center;
119
+ gap: var(--cp-s-10);
120
+ width: 100%;
121
+ padding: var(--cp-s-8) var(--cp-s-10);
122
+ border: none;
123
+ border-radius: var(--rp-radius-small);
124
+ background: none;
125
+ color: var(--cp-c-text-1);
126
+ font-family: var(--cp-font-family-base);
127
+ font-size: var(--cp-fs-prompt-menu-item);
128
+ text-align: left;
129
+ cursor: pointer;
130
+ transition: background var(--cp-duration-fast);
131
+ }
132
+
133
+ .cp-prompt__menu-item:hover {
134
+ background: var(--cp-c-bg-soft);
135
+ }
136
+
137
+ .cp-prompt__menu-icon {
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: center;
141
+ flex-shrink: 0;
142
+ width: var(--cp-size-prompt-menu-icon);
143
+ height: var(--cp-size-prompt-menu-icon);
144
+ color: var(--cp-c-text-2);
145
+ }
146
+
147
+ .cp-prompt__menu-text {
148
+ display: flex;
149
+ flex-direction: column;
150
+ gap: 1px;
151
+ min-width: 0;
152
+ }
153
+
154
+ .cp-prompt__menu-label {
155
+ font-weight: 500;
156
+ line-height: var(--cp-lh-tighter);
157
+ }
158
+
159
+ .cp-prompt__menu-desc {
160
+ font-size: var(--cp-fs-prompt-menu-desc);
161
+ color: var(--cp-c-text-3);
162
+ line-height: var(--cp-lh-tighter);
163
+ }
164
+
165
+ /* ── Hidden raw & Code block ──────────────────────────────── */
166
+
167
+ .cp-prompt__raw {
168
+ display: none;
169
+ }
170
+
171
+ .cp-prompt__code {
172
+ border-top: 1px solid var(--cp-c-border);
173
+ }
174
+
175
+ .cp-prompt__code div[class*='language-'] {
176
+ margin: 0 !important;
177
+ border-radius: 0 0 var(--rp-radius) var(--rp-radius) !important;
178
+ }
@@ -0,0 +1,57 @@
1
+ import type { Story } from '@ladle/react'
2
+
3
+ import { Prompt } from './prompt.tsx'
4
+
5
+ const meta = {
6
+ title: 'Data Display / Prompt',
7
+ }
8
+
9
+ export default meta
10
+
11
+ const SAMPLE_PROMPT = `You are a senior TypeScript engineer. Review the diff below and flag
12
+ correctness issues, edge cases, and missing tests. Be terse and prescriptive
13
+ — no praise, no recap.`
14
+
15
+ /**
16
+ * Default — description, copy action, and an expandable code view of
17
+ * the raw prompt text.
18
+ *
19
+ * @returns Prompt with single Copy action
20
+ */
21
+ // oxlint-disable-next-line func-style -- Ladle stories use `export const`
22
+ export const Default: Story = () => (
23
+ <Prompt description="Senior TypeScript code review prompt">{SAMPLE_PROMPT}</Prompt>
24
+ )
25
+
26
+ /**
27
+ * Prompt with multiple actions — Copy is primary, the rest live in a
28
+ * dropdown next to it.
29
+ *
30
+ * @returns Prompt with full action set
31
+ */
32
+ // oxlint-disable-next-line func-style -- Ladle stories use `export const`
33
+ export const MultipleActions: Story = () => (
34
+ <Prompt
35
+ description="Open this prompt in your tool of choice"
36
+ actions={['copy', 'cursor', 'vscode', 'chatgpt', 'claude']}
37
+ >
38
+ {SAMPLE_PROMPT}
39
+ </Prompt>
40
+ )
41
+
42
+ /**
43
+ * Custom icon — overrides the default `pixelarticons:sparkles`.
44
+ *
45
+ * @returns Prompt with custom icon
46
+ */
47
+ // oxlint-disable-next-line func-style -- Ladle stories use `export const`
48
+ export const CustomIcon: Story = () => (
49
+ <Prompt
50
+ icon="pixelarticons:bug"
51
+ description="Reproduce and isolate a flaky test"
52
+ actions={['copy', 'claude']}
53
+ >
54
+ Identify which test is flaky in the attached file, reduce it to a minimal repro, and propose a
55
+ fix that doesn't trade speed for stability.
56
+ </Prompt>
57
+ )
@@ -0,0 +1,369 @@
1
+ import { CodeBlockRuntime } from '@rspress/core/theme'
2
+ import { match, P } from 'massaman/match'
3
+ import type React from 'react'
4
+ import { useCallback, useEffect, useRef, useState } from 'react'
5
+
6
+ import { Icon } from './icon'
7
+
8
+ export type PromptAction = 'copy' | 'cursor' | 'vscode' | 'chatgpt' | 'claude'
9
+
10
+ export interface PromptProps {
11
+ /**
12
+ * Short description of what the prompt does.
13
+ * Displayed as the visible summary.
14
+ */
15
+ readonly description?: string
16
+ /**
17
+ * Iconify icon ID rendered before the description.
18
+ * Defaults to `pixelarticons:sparkles`.
19
+ */
20
+ readonly icon?: string
21
+ /**
22
+ * Action buttons to display. Defaults to `['copy']`.
23
+ * - `copy` — copy prompt text to clipboard
24
+ * - `cursor` — copy to clipboard and launch Cursor IDE
25
+ * - `vscode` — copy to clipboard and launch VS Code
26
+ * - `chatgpt` — open ChatGPT in a new tab with prompt pre-filled
27
+ * - `claude` — open Claude in a new tab with prompt pre-filled
28
+ */
29
+ readonly actions?: readonly PromptAction[]
30
+ /**
31
+ * The raw prompt text. Rendered inside an expandable code block.
32
+ */
33
+ readonly children: React.ReactNode
34
+ }
35
+
36
+ /**
37
+ * AI prompt block with description, action buttons, and an expandable
38
+ * code view for the full prompt text.
39
+ *
40
+ * @param props - Prompt configuration with description, icon, and actions
41
+ * @returns React element with prompt card
42
+ */
43
+ export function Prompt({
44
+ description,
45
+ icon = 'pixelarticons:sparkles',
46
+ actions = ['copy'],
47
+ children,
48
+ }: PromptProps): React.ReactElement {
49
+ const [expanded, setExpanded] = useState(false)
50
+ const [feedback, setFeedback] = useState<string | null>(null)
51
+ const contentRef = useRef<HTMLDivElement>(null)
52
+
53
+ const getRawText = useCallback(
54
+ (): string =>
55
+ match(contentRef.current)
56
+ .with(null, () => '')
57
+ .otherwise((el) => el.textContent ?? ''),
58
+ []
59
+ )
60
+
61
+ const feedbackTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
62
+
63
+ useEffect(
64
+ () => () => {
65
+ if (feedbackTimerRef.current !== null) {
66
+ clearTimeout(feedbackTimerRef.current)
67
+ }
68
+ },
69
+ []
70
+ )
71
+
72
+ const showFeedback = useCallback((msg: string) => {
73
+ if (feedbackTimerRef.current !== null) {
74
+ clearTimeout(feedbackTimerRef.current)
75
+ }
76
+ setFeedback(msg)
77
+ feedbackTimerRef.current = setTimeout(() => setFeedback(null), 2000)
78
+ }, [])
79
+
80
+ const handleAction = useCallback(
81
+ (action: PromptAction) => {
82
+ const text = getRawText()
83
+
84
+ match(action)
85
+ .with('copy', () => {
86
+ navigator.clipboard
87
+ .writeText(text)
88
+ .then(() => {
89
+ showFeedback('Copied!')
90
+ return null
91
+ })
92
+ .catch(() => {
93
+ showFeedback('Copy failed')
94
+ })
95
+ })
96
+ .with('cursor', () => {
97
+ // Copy prompt to clipboard then open Cursor so the user can paste into composer
98
+ navigator.clipboard
99
+ .writeText(text)
100
+ .then(() => {
101
+ showFeedback('Copied — opening Cursor…')
102
+ globalThis.open('cursor://', '_self')
103
+ return null
104
+ })
105
+ .catch(() => {
106
+ showFeedback('Copy failed')
107
+ })
108
+ })
109
+ .with('vscode', () => {
110
+ navigator.clipboard
111
+ .writeText(text)
112
+ .then(() => {
113
+ showFeedback('Copied — opening VS Code…')
114
+ globalThis.open('vscode://', '_self')
115
+ return null
116
+ })
117
+ .catch(() => {
118
+ showFeedback('Copy failed')
119
+ })
120
+ })
121
+ .with('chatgpt', () => {
122
+ const encoded = encodeURIComponent(text)
123
+ globalThis.open(`https://chatgpt.com/?hints=search&prompt=${encoded}`, '_blank')
124
+ showFeedback('Opened ChatGPT')
125
+ })
126
+ .with('claude', () => {
127
+ const encoded = encodeURIComponent(text)
128
+ globalThis.open(`https://claude.ai/new?q=${encoded}`, '_blank')
129
+ showFeedback('Opened Claude')
130
+ })
131
+ .exhaustive()
132
+ },
133
+ [getRawText, showFeedback]
134
+ )
135
+
136
+ const toggleExpanded = useCallback(() => {
137
+ setExpanded((prev) => !prev)
138
+ }, [])
139
+
140
+ const descEl = match(description)
141
+ .with(P.nonNullable, (d) => <span className="cp-prompt__description">{d}</span>)
142
+ .otherwise(() => null)
143
+
144
+ const feedbackEl = match(feedback)
145
+ .with(P.nonNullable, (msg) => <span className="cp-prompt__feedback">{msg}</span>)
146
+ .otherwise(() => null)
147
+
148
+ const primaryAction = actions[0] ?? 'copy'
149
+ const dropdownActions = actions.slice(1)
150
+
151
+ return (
152
+ <div className="cp-prompt">
153
+ <div className="cp-prompt__header">
154
+ <span className="cp-prompt__icon">
155
+ <Icon icon={icon} />
156
+ </span>
157
+ {descEl}
158
+ {feedbackEl}
159
+ <span className="cp-prompt__actions">
160
+ <PrimaryButton action={primaryAction} onPress={handleAction} />
161
+ {match(dropdownActions.length > 0)
162
+ .with(true, () => <ActionDropdown actions={dropdownActions} onPress={handleAction} />)
163
+ .otherwise(() => null)}
164
+ <button
165
+ type="button"
166
+ className="cp-prompt__btn cp-prompt__btn--icon"
167
+ onClick={toggleExpanded}
168
+ title={match(expanded)
169
+ .with(true, () => 'Hide prompt')
170
+ .otherwise(() => 'Show prompt')}
171
+ >
172
+ <Icon
173
+ icon={match(expanded)
174
+ .with(true, () => 'pixelarticons:collapse')
175
+ .otherwise(() => 'pixelarticons:expand')}
176
+ />
177
+ </button>
178
+ </span>
179
+ </div>
180
+ <div ref={contentRef} className="cp-prompt__raw" hidden>
181
+ {children}
182
+ </div>
183
+ {match(expanded)
184
+ .with(true, () => (
185
+ <div className="cp-prompt__code">
186
+ <CodeBlockRuntime lang="markdown" code={getRawText()} />
187
+ </div>
188
+ ))
189
+ .otherwise(() => null)}
190
+ </div>
191
+ )
192
+ }
193
+
194
+ /**
195
+ * Action metadata for display.
196
+ *
197
+ * @private
198
+ */
199
+ interface ActionMeta {
200
+ readonly label: string
201
+ readonly icon: React.ReactNode
202
+ readonly description: string
203
+ }
204
+
205
+ /**
206
+ * Resolve display metadata for a prompt action.
207
+ *
208
+ * @private
209
+ * @param action - The prompt action type
210
+ * @returns Label, icon, and description for the action
211
+ */
212
+ function actionMeta(action: PromptAction): ActionMeta {
213
+ return match(action)
214
+ .with('copy', () => ({
215
+ label: 'Copy',
216
+ icon: <Icon icon="pixelarticons:clipboard" />,
217
+ description: 'Copy to clipboard',
218
+ }))
219
+ .with('cursor', () => ({
220
+ label: 'Cursor',
221
+ icon: <Icon icon="simple-icons:cursor" />,
222
+ description: 'Copy and open Cursor IDE',
223
+ }))
224
+ .with('vscode', () => ({
225
+ label: 'VS Code',
226
+ icon: <Icon icon="mdi:microsoft-visual-studio-code" />,
227
+ description: 'Copy and open VS Code',
228
+ }))
229
+ .with('chatgpt', () => ({
230
+ label: 'ChatGPT',
231
+ icon: <Icon icon="simple-icons:openai" />,
232
+ description: 'Open in ChatGPT with prompt',
233
+ }))
234
+ .with('claude', () => ({
235
+ label: 'Claude',
236
+ icon: <Icon icon="simple-icons:claude" />,
237
+ description: 'Open in Claude with prompt',
238
+ }))
239
+ .exhaustive()
240
+ }
241
+
242
+ interface PrimaryButtonProps {
243
+ readonly action: PromptAction
244
+ readonly onPress: (action: PromptAction) => void
245
+ }
246
+
247
+ /**
248
+ * Full-width primary action button.
249
+ *
250
+ * @private
251
+ * @param props - Action and press handler
252
+ * @returns Button element
253
+ */
254
+ function PrimaryButton({ action, onPress }: PrimaryButtonProps): React.ReactElement {
255
+ const meta = actionMeta(action)
256
+ const handleClick = useCallback(() => {
257
+ onPress(action)
258
+ }, [action, onPress])
259
+
260
+ return (
261
+ <button type="button" className="cp-prompt__btn cp-prompt__btn--primary" onClick={handleClick}>
262
+ {meta.icon}
263
+ <span>{meta.label}</span>
264
+ </button>
265
+ )
266
+ }
267
+
268
+ interface ActionDropdownProps {
269
+ readonly actions: readonly PromptAction[]
270
+ readonly onPress: (action: PromptAction) => void
271
+ }
272
+
273
+ /**
274
+ * Dropdown menu for additional prompt actions.
275
+ *
276
+ * @private
277
+ * @param props - Actions list and press handler
278
+ * @returns Dropdown element with toggle and menu
279
+ */
280
+ function ActionDropdown({ actions, onPress }: ActionDropdownProps): React.ReactElement {
281
+ const [open, setOpen] = useState(false)
282
+ const ref = useRef<HTMLDivElement>(null)
283
+
284
+ useEffect(() => {
285
+ if (!open) {
286
+ return
287
+ }
288
+
289
+ function handler(e: MouseEvent): void {
290
+ if (ref.current && !ref.current.contains(e.target as Node)) {
291
+ setOpen(false)
292
+ }
293
+ }
294
+
295
+ document.addEventListener('mousedown', handler)
296
+ return () => document.removeEventListener('mousedown', handler)
297
+ }, [open])
298
+
299
+ const handleToggle = useCallback(() => {
300
+ setOpen((prev) => !prev)
301
+ }, [])
302
+
303
+ return (
304
+ <div ref={ref} className="cp-prompt__dropdown">
305
+ <button
306
+ type="button"
307
+ className="cp-prompt__btn cp-prompt__btn--icon"
308
+ onClick={handleToggle}
309
+ aria-expanded={open}
310
+ aria-haspopup="menu"
311
+ >
312
+ <Icon
313
+ icon={match(open)
314
+ .with(true, () => 'pixelarticons:chevron-up')
315
+ .otherwise(() => 'pixelarticons:chevron-down')}
316
+ />
317
+ </button>
318
+ {match(open)
319
+ .with(true, () => (
320
+ <div className="cp-prompt__menu" role="menu">
321
+ {actions.map((action) => {
322
+ const meta = actionMeta(action)
323
+ return (
324
+ <DropdownItem
325
+ key={action}
326
+ action={action}
327
+ meta={meta}
328
+ onPress={(a) => {
329
+ onPress(a)
330
+ setOpen(false)
331
+ }}
332
+ />
333
+ )
334
+ })}
335
+ </div>
336
+ ))
337
+ .otherwise(() => null)}
338
+ </div>
339
+ )
340
+ }
341
+
342
+ interface DropdownItemProps {
343
+ readonly action: PromptAction
344
+ readonly meta: ActionMeta
345
+ readonly onPress: (action: PromptAction) => void
346
+ }
347
+
348
+ /**
349
+ * A single item in the action dropdown menu.
350
+ *
351
+ * @private
352
+ * @param props - Action, display metadata, and press handler
353
+ * @returns Menu item button
354
+ */
355
+ function DropdownItem({ action, meta, onPress }: DropdownItemProps): React.ReactElement {
356
+ const handleClick = useCallback(() => {
357
+ onPress(action)
358
+ }, [action, onPress])
359
+
360
+ return (
361
+ <button type="button" className="cp-prompt__menu-item" role="menuitem" onClick={handleClick}>
362
+ <span className="cp-prompt__menu-icon">{meta.icon}</span>
363
+ <span className="cp-prompt__menu-text">
364
+ <span className="cp-prompt__menu-label">{meta.label}</span>
365
+ <span className="cp-prompt__menu-desc">{meta.description}</span>
366
+ </span>
367
+ </button>
368
+ )
369
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Resolved icon with id and color.
3
+ */
4
+ export interface ResolvedCardIcon {
5
+ readonly id: string
6
+ readonly color: string
7
+ }
8
+
9
+ /**
10
+ * Resolve a unified icon config into id + color.
11
+ *
12
+ * String icons get the default `'purple'` color. Object icons pass through.
13
+ * Returns `undefined` for undefined input.
14
+ *
15
+ * @param icon - Icon config (string, object, or undefined)
16
+ * @returns Resolved icon or undefined
17
+ */
18
+ export function resolveCardIcon(
19
+ icon: string | { readonly id: string; readonly color: string } | undefined
20
+ ): ResolvedCardIcon | undefined {
21
+ if (icon === undefined) {
22
+ return undefined
23
+ }
24
+ if (typeof icon === 'string') {
25
+ return { id: icon, color: 'purple' }
26
+ }
27
+ return icon
28
+ }
@@ -0,0 +1,51 @@
1
+ import type { Story } from '@ladle/react'
2
+
3
+ import { SectionCard } from './section-card.tsx'
4
+
5
+ const meta = {
6
+ title: 'Surface / Section Card',
7
+ }
8
+
9
+ export default meta
10
+
11
+ /**
12
+ * Default — icon, title, description. Renders as a clickable `<a>`
13
+ * routed through the stubbed Rspress `<Link>`.
14
+ *
15
+ * @returns Linked section card
16
+ */
17
+ // oxlint-disable-next-line func-style -- Ladle stories use `export const`
18
+ export const Default: Story = () => (
19
+ <SectionCard
20
+ href="/guides/getting-started"
21
+ title="Getting started"
22
+ description="Spin up a Ciderpress site against your existing markdown in under five minutes."
23
+ icon="pixelarticons:flag"
24
+ />
25
+ )
26
+
27
+ /**
28
+ * No description — minimum content.
29
+ *
30
+ * @returns Section card with title only
31
+ */
32
+ // oxlint-disable-next-line func-style -- Ladle stories use `export const`
33
+ export const TitleOnly: Story = () => (
34
+ <SectionCard href="/concepts" title="Concepts" icon="pixelarticons:bulb" />
35
+ )
36
+
37
+ /**
38
+ * Custom icon + colour — pass `{ id, color }` to override the auto
39
+ * resolution from `resolveCardIcon`.
40
+ *
41
+ * @returns Card with explicit blue icon
42
+ */
43
+ // oxlint-disable-next-line func-style -- Ladle stories use `export const`
44
+ export const CustomIcon: Story = () => (
45
+ <SectionCard
46
+ href="/references/api"
47
+ title="API reference"
48
+ description="Every public export with full prop tables and example snippets."
49
+ icon={{ id: 'pixelarticons:code', color: 'blue' }}
50
+ />
51
+ )