@speakeasy-api/moonshine 2.0.0-alpha.1 → 2.0.0-alpha.2

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 (330) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +49 -23
  3. package/package.json +33 -50
  4. package/scripts/generate-utility-docs.js +324 -0
  5. package/src/assets/icons/external/github.svg +3 -0
  6. package/src/assets/icons/external/maven.svg +152 -0
  7. package/src/assets/icons/external/npm.svg +4 -0
  8. package/src/assets/icons/external/nuget.svg +5 -0
  9. package/src/assets/icons/external/packagist.svg +1 -0
  10. package/src/assets/icons/external/pypi.svg +182 -0
  11. package/src/assets/icons/external/rubygems.svg +14 -0
  12. package/src/assets/icons/external/terraform.svg +1 -0
  13. package/src/assets/icons/languages/csharp.svg +1 -0
  14. package/src/assets/icons/languages/go.svg +1 -0
  15. package/src/assets/icons/languages/java.svg +1 -0
  16. package/src/assets/icons/languages/json.svg +2 -0
  17. package/src/assets/icons/languages/php.svg +1 -0
  18. package/src/assets/icons/languages/postman.svg +3 -0
  19. package/src/assets/icons/languages/python.svg +1 -0
  20. package/src/assets/icons/languages/ruby.svg +1 -0
  21. package/src/assets/icons/languages/swift.svg +1 -0
  22. package/src/assets/icons/languages/terraform.svg +1 -0
  23. package/src/assets/icons/languages/typescript.svg +1 -0
  24. package/src/assets/icons/languages/unity.svg +1 -0
  25. package/src/base.css +12 -12
  26. package/src/components/AIChat/AIChatContainer.tsx +71 -0
  27. package/src/components/AIChat/AIChatMessage.tsx +135 -0
  28. package/src/components/AIChat/AIChatMessageComposer.tsx +175 -0
  29. package/src/components/AIChat/AIChatMessageList.tsx +34 -0
  30. package/src/components/AIChat/AIChatModelSelector.tsx +159 -0
  31. package/src/components/AIChat/componentsTypes.ts +36 -0
  32. package/src/components/AIChat/context.ts +15 -0
  33. package/src/components/AIChat/index.ts +12 -0
  34. package/src/components/AIChat/parts/AIChatMessageFilePart.tsx +129 -0
  35. package/src/components/AIChat/parts/AIChatMessageReasoningPart.tsx +23 -0
  36. package/src/components/AIChat/parts/AIChatMessageSourcePart.tsx +58 -0
  37. package/src/components/AIChat/parts/AIChatMessageTextPart.tsx +33 -0
  38. package/src/components/AIChat/parts/AIChatMessageToolInvocationPart.tsx +53 -0
  39. package/src/components/AIChat/parts/AIChatMessageToolPart.tsx +395 -0
  40. package/src/components/AIChat/parts/AIChatMessageToolResultPart.tsx +46 -0
  41. package/src/components/AIChat/toolCallApproval.ts +61 -0
  42. package/src/components/AIChat/types.ts +97 -0
  43. package/src/components/ActionBar/index.tsx +184 -0
  44. package/src/components/Alert/index.tsx +118 -0
  45. package/src/components/Alert/types.ts +12 -0
  46. package/src/components/AppLayout/context.tsx +31 -0
  47. package/src/components/AppLayout/index.tsx +550 -0
  48. package/src/components/AppLayout/provider.tsx +40 -0
  49. package/src/components/AppLayout/useAppLayoutKeys.ts +26 -0
  50. package/src/components/Badge/index.tsx +227 -0
  51. package/src/components/Button/index.tsx +531 -0
  52. package/src/components/Card/index.tsx +193 -0
  53. package/src/components/CodeEditorLayout/index.tsx +394 -0
  54. package/src/components/CodeEditorLayout/styles.module.css +8 -0
  55. package/src/components/CodeHighlight/Pre.tsx +63 -0
  56. package/src/components/CodePlayground/index.tsx +411 -0
  57. package/src/components/CodeSnippet/codeSnippet.css +97 -0
  58. package/src/components/CodeSnippet/index.tsx +224 -0
  59. package/src/components/Combobox/index.tsx +193 -0
  60. package/src/components/Command/index.tsx +152 -0
  61. package/src/components/Container/index.tsx +31 -0
  62. package/src/components/ContextDropdown/index.tsx +150 -0
  63. package/src/components/Dialog/index.tsx +123 -0
  64. package/src/components/DragNDrop/DragNDropArea.tsx +30 -0
  65. package/src/components/DragNDrop/DragOverlay.tsx +4 -0
  66. package/src/components/DragNDrop/Draggable.tsx +97 -0
  67. package/src/components/DragNDrop/Droppable.tsx +51 -0
  68. package/src/components/Dropdown/index.tsx +201 -0
  69. package/src/components/ExternalPill/index.tsx +58 -0
  70. package/src/components/Facepile/index.tsx +309 -0
  71. package/src/components/GradientCircle/gradientCircle.css +34 -0
  72. package/src/components/GradientCircle/index.tsx +143 -0
  73. package/src/components/Grid/index.tsx +150 -0
  74. package/src/components/Heading/index.tsx +54 -0
  75. package/src/components/HighlightedText/index.tsx +152 -0
  76. package/src/components/Icon/customIcons/createCustomLucideIcon.ts +25 -0
  77. package/src/components/Icon/customIcons/gems.ts +26 -0
  78. package/{dist/go-CiWl_aXI.mjs → src/components/Icon/customIcons/go.ts} +21 -19
  79. package/src/components/Icon/customIcons/index.ts +11 -0
  80. package/{dist/maven-DhmnGXoB.mjs → src/components/Icon/customIcons/maven.ts} +17 -15
  81. package/src/components/Icon/customIcons/npm.ts +19 -0
  82. package/{dist/nuget-5a2icRS2.mjs → src/components/Icon/customIcons/nuget.ts} +17 -15
  83. package/src/components/Icon/customIcons/packagist.ts +124 -0
  84. package/{dist/pypi-DsuRYjdK.mjs → src/components/Icon/customIcons/pypi.ts} +16 -14
  85. package/src/components/Icon/index.tsx +83 -0
  86. package/src/components/Icon/isIconName.ts +10 -0
  87. package/src/components/Icon/names.ts +14 -0
  88. package/src/components/IconButton/index.tsx +51 -0
  89. package/src/components/Input/index.tsx +98 -0
  90. package/src/components/KeyHint/index.tsx +118 -0
  91. package/src/components/LanguageIndicator/index.tsx +68 -0
  92. package/src/components/Link/index.tsx +153 -0
  93. package/src/components/LoggedInUserMenu/index.tsx +116 -0
  94. package/src/components/Logo/Animated.tsx +191 -0
  95. package/src/components/Logo/index.tsx +17 -0
  96. package/src/components/Logo/speakeasy-logo.riv +0 -0
  97. package/src/components/Logo/svgs/index.tsx +126 -0
  98. package/src/components/Modal/index.tsx +104 -0
  99. package/src/components/PageHeader/index.tsx +227 -0
  100. package/src/components/PageHeader/styles.module.css +27 -0
  101. package/src/components/Popover/index.tsx +35 -0
  102. package/src/components/PromptInput/index.tsx +372 -0
  103. package/src/components/PullRequestLink/index.tsx +64 -0
  104. package/src/components/ResizablePanel/index.tsx +119 -0
  105. package/src/components/Score/index.module.css +32 -0
  106. package/src/components/Score/index.tsx +268 -0
  107. package/src/components/ScrollArea/index.tsx +48 -0
  108. package/src/components/SegmentedButton/index.module.css +19 -0
  109. package/src/components/SegmentedButton/index.tsx +101 -0
  110. package/src/components/Select/index.tsx +159 -0
  111. package/src/components/Separator/index.tsx +23 -0
  112. package/src/components/Skeleton/index.tsx +61 -0
  113. package/src/components/Skeleton/skeleton.css +52 -0
  114. package/src/components/Stack/index.tsx +137 -0
  115. package/src/components/Subnav/index.tsx +315 -0
  116. package/src/components/Switch/index.tsx +29 -0
  117. package/src/components/Table/context/context.tsx +19 -0
  118. package/src/components/Table/context/tableProvider.tsx +39 -0
  119. package/src/components/Table/index.tsx +707 -0
  120. package/src/components/Table/styles.module.css +25 -0
  121. package/src/components/Tabs/index.tsx +87 -0
  122. package/src/components/TargetLanguageIcon/index.tsx +84 -0
  123. package/src/components/Text/index.tsx +59 -0
  124. package/src/components/ThemeSwitcher/index.tsx +118 -0
  125. package/src/components/Timeline/index.tsx +290 -0
  126. package/src/components/Tooltip/index.tsx +41 -0
  127. package/src/components/UserAvatar/index.tsx +87 -0
  128. package/src/components/UserAvatar/sizeMap.ts +12 -0
  129. package/src/components/Wizard/index.tsx +208 -0
  130. package/src/components/Wizard/types.ts +17 -0
  131. package/src/components/WorkspaceSelector/CreateOrg.tsx +95 -0
  132. package/src/components/WorkspaceSelector/CreateWorkspace.tsx +196 -0
  133. package/src/components/WorkspaceSelector/OrgList.tsx +115 -0
  134. package/src/components/WorkspaceSelector/OrgSelector.tsx +207 -0
  135. package/src/components/WorkspaceSelector/RecentWorkspaces.tsx +83 -0
  136. package/src/components/WorkspaceSelector/ScrollingList.tsx +84 -0
  137. package/src/components/WorkspaceSelector/SearchBox.tsx +40 -0
  138. package/src/components/WorkspaceSelector/WorkspaceItem.tsx +37 -0
  139. package/src/components/WorkspaceSelector/WorkspaceList.tsx +107 -0
  140. package/src/components/WorkspaceSelector/index.tsx +400 -0
  141. package/src/components/WorkspaceSelector/styles.css +74 -0
  142. package/src/components/__beta__/CLIWizard/index.tsx +357 -0
  143. package/src/components/__beta__/CLIWizard/terminal-command.tsx +108 -0
  144. package/src/components/__beta__/CLIWizard/terminal.tsx +83 -0
  145. package/src/components/__beta__/README.md +3 -0
  146. package/src/components/index.mdx +38 -0
  147. package/src/context/ConfigContext.tsx +43 -0
  148. package/src/context/ModalContext.tsx +118 -0
  149. package/src/context/theme.ts +1 -0
  150. package/src/hooks/useAppLayout.ts +10 -0
  151. package/src/hooks/useConfig.ts +10 -0
  152. package/src/hooks/useIsMounted.ts +13 -0
  153. package/src/hooks/useModal.tsx +10 -0
  154. package/src/hooks/useTailwindBreakpoint.ts +47 -0
  155. package/src/hooks/useTheme.ts +13 -0
  156. package/src/index.ts +234 -0
  157. package/src/lib/assert.ts +9 -0
  158. package/src/lib/codeUtils.ts +177 -0
  159. package/src/lib/debounce.ts +9 -0
  160. package/src/lib/responsiveMappers.ts +69 -0
  161. package/src/lib/responsiveUtils.ts +23 -0
  162. package/src/lib/storybookUtils.tsx +26 -0
  163. package/src/lib/typeUtils.ts +109 -0
  164. package/src/lib/utils.ts +85 -0
  165. package/src/styles/codeSyntax.css +59 -0
  166. package/src/styles/globals.css +51 -0
  167. package/src/types.ts +200 -0
  168. package/src/utilities.css +347 -6
  169. package/src/vite-env.d.ts +6 -0
  170. package/types/utilities.d.ts +43 -1
  171. package/dist/components/AIChat/AIChatContainer.d.ts +0 -25
  172. package/dist/components/AIChat/AIChatMessage.d.ts +0 -19
  173. package/dist/components/AIChat/AIChatMessageComposer.d.ts +0 -22
  174. package/dist/components/AIChat/AIChatMessageList.d.ts +0 -6
  175. package/dist/components/AIChat/AIChatModelSelector.d.ts +0 -14
  176. package/dist/components/AIChat/componentsTypes.d.ts +0 -11
  177. package/dist/components/AIChat/context.d.ts +0 -3
  178. package/dist/components/AIChat/index.d.ts +0 -12
  179. package/dist/components/AIChat/parts/AIChatMessageFilePart.d.ts +0 -7
  180. package/dist/components/AIChat/parts/AIChatMessageReasoningPart.d.ts +0 -5
  181. package/dist/components/AIChat/parts/AIChatMessageSourcePart.d.ts +0 -9
  182. package/dist/components/AIChat/parts/AIChatMessageTextPart.d.ts +0 -5
  183. package/dist/components/AIChat/parts/AIChatMessageToolInvocationPart.d.ts +0 -6
  184. package/dist/components/AIChat/parts/AIChatMessageToolPart.d.ts +0 -33
  185. package/dist/components/AIChat/parts/AIChatMessageToolResultPart.d.ts +0 -5
  186. package/dist/components/AIChat/toolCallApproval.d.ts +0 -15
  187. package/dist/components/AIChat/types.d.ts +0 -78
  188. package/dist/components/ActionBar/index.d.ts +0 -36
  189. package/dist/components/Alert/index.d.ts +0 -18
  190. package/dist/components/Alert/types.d.ts +0 -4
  191. package/dist/components/Badge/index.d.ts +0 -10
  192. package/dist/components/Button/index.d.ts +0 -11
  193. package/dist/components/Card/index.d.ts +0 -47
  194. package/dist/components/CodeEditorLayout/index.d.ts +0 -101
  195. package/dist/components/CodePlayground/index.d.ts +0 -108
  196. package/dist/components/CodePlayground/lineNumbers.d.ts +0 -2
  197. package/dist/components/CodePlayground/tokenTransitions.d.ts +0 -2
  198. package/dist/components/CodePlayground/wordWrap.d.ts +0 -2
  199. package/dist/components/CodeSnippet/index.d.ts +0 -50
  200. package/dist/components/Combobox/index.d.ts +0 -35
  201. package/dist/components/Command/index.d.ts +0 -80
  202. package/dist/components/Container/index.d.ts +0 -9
  203. package/dist/components/ContextDropdown/index.d.ts +0 -7
  204. package/dist/components/ContextDropdown/provider.d.ts +0 -22
  205. package/dist/components/ContextDropdown/useModal.d.ts +0 -11
  206. package/dist/components/Dialog/index.d.ts +0 -19
  207. package/dist/components/DragNDrop/DragNDropArea.d.ts +0 -8
  208. package/dist/components/DragNDrop/DragOverlay.d.ts +0 -1
  209. package/dist/components/DragNDrop/Draggable.d.ts +0 -29
  210. package/dist/components/DragNDrop/Droppable.d.ts +0 -28
  211. package/dist/components/Dropdown/index.d.ts +0 -27
  212. package/dist/components/ExternalPill/index.d.ts +0 -12
  213. package/dist/components/Facepile/index.d.ts +0 -16
  214. package/dist/components/GradientCircle/index.d.ts +0 -10
  215. package/dist/components/Grid/index.d.ts +0 -80
  216. package/dist/components/Heading/index.d.ts +0 -12
  217. package/dist/components/HighlightedText/index.d.ts +0 -19
  218. package/dist/components/Icon/customIcons/createCustomLucideIcon.d.ts +0 -3
  219. package/dist/components/Icon/customIcons/gems.d.ts +0 -2
  220. package/dist/components/Icon/customIcons/go.d.ts +0 -2
  221. package/dist/components/Icon/customIcons/index.d.ts +0 -10
  222. package/dist/components/Icon/customIcons/maven.d.ts +0 -2
  223. package/dist/components/Icon/customIcons/npm.d.ts +0 -2
  224. package/dist/components/Icon/customIcons/nuget.d.ts +0 -2
  225. package/dist/components/Icon/customIcons/packagist.d.ts +0 -2
  226. package/dist/components/Icon/customIcons/pypi.d.ts +0 -2
  227. package/dist/components/Icon/index.d.ts +0 -10
  228. package/dist/components/Icon/isIconName.d.ts +0 -2
  229. package/dist/components/Icon/names.d.ts +0 -6
  230. package/dist/components/Input/index.d.ts +0 -8
  231. package/dist/components/KeyHint/index.d.ts +0 -16
  232. package/dist/components/LanguageIndicator/index.d.ts +0 -7
  233. package/dist/components/Link/index.d.ts +0 -19
  234. package/dist/components/LoggedInUserMenu/index.d.ts +0 -17
  235. package/dist/components/Logo/Animated.d.ts +0 -7
  236. package/dist/components/Logo/index.d.ts +0 -7
  237. package/dist/components/Logo/svgs/index.d.ts +0 -6
  238. package/dist/components/Navbar/Slim.d.ts +0 -33
  239. package/dist/components/Navbar/index.d.ts +0 -15
  240. package/dist/components/PageHeader/index.d.ts +0 -45
  241. package/dist/components/Popover/index.d.ts +0 -8
  242. package/dist/components/PromptInput/index.d.ts +0 -55
  243. package/dist/components/PullRequestLink/index.d.ts +0 -10
  244. package/dist/components/ResizablePanel/index.d.ts +0 -26
  245. package/dist/components/Score/index.d.ts +0 -37
  246. package/dist/components/ScrollArea/index.d.ts +0 -5
  247. package/dist/components/Select/index.d.ts +0 -13
  248. package/dist/components/Separator/index.d.ts +0 -6
  249. package/dist/components/Skeleton/index.d.ts +0 -27
  250. package/dist/components/Stack/index.d.ts +0 -33
  251. package/dist/components/Subnav/index.d.ts +0 -12
  252. package/dist/components/Switch/index.d.ts +0 -4
  253. package/dist/components/Table/context/context.d.ts +0 -8
  254. package/dist/components/Table/context/tableProvider.d.ts +0 -6
  255. package/dist/components/Table/index.d.ts +0 -94
  256. package/dist/components/Tabs/index.d.ts +0 -21
  257. package/dist/components/TargetLanguageIcon/index.d.ts +0 -7
  258. package/dist/components/Text/index.d.ts +0 -19
  259. package/dist/components/ThemeSwitcher/index.d.ts +0 -5
  260. package/dist/components/Tooltip/index.d.ts +0 -8
  261. package/dist/components/UserAvatar/index.d.ts +0 -9
  262. package/dist/components/UserAvatar/sizeMap.d.ts +0 -3
  263. package/dist/components/Wizard/index.d.ts +0 -19
  264. package/dist/components/Wizard/types.d.ts +0 -15
  265. package/dist/components/WorkspaceSelector/CreateOrg.d.ts +0 -6
  266. package/dist/components/WorkspaceSelector/CreateWorkspace.d.ts +0 -17
  267. package/dist/components/WorkspaceSelector/OrgList.d.ts +0 -11
  268. package/dist/components/WorkspaceSelector/OrgSelector.d.ts +0 -13
  269. package/dist/components/WorkspaceSelector/RecentWorkspaces.d.ts +0 -11
  270. package/dist/components/WorkspaceSelector/ScrollingList.d.ts +0 -21
  271. package/dist/components/WorkspaceSelector/SearchBox.d.ts +0 -9
  272. package/dist/components/WorkspaceSelector/WorkspaceItem.d.ts +0 -9
  273. package/dist/components/WorkspaceSelector/WorkspaceList.d.ts +0 -10
  274. package/dist/components/WorkspaceSelector/index.d.ts +0 -34
  275. package/dist/components/__beta__/CLIWizard/index.d.ts +0 -21
  276. package/dist/components/__beta__/CLIWizard/terminal-command.d.ts +0 -19
  277. package/dist/components/__beta__/CLIWizard/terminal.d.ts +0 -26
  278. package/dist/context/ConfigContext.d.ts +0 -18
  279. package/dist/context/theme.d.ts +0 -1
  280. package/dist/createCustomLucideIcon-YlrRX5h9.mjs +0 -19
  281. package/dist/createCustomLucideIcon-YlrRX5h9.mjs.map +0 -1
  282. package/dist/gems-BcsO9cXq.mjs +0 -24
  283. package/dist/gems-BcsO9cXq.mjs.map +0 -1
  284. package/dist/github-kgjMtfE7.mjs +0 -11
  285. package/dist/github-kgjMtfE7.mjs.map +0 -1
  286. package/dist/go-CiWl_aXI.mjs.map +0 -1
  287. package/dist/hooks/useConfig.d.ts +0 -2
  288. package/dist/hooks/useIsMounted.d.ts +0 -1
  289. package/dist/hooks/useTailwindBreakpoint.d.ts +0 -3
  290. package/dist/hooks/useTheme.d.ts +0 -6
  291. package/dist/index-COXZ9O-g.mjs +0 -50882
  292. package/dist/index-COXZ9O-g.mjs.map +0 -1
  293. package/dist/index.d.ts +0 -73
  294. package/dist/lib/assert.d.ts +0 -2
  295. package/dist/lib/codeUtils.d.ts +0 -35
  296. package/dist/lib/debounce.d.ts +0 -1
  297. package/dist/lib/responsiveMappers.d.ts +0 -10
  298. package/dist/lib/responsiveUtils.d.ts +0 -3
  299. package/dist/lib/storybookUtils.d.ts +0 -5
  300. package/dist/lib/typeUtils.d.ts +0 -24
  301. package/dist/lib/utils.d.ts +0 -23
  302. package/dist/lucide-icons-BDw0imyx.mjs +0 -28054
  303. package/dist/lucide-icons-BDw0imyx.mjs.map +0 -1
  304. package/dist/maven-DhmnGXoB.mjs.map +0 -1
  305. package/dist/maven-W_nkSDNW.mjs +0 -107
  306. package/dist/maven-W_nkSDNW.mjs.map +0 -1
  307. package/dist/moonshine.es.js +0 -114
  308. package/dist/moonshine.es.js.map +0 -1
  309. package/dist/npm-BWTcVvFH.mjs +0 -11
  310. package/dist/npm-BWTcVvFH.mjs.map +0 -1
  311. package/dist/npm-CvQ4GKW4.mjs +0 -17
  312. package/dist/npm-CvQ4GKW4.mjs.map +0 -1
  313. package/dist/nuget-5a2icRS2.mjs.map +0 -1
  314. package/dist/nuget-CV5HU1JR.mjs +0 -11
  315. package/dist/nuget-CV5HU1JR.mjs.map +0 -1
  316. package/dist/packagist-CET6q9hi.mjs +0 -118
  317. package/dist/packagist-CET6q9hi.mjs.map +0 -1
  318. package/dist/packagist-D01fn9N_.mjs +0 -11
  319. package/dist/packagist-D01fn9N_.mjs.map +0 -1
  320. package/dist/pypi-DLh6kIJe.mjs +0 -11
  321. package/dist/pypi-DLh6kIJe.mjs.map +0 -1
  322. package/dist/pypi-DsuRYjdK.mjs.map +0 -1
  323. package/dist/rubygems-DeiNjcDV.mjs +0 -11
  324. package/dist/rubygems-DeiNjcDV.mjs.map +0 -1
  325. package/dist/speakeasy-logo-ByBTXLWb.mjs +0 -5
  326. package/dist/speakeasy-logo-ByBTXLWb.mjs.map +0 -1
  327. package/dist/style.css +0 -1
  328. package/dist/terraform-C4aktQ0o.mjs +0 -11
  329. package/dist/terraform-C4aktQ0o.mjs.map +0 -1
  330. package/dist/types.d.ts +0 -80
@@ -0,0 +1,64 @@
1
+ // TODO: https://linear.app/speakeasy/issue/SXF-174/pull-request-link-component
2
+ import { assertNever } from '../../lib/assert'
3
+ import { cn } from '../../lib/utils'
4
+ import { GitPullRequest, GitPullRequestClosed, Merge } from 'lucide-react'
5
+
6
+ type Status = 'open' | 'closed' | 'merged'
7
+
8
+ export interface PullRequestLinkProps {
9
+ href: string
10
+ prNumber?: number
11
+ status?: Status
12
+ target?: '_blank' | '_self' | '_parent' | '_top'
13
+ className?: string
14
+ }
15
+
16
+ export function PullRequestLink({
17
+ href,
18
+ prNumber,
19
+ status = 'open',
20
+ target = '_blank',
21
+ className,
22
+ }: PullRequestLinkProps) {
23
+ // If prNumber is not provided, try to extract it from the href
24
+ const num = prNumber ?? (href.split('/').pop() || 'Unknown')
25
+
26
+ return (
27
+ <a
28
+ className={cn(
29
+ 'hover:bg-muted-foreground/5 inline-flex flex-row items-center gap-1 rounded-lg border px-1.5 py-1 text-sm transition-colors duration-500',
30
+ className
31
+ )}
32
+ href={href}
33
+ target={target}
34
+ >
35
+ <span className={colors[status]}>
36
+ <PullRequestStatusIcon status={status} />
37
+ </span>
38
+ <span className="text-body text-xs font-semibold">{`#${num}`}</span>
39
+ </a>
40
+ )
41
+ }
42
+
43
+ const colors: Record<Status, string> = {
44
+ open: 'text-emerald-500',
45
+ closed: 'text-rose-500',
46
+ merged: 'text-violet-500',
47
+ }
48
+
49
+ function PullRequestStatusIcon({
50
+ status,
51
+ }: {
52
+ status: 'open' | 'closed' | 'merged'
53
+ }): React.ReactNode {
54
+ switch (status) {
55
+ case 'open':
56
+ return <GitPullRequest size={14} />
57
+ case 'closed':
58
+ return <GitPullRequestClosed size={14} />
59
+ case 'merged':
60
+ return <Merge size={14} />
61
+ default:
62
+ assertNever(status)
63
+ }
64
+ }
@@ -0,0 +1,119 @@
1
+ import { Icon } from '../Icon'
2
+ import { cn } from '../../lib/utils'
3
+ import React, { Children, isValidElement, useMemo, useState } from 'react'
4
+ import { ComponentProps, ReactNode } from 'react'
5
+ import {
6
+ ImperativePanelHandle,
7
+ Panel,
8
+ PanelGroup,
9
+ PanelResizeHandle,
10
+ } from 'react-resizable-panels'
11
+
12
+ export interface ResizeHandleProps
13
+ extends ComponentProps<typeof PanelResizeHandle> {
14
+ children?: ReactNode
15
+ }
16
+
17
+ const ResizeHandle = ({ children, ...props }: ResizeHandleProps) => {
18
+ return <PanelResizeHandle {...props}>{children}</PanelResizeHandle>
19
+ }
20
+
21
+ ResizeHandle.displayName = 'ResizablePanel.ResizeHandle'
22
+
23
+ export interface ResizablePanelProps extends ComponentProps<typeof PanelGroup> {
24
+ children: ReactNode
25
+ className?: string
26
+ useDefaultHandle?: boolean
27
+ }
28
+
29
+ const ResizablePanel = ({
30
+ children,
31
+ className,
32
+ useDefaultHandle = true,
33
+ onResize,
34
+ ...props
35
+ }: ResizablePanelProps) => {
36
+ const validChildren = useMemo(
37
+ () =>
38
+ Children.toArray(children).filter((child) => {
39
+ if (!isValidElement(child)) return false
40
+ const type = child.type as { displayName?: string }
41
+ return (
42
+ type.displayName === 'ResizablePanel.Pane' ||
43
+ type.displayName === 'ResizablePanel.ResizeHandle'
44
+ )
45
+ }),
46
+ [children]
47
+ )
48
+
49
+ return (
50
+ <PanelGroup onResize={onResize} {...props} className={className}>
51
+ {React.Children.map(validChildren, (child, index) => {
52
+ if (!isValidElement(child)) return child
53
+ return (
54
+ <>
55
+ {child}
56
+
57
+ {index < validChildren.length - 1 && useDefaultHandle && (
58
+ <DefaultResizeHandle direction={props.direction} />
59
+ )}
60
+ </>
61
+ )
62
+ })}
63
+ </PanelGroup>
64
+ )
65
+ }
66
+
67
+ export interface PaneProps extends ComponentProps<typeof Panel> {
68
+ children: ReactNode
69
+ className?: string
70
+ panelRef?: React.LegacyRef<ImperativePanelHandle>
71
+ }
72
+
73
+ const Pane = ({ children, className, panelRef, ...props }: PaneProps) => {
74
+ return (
75
+ <Panel className={className} {...props} ref={panelRef}>
76
+ {children}
77
+ </Panel>
78
+ )
79
+ }
80
+
81
+ Pane.displayName = 'ResizablePanel.Pane'
82
+
83
+ const DefaultResizeHandle = ({
84
+ direction,
85
+ }: {
86
+ direction: 'horizontal' | 'vertical'
87
+ }) => {
88
+ const [isResizing, setIsResizing] = useState(false)
89
+ return (
90
+ <PanelResizeHandle
91
+ onDragging={(dragging) => setIsResizing(dragging)}
92
+ hitAreaMargins={{
93
+ coarse: 10,
94
+ fine: 10,
95
+ }}
96
+ className={cn(
97
+ 'relative border-[1.25px] border-zinc-900/50',
98
+ isResizing && 'border-foreground/10'
99
+ )}
100
+ >
101
+ <div
102
+ className={cn(
103
+ 'bg-card text-body-muted absolute top-[50%] flex translate-x-[-50%] items-center justify-center rounded-md border shadow-sm shadow-zinc-400/5',
104
+ direction === 'vertical' ? 'cursor-ns-resize' : 'cursor-ew-resize',
105
+ isResizing && 'text-foreground'
106
+ )}
107
+ >
108
+ <Icon name="grip-vertical" className="h-8 w-5" />
109
+ </div>
110
+ </PanelResizeHandle>
111
+ )
112
+ }
113
+
114
+ const ResizablePanelWithSubcomponents = Object.assign(ResizablePanel, {
115
+ Pane,
116
+ ResizeHandle,
117
+ })
118
+
119
+ export { ResizablePanelWithSubcomponents as ResizablePanel }
@@ -0,0 +1,32 @@
1
+ .svg {
2
+ user-select: none;
3
+ }
4
+
5
+ .text {
6
+ fill: var(--progress-colour);
7
+ transition: fill var(--transition-length) ease var(--delay);
8
+ font-weight: bold;
9
+ font-size: 36px;
10
+ }
11
+
12
+ .circleBase {
13
+ stroke-linecap: round;
14
+ stroke-linejoin: round;
15
+ stroke-dashoffset: 0;
16
+ transform-origin: 50% 50%;
17
+ shape-rendering: geometricprecision;
18
+ stroke-width: var(--stroke-width);
19
+ transition: stroke var(--transition-length) ease var(--delay);
20
+ }
21
+
22
+ .progress {
23
+ composes: circleBase;
24
+ stroke: var(--progress-colour);
25
+ stroke-dasharray: var(--progress-stroke-dasharray);
26
+ }
27
+
28
+ .track {
29
+ composes: circleBase;
30
+ stroke: var(--track-colour);
31
+ stroke-dasharray: var(--track-stroke-dasharray);
32
+ }
@@ -0,0 +1,268 @@
1
+ import { Size } from '../../types'
2
+ import type { Range } from '../../lib/typeUtils'
3
+ import { useEffect, useMemo, useState, type CSSProperties } from 'react'
4
+ import styles from './index.module.css'
5
+ import { cn } from '../../lib/utils'
6
+
7
+ export type ScoreValue = Range<100>
8
+
9
+ interface ThresholdStyles {
10
+ /**
11
+ * The color of the track when the score is greater than the threshold.
12
+ */
13
+ [key: number]: string
14
+ }
15
+
16
+ export interface ScoreProps {
17
+ /**
18
+ * The score to display. Must be between 0 and 100.
19
+ */
20
+ score: ScoreValue
21
+ /**
22
+ * The size of the score component e.g small, medium, large, xl, 2xl
23
+ */
24
+ size?: Size
25
+ /**
26
+ * Whether to show the label.
27
+ */
28
+ showLabel?: boolean
29
+ /**
30
+ * The color of the track.
31
+ */
32
+ trackColor?: string
33
+ /**
34
+ * Custom definition of thresholds for color changes.
35
+ * e.g [50, 75] will change color to green at 75%, orange at 50%. Otherwise red.
36
+ */
37
+ thresholds?: ThresholdStyles
38
+
39
+ animate?: boolean
40
+
41
+ animationDuration?: number
42
+ className?: string
43
+ }
44
+
45
+ const transition = {
46
+ length: 1000, // ms
47
+ step: 200, // ms
48
+ delay: 0, // ms
49
+ }
50
+ const gapPercent = 5
51
+ const strokeWidth = 10
52
+ const circleSize = 100
53
+ const radius = circleSize / 2 - strokeWidth / 2
54
+ const circumference = 2 * Math.PI * radius
55
+ const percentToDegree = 360 / 100 // deg
56
+ const percentToPx = circumference / 100 // px
57
+ const offsetFactor = 0
58
+ const offsetFactorSecondary = 1 - offsetFactor
59
+
60
+ const sizeMap: Record<Size, number> = {
61
+ small: 40,
62
+ medium: 50,
63
+ large: 60,
64
+ xl: 70,
65
+ '2xl': 80,
66
+ }
67
+
68
+ const defaultThresholds: ThresholdStyles = {
69
+ 0: 'var(--score-low)',
70
+ 40: 'var(--score-mid)',
71
+ 60: 'var(--score-high)',
72
+ }
73
+
74
+ const defaultTrackColor = 'var(--score-track)'
75
+
76
+ export function Score({
77
+ score,
78
+ size = 'large',
79
+ showLabel = true,
80
+ trackColor = defaultTrackColor,
81
+ thresholds = defaultThresholds,
82
+ animate = false,
83
+ animationDuration = 40,
84
+ className,
85
+ }: ScoreProps) {
86
+ const [scoreInternal, setScoreInternal] = useState<ScoreValue>(
87
+ animate ? 0 : score
88
+ )
89
+ const strokePercent = scoreInternal // %
90
+
91
+ useEffect(() => {
92
+ if (animate) {
93
+ const stepSize = Math.max(1, Math.floor(score / 20)) // Increase by 5% of total at minimum
94
+ const interval = setInterval(() => {
95
+ setScoreInternal((prevScore) => {
96
+ if (prevScore >= score) {
97
+ clearInterval(interval)
98
+ return score
99
+ }
100
+ return Math.min(prevScore + stepSize, score) as ScoreValue
101
+ })
102
+ }, animationDuration) // Reduced interval time for smoother animation
103
+ return () => clearInterval(interval)
104
+ }
105
+ }, [])
106
+
107
+ const progressColour = useMemo(() => {
108
+ for (const [threshold, color] of Object.entries(thresholds).reverse()) {
109
+ if (scoreInternal >= Number(threshold)) return color
110
+ }
111
+ }, [scoreInternal, thresholds])
112
+
113
+ const primaryStrokeDasharray = () => {
114
+ if (
115
+ offsetFactor > 0 &&
116
+ strokePercent > 100 - gapPercent * 2 * offsetFactor
117
+ ) {
118
+ // calculation to gradually shift back to 0 offset as progress nears 100% when offsetFactor > 0
119
+ const subtract = -strokePercent + 100
120
+
121
+ return `${Math.max(strokePercent * percentToPx - subtract * percentToPx, 0)} ${circumference}`
122
+ } else {
123
+ const subtract = gapPercent * 2 * offsetFactor
124
+
125
+ return `${Math.max(strokePercent * percentToPx - subtract * percentToPx, 0)} ${circumference}`
126
+ }
127
+ }
128
+
129
+ const secondaryStrokeDasharray = () => {
130
+ if (
131
+ offsetFactorSecondary < 1 &&
132
+ strokePercent < gapPercent * 2 * offsetFactorSecondary
133
+ ) {
134
+ // calculation to gradually shift back to 1 secondary offset as progress nears 100% when offsetFactorSecondary < 1
135
+ const subtract = strokePercent
136
+
137
+ return `${Math.max((100 - strokePercent) * percentToPx - subtract * percentToPx, 0)} ${circumference}`
138
+ } else {
139
+ const subtract = gapPercent * 2 * offsetFactorSecondary
140
+
141
+ return `${Math.max((100 - strokePercent) * percentToPx - subtract * percentToPx, 0)} ${circumference}`
142
+ }
143
+ }
144
+
145
+ const primaryTransform = () => {
146
+ if (
147
+ offsetFactor > 0 &&
148
+ strokePercent > 100 - gapPercent * 2 * offsetFactor
149
+ ) {
150
+ // calculation to gradually shift back to 0 offset as progress nears 100% when offsetFactor > 0
151
+ const add = 0.5 * (-strokePercent + 100)
152
+
153
+ return `rotate(${-90 + add * percentToDegree}deg)`
154
+ } else {
155
+ const add = gapPercent * offsetFactor
156
+
157
+ return `rotate(${-90 + add * percentToDegree}deg)`
158
+ }
159
+ }
160
+
161
+ const secondaryTransform = () => {
162
+ if (
163
+ offsetFactorSecondary < 1 &&
164
+ strokePercent < gapPercent * 2 * offsetFactorSecondary
165
+ ) {
166
+ // calculation to gradually shift back to 1 secondary offset as progress nears 100% when offsetFactorSecondary < 1
167
+ const subtract = 0.5 * strokePercent
168
+
169
+ return `rotate(${360 - 90 - subtract * percentToDegree}deg) scaleY(-1)`
170
+ } else {
171
+ const subtract = gapPercent * offsetFactorSecondary
172
+
173
+ return `rotate(${360 - 90 - subtract * percentToDegree}deg) scaleY(-1)`
174
+ }
175
+ }
176
+
177
+ const primaryOpacity = () => {
178
+ if (
179
+ offsetFactor > 0 &&
180
+ strokePercent < gapPercent * 2 * offsetFactor &&
181
+ strokePercent < gapPercent * 2 * offsetFactorSecondary
182
+ ) {
183
+ return 0
184
+ } else return 1
185
+ }
186
+
187
+ const secondaryOpacity = () => {
188
+ if (
189
+ (offsetFactor === 0 && strokePercent > 100 - gapPercent * 2) ||
190
+ (offsetFactor > 0 &&
191
+ strokePercent > 100 - gapPercent * 2 * offsetFactor &&
192
+ strokePercent > 100 - gapPercent * 2 * offsetFactorSecondary)
193
+ ) {
194
+ return 0
195
+ } else return 1
196
+ }
197
+
198
+ return (
199
+ <svg
200
+ xmlns="http://www.w3.org/2000/svg"
201
+ viewBox={`0 0 ${circleSize} ${circleSize}`}
202
+ shapeRendering="crispEdges"
203
+ width={sizeMap[size]}
204
+ height={sizeMap[size]}
205
+ className={cn(styles.svg, className)}
206
+ style={
207
+ {
208
+ '--stroke-width': strokeWidth,
209
+ '--transition-length': `${transition?.length}ms`,
210
+ '--delay': `${transition?.delay}ms`,
211
+ } as CSSProperties
212
+ }
213
+ fill="none"
214
+ >
215
+ <circle
216
+ cx={circleSize / 2}
217
+ cy={circleSize / 2}
218
+ r={radius}
219
+ style={
220
+ {
221
+ '--track-colour': trackColor,
222
+ '--track-stroke-dasharray': secondaryStrokeDasharray(),
223
+ transform: secondaryTransform(),
224
+ opacity: secondaryOpacity(),
225
+ } as CSSProperties
226
+ }
227
+ className={styles.track}
228
+ />
229
+
230
+ <circle
231
+ cx={circleSize / 2}
232
+ cy={circleSize / 2}
233
+ r={radius}
234
+ style={
235
+ {
236
+ '--progress-colour': progressColour,
237
+ '--progress-stroke-dasharray': primaryStrokeDasharray(),
238
+ '--transition-length': `${transition?.length}ms`,
239
+ '--delay': `${transition?.delay}ms`,
240
+ transform: primaryTransform(),
241
+ opacity: primaryOpacity(),
242
+ } as CSSProperties
243
+ }
244
+ className={styles.progress}
245
+ />
246
+
247
+ {showLabel && (
248
+ <text
249
+ x="50%"
250
+ y="50%"
251
+ textAnchor="middle"
252
+ dominantBaseline="middle"
253
+ alignmentBaseline="central"
254
+ className={styles.text}
255
+ style={
256
+ {
257
+ '--progress-colour': progressColour,
258
+ '--transition-length': `${transition?.length}ms`,
259
+ '--delay': `${transition?.delay}ms`,
260
+ } as CSSProperties
261
+ }
262
+ >
263
+ {Math.round(strokePercent)}
264
+ </text>
265
+ )}
266
+ </svg>
267
+ )
268
+ }
@@ -0,0 +1,48 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'
5
+
6
+ import { cn } from '../../lib/utils'
7
+
8
+ const ScrollArea = React.forwardRef<
9
+ React.ElementRef<typeof ScrollAreaPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
11
+ >(({ className, children, ...props }, ref) => (
12
+ <ScrollAreaPrimitive.Root
13
+ ref={ref}
14
+ className={cn('relative overflow-hidden', className)}
15
+ {...props}
16
+ >
17
+ <ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
18
+ {children}
19
+ </ScrollAreaPrimitive.Viewport>
20
+ <ScrollBar />
21
+ <ScrollAreaPrimitive.Corner />
22
+ </ScrollAreaPrimitive.Root>
23
+ ))
24
+ ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
25
+
26
+ const ScrollBar = React.forwardRef<
27
+ React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
28
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
29
+ >(({ className, orientation = 'vertical', ...props }, ref) => (
30
+ <ScrollAreaPrimitive.ScrollAreaScrollbar
31
+ ref={ref}
32
+ orientation={orientation}
33
+ className={cn(
34
+ 'flex touch-none transition-colors select-none',
35
+ orientation === 'vertical' &&
36
+ 'h-full w-2.5 border-l border-l-transparent p-[1px]',
37
+ orientation === 'horizontal' &&
38
+ 'h-2.5 flex-col border-t border-t-transparent p-[1px]',
39
+ className
40
+ )}
41
+ {...props}
42
+ >
43
+ <ScrollAreaPrimitive.ScrollAreaThumb className="bg-border relative flex-1 rounded-full" />
44
+ </ScrollAreaPrimitive.ScrollAreaScrollbar>
45
+ ))
46
+ ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
47
+
48
+ export { ScrollArea, ScrollBar }
@@ -0,0 +1,19 @@
1
+ .active {
2
+ border-radius: 50px;
3
+ background: #f8f8f8;
4
+ box-shadow:
5
+ 0 0 4px 0 rgba(0, 0, 0, 0.1),
6
+ 0 2px 1px 0 #fff inset,
7
+ 0 -2px 1px 0 rgba(0, 0, 0, 0.05) inset;
8
+ }
9
+
10
+ :global(.dark) {
11
+ .active {
12
+ background: #121212;
13
+ box-shadow:
14
+ 0 0 6px 0 rgba(0, 0, 0, 0.35),
15
+ 0 2px 1px 0 rgba(255, 255, 255, 0.08) inset,
16
+ 0 -2px 1px 0 rgba(0, 0, 0, 0.45) inset;
17
+ }
18
+ }
19
+
@@ -0,0 +1,101 @@
1
+ import { cn } from '../../lib/utils'
2
+ import { motion, MotionProps } from 'motion/react'
3
+ import React, { useMemo } from 'react'
4
+ import styles from './index.module.css'
5
+
6
+ export interface SegmentedButtonProps {
7
+ children: React.ReactNode
8
+ className?: string
9
+ }
10
+
11
+ const SegmentedButtonBase = ({ children, className }: SegmentedButtonProps) => {
12
+ const [hoveredId, setHoveredId] = React.useState<string | null>(null)
13
+
14
+ const enhancedChildren = useMemo(
15
+ () =>
16
+ React.Children.map(children, (child) => {
17
+ if (!React.isValidElement<SegmentedButtonItemProps>(child)) return child
18
+ const childId: string | undefined = child.props.id
19
+
20
+ const onClick: React.MouseEventHandler<HTMLButtonElement> = (e) => {
21
+ setHoveredId(childId ?? null)
22
+ child.props.onClick?.(e)
23
+ }
24
+
25
+ const highlighted = hoveredId
26
+ ? childId === hoveredId
27
+ : child.props.active
28
+
29
+ return React.cloneElement(
30
+ child as React.ReactElement<SegmentedButtonItemProps>,
31
+ {
32
+ highlighted,
33
+ onClick,
34
+ }
35
+ )
36
+ }),
37
+ [children]
38
+ )
39
+
40
+ return (
41
+ <div
42
+ className={cn(
43
+ 'border-neutral-softest relative inline-flex flex-row items-center overflow-hidden rounded-full border shadow-xs backdrop-blur-md',
44
+ className
45
+ )}
46
+ >
47
+ {enhancedChildren}
48
+ </div>
49
+ )
50
+ }
51
+
52
+ SegmentedButtonBase.displayName = 'SegmentedButton'
53
+
54
+ export interface SegmentedButtonItemProps extends MotionProps {
55
+ children: React.ReactNode
56
+ className?: string
57
+ active?: boolean
58
+ highlighted?: boolean
59
+ id: string
60
+ onClick?: React.MouseEventHandler<HTMLButtonElement>
61
+ }
62
+
63
+ const SegmentedButtonItem = ({
64
+ children,
65
+ className,
66
+ highlighted,
67
+ ...props
68
+ }: SegmentedButtonItemProps) => {
69
+ return (
70
+ <motion.button
71
+ className="text-codeline-xs hover:text-highlight relative flex items-center rounded-full px-5 py-1 uppercase"
72
+ {...props}
73
+ >
74
+ {highlighted && (
75
+ <motion.span
76
+ layoutId="segmented-highlight"
77
+ className={cn(
78
+ 'absolute inset-0 -z-10 rounded-full shadow-sm',
79
+ styles.active
80
+ )}
81
+ transition={{ type: 'spring', duration: 0.7, bounce: 0.3 }}
82
+ />
83
+ )}
84
+ <span
85
+ className={cn(
86
+ 'relative z-10',
87
+ highlighted && 'text-highlight',
88
+ className
89
+ )}
90
+ >
91
+ {children}
92
+ </span>
93
+ </motion.button>
94
+ )
95
+ }
96
+
97
+ SegmentedButtonItem.displayName = 'SegmentedButtonItem'
98
+
99
+ export const SegmentedButton = Object.assign(SegmentedButtonBase, {
100
+ Item: SegmentedButtonItem,
101
+ })