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

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,550 @@
1
+ import { cn, partitionBy } from '../../lib/utils'
2
+ import React, {
3
+ Children,
4
+ isValidElement,
5
+ PropsWithChildren,
6
+ HTMLAttributes,
7
+ } from 'react'
8
+ import { Slot } from '@radix-ui/react-slot'
9
+ import { Icon } from '../Icon'
10
+ import { useAppLayout } from '../../hooks/useAppLayout'
11
+ import { motion } from 'motion/react'
12
+ import { Logo } from '../Logo'
13
+ import {
14
+ Tooltip,
15
+ TooltipContent,
16
+ TooltipProvider,
17
+ TooltipTrigger,
18
+ TooltipPortal,
19
+ TooltipArrow,
20
+ } from '../Tooltip'
21
+ import { Key } from '../KeyHint'
22
+ import { useAppLayoutKeys } from './useAppLayoutKeys'
23
+ import { IconName } from '../Icon/names'
24
+ import { ThemeSwitcher } from '../ThemeSwitcher'
25
+
26
+ interface AppLayoutProps extends PropsWithChildren {
27
+ className?: string
28
+ }
29
+
30
+ const AppLayoutBase = ({ children, className }: AppLayoutProps) => {
31
+ const { collapsed } = useAppLayout()
32
+
33
+ const childComponents = Children.toArray(children).reduce(
34
+ (acc, child) => {
35
+ if (!isValidElement(child)) return acc
36
+ const type = child.type as { displayName?: string }
37
+ const displayName = type.displayName
38
+
39
+ switch (displayName) {
40
+ case 'AppLayout.Sidebar':
41
+ acc.sidebar = child
42
+ break
43
+ case 'AppLayout.Surface':
44
+ acc.surface = child
45
+ break
46
+ case 'AppLayout.SurfaceHeader':
47
+ acc.surfaceHeader = child
48
+ break
49
+ case 'AppLayout.Header':
50
+ acc.header = child
51
+ break
52
+ }
53
+
54
+ return acc
55
+ },
56
+ {
57
+ sidebar: null as React.ReactElement | null,
58
+ surface: null as React.ReactElement | null,
59
+ surfaceHeader: null as React.ReactElement | null,
60
+ header: null as React.ReactElement | null,
61
+ }
62
+ )
63
+ const { sidebar, surface, surfaceHeader, header } = childComponents
64
+
65
+ return (
66
+ <div
67
+ className={cn(
68
+ 'bg-surface-secondary flex h-screen w-full gap-3 overflow-hidden p-2 pr-0 pb-0',
69
+ className
70
+ )}
71
+ >
72
+ {!collapsed && sidebar}
73
+
74
+ <motion.div
75
+ layout
76
+ className="flex w-full flex-col"
77
+ initial={{ left: collapsed ? '100%' : '0' }}
78
+ animate={{ left: collapsed ? '100%' : '0' }}
79
+ transition={{ duration: 0.25, type: 'spring', bounce: 0 }}
80
+ >
81
+ {header}
82
+
83
+ <main
84
+ className={cn(
85
+ 'bg-surface-primary mr-2 mb-2 flex h-full flex-col overflow-hidden rounded-xl shadow-sm'
86
+ )}
87
+ >
88
+ <div className="flex w-full flex-shrink-0 items-center border-b p-2">
89
+ {surfaceHeader}
90
+ </div>
91
+
92
+ <div className="min-h-0 flex-1">{surface}</div>
93
+ </main>
94
+ </motion.div>
95
+ </div>
96
+ )
97
+ }
98
+
99
+ AppLayoutBase.displayName = 'AppLayout'
100
+
101
+ interface AppLayoutSurfaceProps extends HTMLAttributes<HTMLDivElement> {
102
+ className?: string
103
+ }
104
+
105
+ const AppLayoutSurface = ({
106
+ children,
107
+ className,
108
+ ...props
109
+ }: AppLayoutSurfaceProps) => {
110
+ return (
111
+ <div className={cn('h-full overflow-auto', className)} {...props}>
112
+ {children}
113
+ </div>
114
+ )
115
+ }
116
+
117
+ AppLayoutSurface.displayName = 'AppLayout.Surface'
118
+
119
+ interface AppLayoutSidebarProps {
120
+ className?: string
121
+ children?: React.ReactNode
122
+
123
+ /**
124
+ * A React.ReactNode to render instead of the default Logo.
125
+ */
126
+ Logo?: React.ReactNode
127
+
128
+ /**
129
+ * A function to call when the the brand logo is clicked.
130
+ */
131
+ onHomeNavigation?: () => void
132
+ }
133
+
134
+ const AppLayoutSidebar = ({
135
+ children,
136
+ className,
137
+ onHomeNavigation,
138
+ }: AppLayoutSidebarProps) => {
139
+ const { collapsed } = useAppLayout()
140
+
141
+ const [nav, rest] = partitionBy(Children.toArray(children), (child) => {
142
+ if (!isValidElement(child)) return false
143
+ const type = child.type as { displayName?: string }
144
+ return type.displayName === 'AppLayout.Nav'
145
+ })
146
+
147
+ return (
148
+ <motion.div
149
+ initial={false}
150
+ layout="position"
151
+ className={cn('mt-4 flex w-fit flex-col items-start px-1', className)}
152
+ transition={{ duration: 0.25, type: 'spring', bounce: 0 }}
153
+ >
154
+ <div className="flex flex-col gap-4">
155
+ <Logo
156
+ variant={collapsed ? 'icon' : 'wordmark'}
157
+ className={cn('cursor-pointer', !collapsed && 'min-w-[140px]')}
158
+ onClick={onHomeNavigation}
159
+ />
160
+ {nav}
161
+ </div>
162
+
163
+ <div className="flex h-full flex-col gap-1">{rest}</div>
164
+ </motion.div>
165
+ )
166
+ }
167
+ AppLayoutSidebar.displayName = 'AppLayout.Sidebar'
168
+
169
+ interface AppLayoutThemeSwitcherProps {
170
+ className?: string
171
+ }
172
+
173
+ const AppLayoutThemeSwitcher = ({ className }: AppLayoutThemeSwitcherProps) => {
174
+ const { collapsed } = useAppLayout()
175
+ return (
176
+ <motion.div data-theme-switcher className={cn('mt-auto mb-6', className)}>
177
+ <ThemeSwitcher orientation={collapsed ? 'vertical' : 'horizontal'} />
178
+ </motion.div>
179
+ )
180
+ }
181
+
182
+ AppLayoutThemeSwitcher.displayName = 'AppLayout.ThemeSwitcher'
183
+
184
+ interface AppLayoutBreadcrumbProps extends PropsWithChildren {
185
+ className?: string
186
+ }
187
+
188
+ const AppLayoutBreadcrumb = ({
189
+ children,
190
+ className,
191
+ }: AppLayoutBreadcrumbProps) => {
192
+ const validChildren = Children.toArray(children).filter((child) => {
193
+ if (!isValidElement(child)) return false
194
+ const type = child.type as { displayName?: string }
195
+ const isValidSubType = type.displayName === 'AppLayout.BreadcrumbItem'
196
+ if (!isValidSubType) {
197
+ console.warn(
198
+ `Invalid child type: ${type.displayName}. Must be one of: CodeEditor.Pane, CodeEditor.Tabs, CodeEditor.CommandBar, CodeEditor.Empty`
199
+ )
200
+ }
201
+ return isValidSubType
202
+ })
203
+
204
+ return (
205
+ <div
206
+ className={cn(
207
+ 'bg-surface-primary flex min-h-8 items-center gap-1.5',
208
+ className
209
+ )}
210
+ >
211
+ {validChildren.map((child, index) => (
212
+ <React.Fragment key={index}>
213
+ {child}
214
+ {index < validChildren.length - 1 && <AppLayoutBreadcrumbDivider />}
215
+ </React.Fragment>
216
+ ))}
217
+ </div>
218
+ )
219
+ }
220
+
221
+ AppLayoutBreadcrumb.displayName = 'AppLayout.Breadcrumb'
222
+
223
+ const AppLayoutBreadcrumbDivider = () => {
224
+ return (
225
+ <span className="text-muted-foreground typography-body-lg select-none">
226
+ /
227
+ </span>
228
+ )
229
+ }
230
+
231
+ export interface AppLayoutBreadcrumbItemProps
232
+ extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
233
+ className?: string
234
+ active?: boolean
235
+ children?: React.ReactNode
236
+ disabled?: boolean
237
+ asChild?: boolean
238
+ }
239
+
240
+ const AppLayoutBreadcrumbItem = React.forwardRef<
241
+ HTMLAnchorElement,
242
+ AppLayoutBreadcrumbItemProps
243
+ >(
244
+ (
245
+ {
246
+ asChild = false,
247
+ children,
248
+ className,
249
+ active = false,
250
+ disabled = false,
251
+ ...rest
252
+ },
253
+ ref
254
+ ) => {
255
+ const Comp = asChild ? Slot : 'a'
256
+
257
+ return (
258
+ <Comp
259
+ ref={ref}
260
+ className={cn(
261
+ 'typography-body-md text-muted-foreground cursor-pointer rounded-md px-1.5 text-lg font-light select-none',
262
+ active && 'text-foreground cursor-default',
263
+ !active && 'hover:text-foreground hover:bg-accent',
264
+ disabled &&
265
+ 'hover:text-muted-foreground cursor-default opacity-50 hover:bg-transparent',
266
+ className
267
+ )}
268
+ {...rest}
269
+ >
270
+ {children}
271
+ </Comp>
272
+ )
273
+ }
274
+ )
275
+
276
+ AppLayoutBreadcrumbItem.displayName = 'AppLayout.BreadcrumbItem'
277
+
278
+ const AppLayoutHeaderDivider = () => {
279
+ return <div className="bg-border h-full min-h-4 w-px" />
280
+ }
281
+
282
+ AppLayoutHeaderDivider.displayName = 'AppLayout.HeaderDivider'
283
+
284
+ interface AppLayoutCollapseButtonProps extends PropsWithChildren {
285
+ className?: string
286
+ }
287
+
288
+ const AppLayoutCollapseButton = ({
289
+ className,
290
+ }: AppLayoutCollapseButtonProps) => {
291
+ const { collapsed, setCollapsed, keybinds } = useAppLayout()
292
+ useAppLayoutKeys()
293
+ return (
294
+ <div className={cn('flex items-center gap-2', className)}>
295
+ <TooltipProvider>
296
+ <Tooltip delayDuration={800} disableHoverableContent>
297
+ <TooltipTrigger asChild>
298
+ <button
299
+ className="group typography-body-md hover:bg-accent hover:text-primary rounded-md p-1.5"
300
+ onClick={() => {
301
+ setCollapsed(!collapsed)
302
+ }}
303
+ aria-label="Toggle sidebar"
304
+ >
305
+ <Icon
306
+ name="panel-left"
307
+ className="text-muted group-hover:!text-primary size-4"
308
+ />
309
+ </button>
310
+ </TooltipTrigger>
311
+ <TooltipPortal>
312
+ <TooltipContent
313
+ side="right"
314
+ align="start"
315
+ className="!z-50 flex flex-row items-center gap-2 px-2 py-1"
316
+ >
317
+ <span className="text-body-md text-muted">
318
+ {keybinds.toggle.description}
319
+ </span>
320
+ <div className="flex flex-row items-center gap-1 p-0.5">
321
+ <Key value="⌘" />
322
+ <span>+</span>
323
+ <Key value={keybinds.toggle.key} />
324
+ </div>
325
+ </TooltipContent>
326
+ </TooltipPortal>
327
+ </Tooltip>
328
+ </TooltipProvider>
329
+ </div>
330
+ )
331
+ }
332
+
333
+ AppLayoutCollapseButton.displayName = 'AppLayout.CollapseButton'
334
+
335
+ interface AppLayoutSurfaceHeaderProps extends PropsWithChildren {
336
+ className?: string
337
+ }
338
+
339
+ const AppLayoutSurfaceHeader = ({
340
+ children,
341
+ className,
342
+ }: AppLayoutSurfaceHeaderProps) => {
343
+ return (
344
+ <div className={cn('flex w-full items-center gap-3', className)}>
345
+ {children}
346
+ </div>
347
+ )
348
+ }
349
+
350
+ AppLayoutSurfaceHeader.displayName = 'AppLayout.SurfaceHeader'
351
+
352
+ interface AppLayoutHeaderProps extends PropsWithChildren {
353
+ className?: string
354
+ }
355
+
356
+ const AppLayoutHeader = ({ children, className }: AppLayoutHeaderProps) => {
357
+ return (
358
+ <div className={cn('flex w-full items-center gap-3', className)}>
359
+ {children}
360
+ </div>
361
+ )
362
+ }
363
+
364
+ AppLayoutHeader.displayName = 'AppLayout.Header'
365
+
366
+ interface AppLayoutNavProps extends HTMLAttributes<HTMLDivElement> {
367
+ className?: string
368
+ }
369
+
370
+ const AppLayoutNav = ({ children, className, ...props }: AppLayoutNavProps) => {
371
+ return (
372
+ <nav
373
+ className={cn('mt-3 flex flex-col items-start gap-1', className)}
374
+ {...props}
375
+ >
376
+ {children}
377
+ </nav>
378
+ )
379
+ }
380
+
381
+ AppLayoutNav.displayName = 'AppLayout.Nav'
382
+
383
+ export interface AppLayoutNavItemProps
384
+ extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
385
+ title: string
386
+ icon: IconName
387
+ children?: React.ReactNode
388
+ render?: ({
389
+ title,
390
+ icon,
391
+ active,
392
+ }: {
393
+ title: string
394
+ icon: React.ReactNode
395
+ active?: boolean
396
+ }) => React.ReactNode
397
+ className?: string
398
+ active?: boolean
399
+ disabled?: boolean
400
+ asChild?: boolean
401
+ }
402
+
403
+ const AppLayoutNavItem = React.forwardRef<
404
+ HTMLAnchorElement,
405
+ AppLayoutNavItemProps
406
+ >(
407
+ (
408
+ {
409
+ asChild = false,
410
+ title,
411
+ icon,
412
+ render,
413
+ className,
414
+ active,
415
+ disabled,
416
+ children,
417
+ ...rest
418
+ },
419
+ ref
420
+ ) => {
421
+ const { collapsed } = useAppLayout()
422
+
423
+ if (render) {
424
+ return render({ title, icon, active })
425
+ }
426
+
427
+ const Comp = asChild ? Slot : 'a'
428
+ const iconElement = (
429
+ <Icon name={icon} className="size-5" strokeWidth={1.3} />
430
+ )
431
+ const titleElement = collapsed ? null : (
432
+ <motion.span
433
+ initial={{ opacity: 0 }}
434
+ animate={{ opacity: 1 }}
435
+ transition={{ duration: 0.2 }}
436
+ className="typography-body-sm"
437
+ >
438
+ {title}
439
+ </motion.span>
440
+ )
441
+
442
+ const content = asChild ? (
443
+ children
444
+ ) : (
445
+ <>
446
+ {iconElement}
447
+ {titleElement}
448
+ </>
449
+ )
450
+
451
+ return (
452
+ <TooltipProvider>
453
+ <Tooltip>
454
+ <TooltipTrigger asChild>
455
+ <Comp
456
+ ref={ref}
457
+ className={cn(
458
+ 'text-muted-foreground hover:text-foreground hover:bg-accent flex h-8 w-full cursor-pointer items-center gap-3 rounded-md px-2',
459
+ active && 'text-foreground bg-accent',
460
+ disabled &&
461
+ 'hover:text-muted-foreground cursor-default opacity-50 hover:bg-transparent',
462
+ className
463
+ )}
464
+ {...rest}
465
+ {...(asChild && {
466
+ 'data-title': title,
467
+ 'data-icon': icon,
468
+ 'data-active': active,
469
+ 'data-disabled': disabled,
470
+ })}
471
+ >
472
+ {content}
473
+ </Comp>
474
+ </TooltipTrigger>
475
+ <TooltipContent
476
+ className="bg-foreground text-background border-foreground flex flex-row items-center gap-2 text-sm"
477
+ side="right"
478
+ hidden={!collapsed || disabled}
479
+ >
480
+ <TooltipArrow className="fill-foreground" />
481
+ {title}
482
+ </TooltipContent>
483
+ </Tooltip>
484
+ </TooltipProvider>
485
+ )
486
+ }
487
+ )
488
+
489
+ AppLayoutNavItem.displayName = 'AppLayout.NavItem'
490
+
491
+ export interface AppLayoutNavItemGroupProps
492
+ extends HTMLAttributes<HTMLDivElement> {
493
+ className?: string
494
+
495
+ /**
496
+ * The name of the group.
497
+ */
498
+ name: string
499
+
500
+ /**
501
+ * Child AppLayout.NavItem components.
502
+ */
503
+ children: React.ReactNode
504
+ }
505
+
506
+ const AppLayoutNavItemGroup = ({
507
+ children,
508
+ className,
509
+ name,
510
+ ...props
511
+ }: AppLayoutNavItemGroupProps) => {
512
+ return (
513
+ <div className={cn('mb-4 flex w-full flex-col', className)} {...props}>
514
+ <div className="text-codeline-sm mb-1.5 px-2 uppercase">{name}</div>
515
+ <div className="flex flex-col gap-1">{children}</div>
516
+ </div>
517
+ )
518
+ }
519
+
520
+ AppLayoutNavItemGroup.displayName = 'AppLayout.NavItemGroup'
521
+
522
+ const AppLayout = AppLayoutBase as typeof AppLayoutBase & {
523
+ Surface: typeof AppLayoutSurface
524
+ SurfaceHeader: typeof AppLayoutSurfaceHeader
525
+ Sidebar: typeof AppLayoutSidebar
526
+ Breadcrumb: typeof AppLayoutBreadcrumb
527
+ BreadcrumbItem: typeof AppLayoutBreadcrumbItem
528
+ CollapseButton: typeof AppLayoutCollapseButton
529
+ HeaderDivider: typeof AppLayoutHeaderDivider
530
+ Header: typeof AppLayoutHeader
531
+ ThemeSwitcher: typeof AppLayoutThemeSwitcher
532
+ Nav: typeof AppLayoutNav
533
+ NavItem: typeof AppLayoutNavItem
534
+ NavItemGroup: typeof AppLayoutNavItemGroup
535
+ }
536
+
537
+ AppLayout.Surface = AppLayoutSurface
538
+ AppLayout.SurfaceHeader = AppLayoutSurfaceHeader
539
+ AppLayout.Sidebar = AppLayoutSidebar
540
+ AppLayout.Breadcrumb = AppLayoutBreadcrumb
541
+ AppLayout.BreadcrumbItem = AppLayoutBreadcrumbItem
542
+ AppLayout.CollapseButton = AppLayoutCollapseButton
543
+ AppLayout.HeaderDivider = AppLayoutHeaderDivider
544
+ AppLayout.Header = AppLayoutHeader
545
+ AppLayout.ThemeSwitcher = AppLayoutThemeSwitcher
546
+ AppLayout.Nav = AppLayoutNav
547
+ AppLayout.NavItem = AppLayoutNavItem
548
+ AppLayout.NavItemGroup = AppLayoutNavItemGroup
549
+
550
+ export { AppLayout }
@@ -0,0 +1,40 @@
1
+ import { PropsWithChildren, useState, useEffect } from 'react'
2
+ import { AppLayoutContext, AppLayoutContextType } from './context'
3
+
4
+ interface AppLayoutProviderProps extends PropsWithChildren {
5
+ defaultCollapsed?: boolean
6
+ keybinds?: AppLayoutContextType['keybinds']
7
+ }
8
+
9
+ const defaultKeybinds = {
10
+ toggle: {
11
+ key: 'B',
12
+ description: 'Toggle',
13
+ },
14
+ }
15
+
16
+ export const AppLayoutProvider = ({
17
+ children,
18
+ defaultCollapsed = false,
19
+ keybinds,
20
+ }: AppLayoutProviderProps) => {
21
+ const finalKeybinds = keybinds ?? defaultKeybinds
22
+ const [collapsed, setCollapsed] = useState(defaultCollapsed)
23
+
24
+ // respond to defaultCollapsed changes
25
+ useEffect(() => {
26
+ setCollapsed(defaultCollapsed)
27
+ }, [defaultCollapsed])
28
+
29
+ return (
30
+ <AppLayoutContext.Provider
31
+ value={{
32
+ collapsed,
33
+ setCollapsed,
34
+ keybinds: finalKeybinds,
35
+ }}
36
+ >
37
+ {children}
38
+ </AppLayoutContext.Provider>
39
+ )
40
+ }
@@ -0,0 +1,26 @@
1
+ import { useAppLayout } from '../../hooks/useAppLayout'
2
+ import { useCallback, useEffect } from 'react'
3
+
4
+ export function useAppLayoutKeys() {
5
+ const { keybinds, collapsed, setCollapsed } = useAppLayout()
6
+
7
+ const handleKeyDown = useCallback(
8
+ (event: KeyboardEvent) => {
9
+ if (
10
+ (event.metaKey || event.ctrlKey) &&
11
+ event.key.toLowerCase() === keybinds.toggle.key.toLowerCase()
12
+ ) {
13
+ event.preventDefault()
14
+ setCollapsed(!collapsed)
15
+ }
16
+ },
17
+ [keybinds, collapsed, setCollapsed]
18
+ )
19
+
20
+ useEffect(() => {
21
+ document.addEventListener('keydown', handleKeyDown)
22
+ return () => {
23
+ document.removeEventListener('keydown', handleKeyDown)
24
+ }
25
+ }, [handleKeyDown])
26
+ }