@speakeasy-api/moonshine 1.33.4 → 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.
- package/README.md +26 -10
- package/package.json +12 -23
- package/scripts/generate-utility-docs.js +324 -0
- package/src/assets/icons/external/github.svg +3 -0
- package/src/assets/icons/external/maven.svg +152 -0
- package/src/assets/icons/external/npm.svg +4 -0
- package/src/assets/icons/external/nuget.svg +5 -0
- package/src/assets/icons/external/packagist.svg +1 -0
- package/src/assets/icons/external/pypi.svg +182 -0
- package/src/assets/icons/external/rubygems.svg +14 -0
- package/src/assets/icons/external/terraform.svg +1 -0
- package/src/assets/icons/languages/csharp.svg +1 -0
- package/src/assets/icons/languages/go.svg +1 -0
- package/src/assets/icons/languages/java.svg +1 -0
- package/src/assets/icons/languages/json.svg +2 -0
- package/src/assets/icons/languages/php.svg +1 -0
- package/src/assets/icons/languages/postman.svg +3 -0
- package/src/assets/icons/languages/python.svg +1 -0
- package/src/assets/icons/languages/ruby.svg +1 -0
- package/src/assets/icons/languages/swift.svg +1 -0
- package/src/assets/icons/languages/terraform.svg +1 -0
- package/src/assets/icons/languages/typescript.svg +1 -0
- package/src/assets/icons/languages/unity.svg +1 -0
- package/src/components/AIChat/AIChatContainer.tsx +71 -0
- package/src/components/AIChat/AIChatMessage.tsx +135 -0
- package/src/components/AIChat/AIChatMessageComposer.tsx +175 -0
- package/src/components/AIChat/AIChatMessageList.tsx +34 -0
- package/src/components/AIChat/AIChatModelSelector.tsx +159 -0
- package/src/components/AIChat/componentsTypes.ts +36 -0
- package/src/components/AIChat/context.ts +15 -0
- package/src/components/AIChat/index.ts +12 -0
- package/src/components/AIChat/parts/AIChatMessageFilePart.tsx +129 -0
- package/src/components/AIChat/parts/AIChatMessageReasoningPart.tsx +23 -0
- package/src/components/AIChat/parts/AIChatMessageSourcePart.tsx +58 -0
- package/src/components/AIChat/parts/AIChatMessageTextPart.tsx +33 -0
- package/src/components/AIChat/parts/AIChatMessageToolInvocationPart.tsx +53 -0
- package/src/components/AIChat/parts/AIChatMessageToolPart.tsx +395 -0
- package/src/components/AIChat/parts/AIChatMessageToolResultPart.tsx +46 -0
- package/src/components/AIChat/toolCallApproval.ts +61 -0
- package/src/components/AIChat/types.ts +97 -0
- package/src/components/ActionBar/index.tsx +184 -0
- package/src/components/Alert/index.tsx +118 -0
- package/src/components/Alert/types.ts +12 -0
- package/src/components/AppLayout/context.tsx +31 -0
- package/src/components/AppLayout/index.tsx +550 -0
- package/src/components/AppLayout/provider.tsx +40 -0
- package/src/components/AppLayout/useAppLayoutKeys.ts +26 -0
- package/src/components/Badge/index.tsx +227 -0
- package/src/components/Button/index.tsx +531 -0
- package/src/components/Card/index.tsx +193 -0
- package/src/components/CodeEditorLayout/index.tsx +394 -0
- package/src/components/CodeEditorLayout/styles.module.css +8 -0
- package/src/components/CodeHighlight/Pre.tsx +63 -0
- package/src/components/CodePlayground/index.tsx +411 -0
- package/src/components/CodeSnippet/codeSnippet.css +97 -0
- package/src/components/CodeSnippet/index.tsx +224 -0
- package/src/components/Combobox/index.tsx +193 -0
- package/src/components/Command/index.tsx +152 -0
- package/src/components/Container/index.tsx +31 -0
- package/src/components/ContextDropdown/index.tsx +150 -0
- package/src/components/Dialog/index.tsx +123 -0
- package/src/components/DragNDrop/DragNDropArea.tsx +30 -0
- package/src/components/DragNDrop/DragOverlay.tsx +4 -0
- package/src/components/DragNDrop/Draggable.tsx +97 -0
- package/src/components/DragNDrop/Droppable.tsx +51 -0
- package/src/components/Dropdown/index.tsx +201 -0
- package/src/components/ExternalPill/index.tsx +58 -0
- package/src/components/Facepile/index.tsx +309 -0
- package/src/components/GradientCircle/gradientCircle.css +34 -0
- package/src/components/GradientCircle/index.tsx +143 -0
- package/src/components/Grid/index.tsx +150 -0
- package/src/components/Heading/index.tsx +54 -0
- package/src/components/HighlightedText/index.tsx +152 -0
- package/src/components/Icon/customIcons/createCustomLucideIcon.ts +25 -0
- package/src/components/Icon/customIcons/gems.ts +26 -0
- package/{dist/go-DsW1bFpz.mjs → src/components/Icon/customIcons/go.ts} +21 -19
- package/src/components/Icon/customIcons/index.ts +11 -0
- package/{dist/maven-Dub5liK1.mjs → src/components/Icon/customIcons/maven.ts} +17 -15
- package/src/components/Icon/customIcons/npm.ts +19 -0
- package/{dist/nuget-D86y5HDl.mjs → src/components/Icon/customIcons/nuget.ts} +17 -15
- package/src/components/Icon/customIcons/packagist.ts +124 -0
- package/{dist/pypi-2SUX-2OR.mjs → src/components/Icon/customIcons/pypi.ts} +16 -14
- package/src/components/Icon/index.tsx +83 -0
- package/src/components/Icon/isIconName.ts +10 -0
- package/src/components/Icon/names.ts +14 -0
- package/src/components/IconButton/index.tsx +51 -0
- package/src/components/Input/index.tsx +98 -0
- package/src/components/KeyHint/index.tsx +118 -0
- package/src/components/LanguageIndicator/index.tsx +68 -0
- package/src/components/Link/index.tsx +153 -0
- package/src/components/LoggedInUserMenu/index.tsx +116 -0
- package/src/components/Logo/Animated.tsx +191 -0
- package/src/components/Logo/index.tsx +17 -0
- package/src/components/Logo/speakeasy-logo.riv +0 -0
- package/src/components/Logo/svgs/index.tsx +126 -0
- package/src/components/Modal/index.tsx +104 -0
- package/src/components/PageHeader/index.tsx +227 -0
- package/src/components/PageHeader/styles.module.css +27 -0
- package/src/components/Popover/index.tsx +35 -0
- package/src/components/PromptInput/index.tsx +372 -0
- package/src/components/PullRequestLink/index.tsx +64 -0
- package/src/components/ResizablePanel/index.tsx +119 -0
- package/src/components/Score/index.module.css +32 -0
- package/src/components/Score/index.tsx +268 -0
- package/src/components/ScrollArea/index.tsx +48 -0
- package/src/components/SegmentedButton/index.module.css +19 -0
- package/src/components/SegmentedButton/index.tsx +101 -0
- package/src/components/Select/index.tsx +159 -0
- package/src/components/Separator/index.tsx +23 -0
- package/src/components/Skeleton/index.tsx +61 -0
- package/src/components/Skeleton/skeleton.css +52 -0
- package/src/components/Stack/index.tsx +137 -0
- package/src/components/Subnav/index.tsx +315 -0
- package/src/components/Switch/index.tsx +29 -0
- package/src/components/Table/context/context.tsx +19 -0
- package/src/components/Table/context/tableProvider.tsx +39 -0
- package/src/components/Table/index.tsx +707 -0
- package/src/components/Table/styles.module.css +25 -0
- package/src/components/Tabs/index.tsx +87 -0
- package/src/components/TargetLanguageIcon/index.tsx +84 -0
- package/src/components/Text/index.tsx +59 -0
- package/src/components/ThemeSwitcher/index.tsx +118 -0
- package/src/components/Timeline/index.tsx +290 -0
- package/src/components/Tooltip/index.tsx +41 -0
- package/src/components/UserAvatar/index.tsx +87 -0
- package/src/components/UserAvatar/sizeMap.ts +12 -0
- package/src/components/Wizard/index.tsx +208 -0
- package/src/components/Wizard/types.ts +17 -0
- package/src/components/WorkspaceSelector/CreateOrg.tsx +95 -0
- package/src/components/WorkspaceSelector/CreateWorkspace.tsx +196 -0
- package/src/components/WorkspaceSelector/OrgList.tsx +115 -0
- package/src/components/WorkspaceSelector/OrgSelector.tsx +207 -0
- package/src/components/WorkspaceSelector/RecentWorkspaces.tsx +83 -0
- package/src/components/WorkspaceSelector/ScrollingList.tsx +84 -0
- package/src/components/WorkspaceSelector/SearchBox.tsx +40 -0
- package/src/components/WorkspaceSelector/WorkspaceItem.tsx +37 -0
- package/src/components/WorkspaceSelector/WorkspaceList.tsx +107 -0
- package/src/components/WorkspaceSelector/index.tsx +400 -0
- package/src/components/WorkspaceSelector/styles.css +74 -0
- package/src/components/__beta__/CLIWizard/index.tsx +357 -0
- package/src/components/__beta__/CLIWizard/terminal-command.tsx +108 -0
- package/src/components/__beta__/CLIWizard/terminal.tsx +83 -0
- package/src/components/__beta__/README.md +3 -0
- package/src/components/index.mdx +38 -0
- package/src/context/ConfigContext.tsx +43 -0
- package/src/context/ModalContext.tsx +118 -0
- package/src/context/theme.ts +1 -0
- package/src/hooks/useAppLayout.ts +10 -0
- package/src/hooks/useConfig.ts +10 -0
- package/src/hooks/useIsMounted.ts +13 -0
- package/src/hooks/useModal.tsx +10 -0
- package/src/hooks/useTailwindBreakpoint.ts +47 -0
- package/src/hooks/useTheme.ts +13 -0
- package/src/index.ts +234 -0
- package/src/lib/assert.ts +9 -0
- package/src/lib/codeUtils.ts +177 -0
- package/src/lib/debounce.ts +9 -0
- package/src/lib/responsiveMappers.ts +69 -0
- package/src/lib/responsiveUtils.ts +23 -0
- package/src/lib/storybookUtils.tsx +26 -0
- package/src/lib/typeUtils.ts +109 -0
- package/src/lib/utils.ts +85 -0
- package/src/styles/codeSyntax.css +59 -0
- package/src/styles/globals.css +51 -0
- package/src/types.ts +200 -0
- package/src/vite-env.d.ts +6 -0
- package/types/utilities.d.ts +1 -1
- package/dist/components/AIChat/AIChatContainer.d.ts +0 -26
- package/dist/components/AIChat/AIChatMessage.d.ts +0 -19
- package/dist/components/AIChat/AIChatMessageComposer.d.ts +0 -22
- package/dist/components/AIChat/AIChatMessageList.d.ts +0 -6
- package/dist/components/AIChat/AIChatModelSelector.d.ts +0 -14
- package/dist/components/AIChat/componentsTypes.d.ts +0 -11
- package/dist/components/AIChat/context.d.ts +0 -3
- package/dist/components/AIChat/index.d.ts +0 -12
- package/dist/components/AIChat/parts/AIChatMessageFilePart.d.ts +0 -7
- package/dist/components/AIChat/parts/AIChatMessageReasoningPart.d.ts +0 -5
- package/dist/components/AIChat/parts/AIChatMessageSourcePart.d.ts +0 -9
- package/dist/components/AIChat/parts/AIChatMessageTextPart.d.ts +0 -5
- package/dist/components/AIChat/parts/AIChatMessageToolInvocationPart.d.ts +0 -6
- package/dist/components/AIChat/parts/AIChatMessageToolPart.d.ts +0 -33
- package/dist/components/AIChat/parts/AIChatMessageToolResultPart.d.ts +0 -5
- package/dist/components/AIChat/toolCallApproval.d.ts +0 -15
- package/dist/components/AIChat/types.d.ts +0 -79
- package/dist/components/ActionBar/index.d.ts +0 -36
- package/dist/components/Alert/index.d.ts +0 -18
- package/dist/components/Alert/types.d.ts +0 -4
- package/dist/components/AppLayout/context.d.ts +0 -16
- package/dist/components/AppLayout/index.d.ts +0 -131
- package/dist/components/AppLayout/provider.d.ts +0 -8
- package/dist/components/AppLayout/useAppLayoutKeys.d.ts +0 -1
- package/dist/components/Badge/index.d.ts +0 -21
- package/dist/components/Button/index.d.ts +0 -22
- package/dist/components/Card/index.d.ts +0 -47
- package/dist/components/CodeEditorLayout/index.d.ts +0 -101
- package/dist/components/CodeHighlight/Pre.d.ts +0 -8
- package/dist/components/CodePlayground/index.d.ts +0 -102
- package/dist/components/CodeSnippet/index.d.ts +0 -49
- package/dist/components/Combobox/index.d.ts +0 -35
- package/dist/components/Command/index.d.ts +0 -80
- package/dist/components/Container/index.d.ts +0 -9
- package/dist/components/ContextDropdown/index.d.ts +0 -7
- package/dist/components/Dialog/index.d.ts +0 -21
- package/dist/components/DragNDrop/DragNDropArea.d.ts +0 -8
- package/dist/components/DragNDrop/DragOverlay.d.ts +0 -1
- package/dist/components/DragNDrop/Draggable.d.ts +0 -29
- package/dist/components/DragNDrop/Droppable.d.ts +0 -28
- package/dist/components/Dropdown/index.d.ts +0 -27
- package/dist/components/ExternalPill/index.d.ts +0 -12
- package/dist/components/Facepile/index.d.ts +0 -16
- package/dist/components/GradientCircle/index.d.ts +0 -10
- package/dist/components/Grid/index.d.ts +0 -80
- package/dist/components/Heading/index.d.ts +0 -12
- package/dist/components/HighlightedText/index.d.ts +0 -19
- package/dist/components/Icon/customIcons/createCustomLucideIcon.d.ts +0 -3
- package/dist/components/Icon/customIcons/gems.d.ts +0 -2
- package/dist/components/Icon/customIcons/go.d.ts +0 -2
- package/dist/components/Icon/customIcons/index.d.ts +0 -10
- package/dist/components/Icon/customIcons/maven.d.ts +0 -2
- package/dist/components/Icon/customIcons/npm.d.ts +0 -2
- package/dist/components/Icon/customIcons/nuget.d.ts +0 -2
- package/dist/components/Icon/customIcons/packagist.d.ts +0 -2
- package/dist/components/Icon/customIcons/pypi.d.ts +0 -2
- package/dist/components/Icon/index.d.ts +0 -10
- package/dist/components/Icon/isIconName.d.ts +0 -2
- package/dist/components/Icon/names.d.ts +0 -6
- package/dist/components/IconButton/index.d.ts +0 -14
- package/dist/components/Input/index.d.ts +0 -8
- package/dist/components/KeyHint/index.d.ts +0 -16
- package/dist/components/LanguageIndicator/index.d.ts +0 -7
- package/dist/components/Link/index.d.ts +0 -15
- package/dist/components/LoggedInUserMenu/index.d.ts +0 -17
- package/dist/components/Logo/Animated.d.ts +0 -7
- package/dist/components/Logo/index.d.ts +0 -7
- package/dist/components/Logo/svgs/index.d.ts +0 -6
- package/dist/components/Modal/index.d.ts +0 -8
- package/dist/components/PageHeader/index.d.ts +0 -45
- package/dist/components/Popover/index.d.ts +0 -8
- package/dist/components/PromptInput/index.d.ts +0 -55
- package/dist/components/PullRequestLink/index.d.ts +0 -10
- package/dist/components/ResizablePanel/index.d.ts +0 -26
- package/dist/components/Score/index.d.ts +0 -37
- package/dist/components/ScrollArea/index.d.ts +0 -5
- package/dist/components/SegmentedButton/index.d.ts +0 -23
- package/dist/components/Select/index.d.ts +0 -13
- package/dist/components/Separator/index.d.ts +0 -6
- package/dist/components/Skeleton/index.d.ts +0 -27
- package/dist/components/Stack/index.d.ts +0 -33
- package/dist/components/Subnav/index.d.ts +0 -12
- package/dist/components/Switch/index.d.ts +0 -4
- package/dist/components/Table/context/context.d.ts +0 -8
- package/dist/components/Table/context/tableProvider.d.ts +0 -6
- package/dist/components/Table/index.d.ts +0 -94
- package/dist/components/Tabs/index.d.ts +0 -21
- package/dist/components/TargetLanguageIcon/index.d.ts +0 -7
- package/dist/components/Text/index.d.ts +0 -19
- package/dist/components/ThemeSwitcher/index.d.ts +0 -6
- package/dist/components/Timeline/index.d.ts +0 -49
- package/dist/components/Tooltip/index.d.ts +0 -9
- package/dist/components/UserAvatar/index.d.ts +0 -9
- package/dist/components/UserAvatar/sizeMap.d.ts +0 -3
- package/dist/components/Wizard/index.d.ts +0 -19
- package/dist/components/Wizard/types.d.ts +0 -15
- package/dist/components/WorkspaceSelector/CreateOrg.d.ts +0 -6
- package/dist/components/WorkspaceSelector/CreateWorkspace.d.ts +0 -17
- package/dist/components/WorkspaceSelector/OrgList.d.ts +0 -12
- package/dist/components/WorkspaceSelector/OrgSelector.d.ts +0 -13
- package/dist/components/WorkspaceSelector/RecentWorkspaces.d.ts +0 -11
- package/dist/components/WorkspaceSelector/ScrollingList.d.ts +0 -21
- package/dist/components/WorkspaceSelector/SearchBox.d.ts +0 -9
- package/dist/components/WorkspaceSelector/WorkspaceItem.d.ts +0 -9
- package/dist/components/WorkspaceSelector/WorkspaceList.d.ts +0 -11
- package/dist/components/WorkspaceSelector/index.d.ts +0 -36
- package/dist/components/__beta__/CLIWizard/index.d.ts +0 -19
- package/dist/components/__beta__/CLIWizard/terminal-command.d.ts +0 -19
- package/dist/components/__beta__/CLIWizard/terminal.d.ts +0 -26
- package/dist/context/ConfigContext.d.ts +0 -18
- package/dist/context/ModalContext.d.ts +0 -22
- package/dist/context/theme.d.ts +0 -1
- package/dist/createCustomLucideIcon-CatlpFc0.mjs +0 -19
- package/dist/createCustomLucideIcon-CatlpFc0.mjs.map +0 -1
- package/dist/gems-DQ7pOLLr.mjs +0 -24
- package/dist/gems-DQ7pOLLr.mjs.map +0 -1
- package/dist/github-kgjMtfE7.mjs +0 -11
- package/dist/github-kgjMtfE7.mjs.map +0 -1
- package/dist/go-DsW1bFpz.mjs.map +0 -1
- package/dist/hooks/useAppLayout.d.ts +0 -1
- package/dist/hooks/useConfig.d.ts +0 -2
- package/dist/hooks/useIsMounted.d.ts +0 -1
- package/dist/hooks/useModal.d.ts +0 -11
- package/dist/hooks/useTailwindBreakpoint.d.ts +0 -3
- package/dist/hooks/useTheme.d.ts +0 -6
- package/dist/index-C9bJtuJ5.mjs +0 -16347
- package/dist/index-C9bJtuJ5.mjs.map +0 -1
- package/dist/index.d.ts +0 -80
- package/dist/lib/assert.d.ts +0 -2
- package/dist/lib/codeUtils.d.ts +0 -36
- package/dist/lib/debounce.d.ts +0 -1
- package/dist/lib/responsiveMappers.d.ts +0 -10
- package/dist/lib/responsiveUtils.d.ts +0 -3
- package/dist/lib/storybookUtils.d.ts +0 -5
- package/dist/lib/typeUtils.d.ts +0 -24
- package/dist/lib/utils.d.ts +0 -24
- package/dist/maven-Dub5liK1.mjs.map +0 -1
- package/dist/maven-W_nkSDNW.mjs +0 -107
- package/dist/maven-W_nkSDNW.mjs.map +0 -1
- package/dist/moonshine.es.js +0 -125
- package/dist/moonshine.es.js.map +0 -1
- package/dist/npm-BWTcVvFH.mjs +0 -11
- package/dist/npm-BWTcVvFH.mjs.map +0 -1
- package/dist/npm-BYcG5_q9.mjs +0 -17
- package/dist/npm-BYcG5_q9.mjs.map +0 -1
- package/dist/nuget-CV5HU1JR.mjs +0 -11
- package/dist/nuget-CV5HU1JR.mjs.map +0 -1
- package/dist/nuget-D86y5HDl.mjs.map +0 -1
- package/dist/packagist-BFSSrw4p.mjs +0 -118
- package/dist/packagist-BFSSrw4p.mjs.map +0 -1
- package/dist/packagist-D01fn9N_.mjs +0 -11
- package/dist/packagist-D01fn9N_.mjs.map +0 -1
- package/dist/pypi-2SUX-2OR.mjs.map +0 -1
- package/dist/pypi-DLh6kIJe.mjs +0 -11
- package/dist/pypi-DLh6kIJe.mjs.map +0 -1
- package/dist/rubygems-DeiNjcDV.mjs +0 -11
- package/dist/rubygems-DeiNjcDV.mjs.map +0 -1
- package/dist/speakeasy-logo-ByBTXLWb.mjs +0 -5
- package/dist/speakeasy-logo-ByBTXLWb.mjs.map +0 -1
- package/dist/style.css +0 -1
- package/dist/terraform-C4aktQ0o.mjs +0 -11
- package/dist/terraform-C4aktQ0o.mjs.map +0 -1
- package/dist/types.d.ts +0 -85
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { cn, getResponsiveClasses } from '../../lib/utils'
|
|
2
|
+
import { ResponsiveValue, Size } from '../../types'
|
|
3
|
+
import { userAvatarSizeMap } from './sizeMap'
|
|
4
|
+
import useTailwindBreakpoint from '../../hooks/useTailwindBreakpoint'
|
|
5
|
+
import { resolveSizeForBreakpoint } from '../../lib/responsiveUtils'
|
|
6
|
+
import { userAvatarSizeMapper } from './sizeMap'
|
|
7
|
+
|
|
8
|
+
export interface UserAvatarProps {
|
|
9
|
+
name: string
|
|
10
|
+
imageUrl?: string
|
|
11
|
+
size?: ResponsiveValue<Size>
|
|
12
|
+
border?: boolean
|
|
13
|
+
className?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const fallbackColors = [
|
|
17
|
+
'bg-red-500',
|
|
18
|
+
'bg-orange-500',
|
|
19
|
+
'bg-yellow-500',
|
|
20
|
+
'bg-green-500',
|
|
21
|
+
'bg-blue-500',
|
|
22
|
+
'bg-purple-500',
|
|
23
|
+
'bg-pink-500',
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
// deterministically returns a color based on the first letter of the name
|
|
27
|
+
function getFallbackColor(name: string | undefined) {
|
|
28
|
+
if (!name) return 'bg-gray-900'
|
|
29
|
+
const firstLetter = name[0].toLowerCase()
|
|
30
|
+
|
|
31
|
+
const index = firstLetter.charCodeAt(0) - 'a'.charCodeAt(0)
|
|
32
|
+
return fallbackColors[index % fallbackColors.length]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function UserAvatar({
|
|
36
|
+
name,
|
|
37
|
+
imageUrl,
|
|
38
|
+
size = 'medium',
|
|
39
|
+
border = false,
|
|
40
|
+
className,
|
|
41
|
+
}: UserAvatarProps) {
|
|
42
|
+
const breakpoint = useTailwindBreakpoint()
|
|
43
|
+
|
|
44
|
+
const resolvedSize = resolveSizeForBreakpoint(breakpoint, size, 'medium')
|
|
45
|
+
const sizeValue = userAvatarSizeMap[resolvedSize]
|
|
46
|
+
|
|
47
|
+
const hasImage = !!imageUrl
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
className={cn(
|
|
52
|
+
'flex items-center justify-center overflow-hidden rounded-full bg-gray-200',
|
|
53
|
+
getResponsiveClasses(size, userAvatarSizeMapper),
|
|
54
|
+
!hasImage && getFallbackColor(name),
|
|
55
|
+
border && 'border-background border-2',
|
|
56
|
+
className
|
|
57
|
+
)}
|
|
58
|
+
>
|
|
59
|
+
{hasImage ? (
|
|
60
|
+
<img
|
|
61
|
+
src={imageUrl}
|
|
62
|
+
alt={name}
|
|
63
|
+
className="h-full w-full object-cover object-center select-none"
|
|
64
|
+
/>
|
|
65
|
+
) : (
|
|
66
|
+
<svg
|
|
67
|
+
className="h-full w-full select-none"
|
|
68
|
+
viewBox={`0 0 ${sizeValue} ${sizeValue}`}
|
|
69
|
+
preserveAspectRatio="xMidYMid meet"
|
|
70
|
+
>
|
|
71
|
+
<text
|
|
72
|
+
x="50%"
|
|
73
|
+
y="50%"
|
|
74
|
+
textAnchor="middle"
|
|
75
|
+
alignmentBaseline="middle"
|
|
76
|
+
fill="white"
|
|
77
|
+
fontSize={sizeValue * 0.75}
|
|
78
|
+
dy="0.075em"
|
|
79
|
+
fontWeight="semibold"
|
|
80
|
+
>
|
|
81
|
+
{name[0] || '✖️'}
|
|
82
|
+
</text>
|
|
83
|
+
</svg>
|
|
84
|
+
)}
|
|
85
|
+
</div>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { cn } from '../../lib/utils'
|
|
3
|
+
import { CodeSnippet, Heading, Text } from '../../index'
|
|
4
|
+
import { useMemo } from 'react'
|
|
5
|
+
import { WizardStep } from './types'
|
|
6
|
+
|
|
7
|
+
export interface WizardProps {
|
|
8
|
+
/**
|
|
9
|
+
* The steps to display in the wizard
|
|
10
|
+
*/
|
|
11
|
+
steps: WizardStep[]
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The current step number, starting from 1
|
|
15
|
+
*/
|
|
16
|
+
currentStep: number
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The steps that have been completed, starting from 1
|
|
20
|
+
*/
|
|
21
|
+
completedSteps: number[]
|
|
22
|
+
|
|
23
|
+
headerContent: (
|
|
24
|
+
completedSteps: number[],
|
|
25
|
+
steps: WizardStep[]
|
|
26
|
+
) => React.ReactNode
|
|
27
|
+
|
|
28
|
+
className?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function Wizard({
|
|
32
|
+
steps,
|
|
33
|
+
currentStep,
|
|
34
|
+
completedSteps,
|
|
35
|
+
headerContent,
|
|
36
|
+
className,
|
|
37
|
+
}: WizardProps) {
|
|
38
|
+
const [stepHeights, setStepHeights] = React.useState<Map<number, number>>(
|
|
39
|
+
new Map()
|
|
40
|
+
)
|
|
41
|
+
const stepRefs = React.useRef<Map<number, HTMLDivElement | null>>(new Map())
|
|
42
|
+
|
|
43
|
+
React.useEffect(() => {
|
|
44
|
+
const currentElement = stepRefs.current.get(currentStep)
|
|
45
|
+
if (currentElement) {
|
|
46
|
+
setStepHeights((prev) => {
|
|
47
|
+
const newMap = new Map(prev)
|
|
48
|
+
newMap.set(currentStep, currentElement.clientHeight)
|
|
49
|
+
return newMap
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
}, [currentStep, completedSteps])
|
|
53
|
+
|
|
54
|
+
// Add ResizeObserver to monitor size changes
|
|
55
|
+
React.useEffect(() => {
|
|
56
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
57
|
+
entries.forEach((entry) => {
|
|
58
|
+
const stepNumber = Array.from(stepRefs.current.entries()).find(
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
60
|
+
([_, el]) => el === entry.target
|
|
61
|
+
)?.[0]
|
|
62
|
+
|
|
63
|
+
if (stepNumber) {
|
|
64
|
+
setStepHeights((prev) => {
|
|
65
|
+
const newMap = new Map(prev)
|
|
66
|
+
newMap.set(stepNumber, entry.target.clientHeight)
|
|
67
|
+
return newMap
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
// Observe all step elements
|
|
74
|
+
stepRefs.current.forEach((element) => {
|
|
75
|
+
if (element) {
|
|
76
|
+
resizeObserver.observe(element)
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
return () => {
|
|
81
|
+
resizeObserver.disconnect()
|
|
82
|
+
}
|
|
83
|
+
}, []) // Empty dependency array since we want to set up the observer once
|
|
84
|
+
|
|
85
|
+
const getStepStatus = (index: number) => {
|
|
86
|
+
if (completedSteps.includes(index + 1)) return 'completed'
|
|
87
|
+
if (index + 1 === currentStep) return 'current'
|
|
88
|
+
return 'upcoming'
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const trackHeight = useMemo(() => {
|
|
92
|
+
return Array.from(
|
|
93
|
+
{
|
|
94
|
+
length:
|
|
95
|
+
completedSteps.length === steps.length
|
|
96
|
+
? currentStep
|
|
97
|
+
: currentStep - 1,
|
|
98
|
+
},
|
|
99
|
+
(_, i) => i + 1
|
|
100
|
+
)
|
|
101
|
+
.map((step) => {
|
|
102
|
+
const height = stepHeights.get(step) || 0
|
|
103
|
+
// Add the top and bottom padding (py-4 = 1rem * 2 = 32px total)
|
|
104
|
+
return height + 16
|
|
105
|
+
})
|
|
106
|
+
.reduce((acc, curr) => acc + curr, 0)
|
|
107
|
+
}, [currentStep, stepHeights])
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<div className={cn('max-w-screen-x flex flex-col gap-2', className)}>
|
|
111
|
+
<div className="border-b p-6">
|
|
112
|
+
<div className="flex items-center justify-between">
|
|
113
|
+
<div className="space-y-1">
|
|
114
|
+
{headerContent(completedSteps, steps)}
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div className="relative mt-8 pl-3">
|
|
120
|
+
<div className="bg-border absolute top-0 bottom-0 left-7 w-px overflow-y-hidden">
|
|
121
|
+
<div
|
|
122
|
+
className="bg-primary absolute top-0 left-0 w-full transition-all duration-500"
|
|
123
|
+
style={{
|
|
124
|
+
height: trackHeight,
|
|
125
|
+
}}
|
|
126
|
+
/>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<div className="flex flex-col gap-8">
|
|
130
|
+
{steps.map((step, index) => {
|
|
131
|
+
const status = getStepStatus(index)
|
|
132
|
+
const stepNumber = index + 1
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<div
|
|
136
|
+
key={index}
|
|
137
|
+
ref={(el) => stepRefs.current.set(stepNumber, el)}
|
|
138
|
+
>
|
|
139
|
+
<div
|
|
140
|
+
className={cn(
|
|
141
|
+
'relative pl-16',
|
|
142
|
+
status === 'completed' && 'text-body'
|
|
143
|
+
)}
|
|
144
|
+
>
|
|
145
|
+
<div className="flex items-center gap-2">
|
|
146
|
+
<div
|
|
147
|
+
className={cn(
|
|
148
|
+
'absolute left-0 flex h-8 w-8 items-center justify-center rounded-full text-sm font-semibold',
|
|
149
|
+
// TODO: update these to use new color tokens
|
|
150
|
+
status === 'completed' &&
|
|
151
|
+
'bg-success-default text-highlight-fixed-light',
|
|
152
|
+
status === 'current' &&
|
|
153
|
+
'bg-surface-primary-inverse text-default-inverse scale-110',
|
|
154
|
+
status === 'upcoming' && 'bg-muted text-muted'
|
|
155
|
+
)}
|
|
156
|
+
>
|
|
157
|
+
{stepNumber}
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
{/* TODO: is this the best way to handle the opacity? */}
|
|
161
|
+
<div className={cn(status === 'upcoming' && 'opacity-50')}>
|
|
162
|
+
<Heading variant="md" as="h2">
|
|
163
|
+
{step.title}
|
|
164
|
+
</Heading>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
{/* TODO: is this the best way to handle the opacity? */}
|
|
169
|
+
<div
|
|
170
|
+
className={cn(
|
|
171
|
+
'mt-2 mb-4',
|
|
172
|
+
status === 'upcoming' && 'opacity-50'
|
|
173
|
+
)}
|
|
174
|
+
>
|
|
175
|
+
<Text muted>{step.description}</Text>
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
<div
|
|
179
|
+
className={cn(
|
|
180
|
+
'mt-8 flex w-full flex-col gap-5',
|
|
181
|
+
status === 'upcoming' && 'opacity-50'
|
|
182
|
+
)}
|
|
183
|
+
>
|
|
184
|
+
{/* TODO: update this to CodeBlock component */}
|
|
185
|
+
{status === 'current' &&
|
|
186
|
+
step.commands?.map((command, cmdIndex) => (
|
|
187
|
+
<CodeSnippet
|
|
188
|
+
key={cmdIndex}
|
|
189
|
+
code={command.code}
|
|
190
|
+
language={command.language}
|
|
191
|
+
onSelectOrCopy={() =>
|
|
192
|
+
command.onSelectOrCopy?.(command.id)
|
|
193
|
+
}
|
|
194
|
+
copyable
|
|
195
|
+
fontSize="small"
|
|
196
|
+
shimmer={command.active}
|
|
197
|
+
/>
|
|
198
|
+
))}
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
)
|
|
203
|
+
})}
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
)
|
|
208
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ProgrammingLanguage } from '../../types'
|
|
2
|
+
|
|
3
|
+
export interface WizardCommand {
|
|
4
|
+
id: string
|
|
5
|
+
code: string
|
|
6
|
+
language: ProgrammingLanguage
|
|
7
|
+
comment?: string
|
|
8
|
+
active?: boolean
|
|
9
|
+
path?: string
|
|
10
|
+
onSelectOrCopy?: (id: string) => void
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface WizardStep {
|
|
14
|
+
title: string
|
|
15
|
+
description: string
|
|
16
|
+
commands?: WizardCommand[]
|
|
17
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { useRef, useState, useEffect } from 'react'
|
|
2
|
+
import { Org } from '.'
|
|
3
|
+
import { Command } from '../Command'
|
|
4
|
+
import { Stack } from '../Stack'
|
|
5
|
+
import { GradientCircle } from '../GradientCircle'
|
|
6
|
+
import { Separator } from '../Separator'
|
|
7
|
+
import { Text } from '../Text'
|
|
8
|
+
import { Button } from '../Button'
|
|
9
|
+
import { Heading } from '../../index'
|
|
10
|
+
|
|
11
|
+
interface CreateOrgProps {
|
|
12
|
+
onSubmit: (name: string) => Promise<Org>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function CreateOrg({ onSubmit }: CreateOrgProps) {
|
|
16
|
+
const [companyName, setCompanyName] = useState('')
|
|
17
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (inputRef.current) {
|
|
21
|
+
inputRef.current.focus()
|
|
22
|
+
}
|
|
23
|
+
}, [])
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<Command className="relative">
|
|
27
|
+
<div className="flex h-full w-full flex-row items-center">
|
|
28
|
+
<div className="flex w-1/3 flex-col items-center justify-center gap-4 px-8 text-center">
|
|
29
|
+
<div className="flex flex-col items-center justify-center gap-4">
|
|
30
|
+
<GradientCircle
|
|
31
|
+
name={companyName}
|
|
32
|
+
size="2xl"
|
|
33
|
+
transition
|
|
34
|
+
showInitial
|
|
35
|
+
/>
|
|
36
|
+
<Stack align="center" gap={2}>
|
|
37
|
+
<Heading variant="xl">Create new company</Heading>
|
|
38
|
+
<div className="max-w-64">
|
|
39
|
+
<Text muted>
|
|
40
|
+
Once you have created your company, we'll take you through
|
|
41
|
+
some onboarding steps to get you started with Speakeasy.
|
|
42
|
+
</Text>
|
|
43
|
+
</div>
|
|
44
|
+
</Stack>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<Separator orientation="vertical" />
|
|
49
|
+
|
|
50
|
+
<div className="flex w-2/3 flex-col items-center justify-center px-8">
|
|
51
|
+
<div className="flex max-w-lg flex-col">
|
|
52
|
+
<div className="flex flex-col gap-4">
|
|
53
|
+
<Stack align="start" gap={2}>
|
|
54
|
+
<Text variant="lg">Enter your company name</Text>
|
|
55
|
+
<Text muted>
|
|
56
|
+
We will automatically generate a unique, URL-friendly slug
|
|
57
|
+
based off your company name.
|
|
58
|
+
</Text>
|
|
59
|
+
</Stack>
|
|
60
|
+
</div>
|
|
61
|
+
<div className="flex flex-col">
|
|
62
|
+
<div className="focus-within:outline-muted/50 shadow-muted bg-input/10 border-neutral-softest ease-in-out-expo mt-5 flex w-full max-w-[660px] flex-row items-center justify-stretch gap-2 rounded-md border px-4 py-1 transition-[border-color] duration-500 focus-within:shadow-sm focus-within:outline focus-within:outline-offset-0 data-[invalid=true]:border-red-400/75">
|
|
63
|
+
<div className="flex w-full">
|
|
64
|
+
<input
|
|
65
|
+
type="text"
|
|
66
|
+
role="textbox"
|
|
67
|
+
name="companyName"
|
|
68
|
+
pattern="^[a-z0-9]+(?:-[a-z0-9]+)*$"
|
|
69
|
+
ref={inputRef}
|
|
70
|
+
placeholder="Your company name"
|
|
71
|
+
value={companyName}
|
|
72
|
+
onChange={(e) => setCompanyName(e.target.value)}
|
|
73
|
+
className="border-neutral-softest text-foreground/80 placeholder:text-muted-foreground/50 ring-offset-background text-md flex h-10 w-full min-w-fit flex-1 flex-grow bg-transparent px-2 py-1.5 pl-0 text-lg outline-none"
|
|
74
|
+
/>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div className="border-neutral-softest bg-background flex border-t px-8 py-4">
|
|
83
|
+
<div className="ml-auto">
|
|
84
|
+
<Button
|
|
85
|
+
variant="secondary"
|
|
86
|
+
disabled={!companyName}
|
|
87
|
+
onClick={() => onSubmit(companyName)}
|
|
88
|
+
>
|
|
89
|
+
Next
|
|
90
|
+
</Button>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</Command>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useState } from 'react'
|
|
2
|
+
import { Org } from '.'
|
|
3
|
+
import { Command } from '../Command'
|
|
4
|
+
import { Text } from '../Text'
|
|
5
|
+
import { Icon } from '../Icon'
|
|
6
|
+
import { Button, Heading, Stack } from '../../index'
|
|
7
|
+
import { Separator } from '../Separator'
|
|
8
|
+
import { GradientCircle } from '../GradientCircle'
|
|
9
|
+
|
|
10
|
+
import { cn } from '../../lib/utils'
|
|
11
|
+
import { OrgSelector } from './OrgSelector'
|
|
12
|
+
|
|
13
|
+
export interface CreateResult {
|
|
14
|
+
success: boolean
|
|
15
|
+
error?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface CreateWorkspaceProps {
|
|
19
|
+
open: boolean
|
|
20
|
+
selectedOrg: Org
|
|
21
|
+
allOrgs: Org[]
|
|
22
|
+
onBack?: () => void
|
|
23
|
+
onSubmit: (org: Org, workspaceName: string) => Promise<CreateResult>
|
|
24
|
+
newWorkspaceName: string
|
|
25
|
+
setNewWorkspaceName: (name: string) => void
|
|
26
|
+
backButtonEnabled?: boolean
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function CreateWorkspace({
|
|
30
|
+
open,
|
|
31
|
+
selectedOrg,
|
|
32
|
+
allOrgs,
|
|
33
|
+
onBack,
|
|
34
|
+
onSubmit,
|
|
35
|
+
newWorkspaceName,
|
|
36
|
+
setNewWorkspaceName,
|
|
37
|
+
backButtonEnabled = true,
|
|
38
|
+
}: CreateWorkspaceProps) {
|
|
39
|
+
const createInputRef = React.useRef<HTMLInputElement>(null)
|
|
40
|
+
const [isInvalid, setIsInvalid] = useState(false)
|
|
41
|
+
const [currentOrg, setCurrentOrg] = useState(selectedOrg)
|
|
42
|
+
const [isSubmitting, setIsSubmitting] = useState(false)
|
|
43
|
+
const [error, setError] = useState<string | null>(null)
|
|
44
|
+
|
|
45
|
+
React.useEffect(() => {
|
|
46
|
+
if (open) {
|
|
47
|
+
setTimeout(() => {
|
|
48
|
+
createInputRef.current?.focus()
|
|
49
|
+
}, 300)
|
|
50
|
+
}
|
|
51
|
+
}, [open])
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
// Focus the input when the org changes
|
|
55
|
+
focusInput()
|
|
56
|
+
}, [currentOrg])
|
|
57
|
+
|
|
58
|
+
const focusInput = () => {
|
|
59
|
+
createInputRef.current?.focus()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const handleSubmit = async () => {
|
|
63
|
+
setIsSubmitting(true)
|
|
64
|
+
const result = await onSubmit(currentOrg, newWorkspaceName)
|
|
65
|
+
setIsSubmitting(false)
|
|
66
|
+
if (!result.success) {
|
|
67
|
+
setError(result.error ?? 'Unknown error')
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const handleChange = useCallback(
|
|
72
|
+
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
73
|
+
setNewWorkspaceName(e.target.value)
|
|
74
|
+
setIsInvalid(!e.target.validity.valid)
|
|
75
|
+
},
|
|
76
|
+
[setNewWorkspaceName, setIsInvalid]
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<Command className="relative">
|
|
81
|
+
<div className="flex h-full w-full flex-row items-center">
|
|
82
|
+
<div className="flex w-1/3 flex-col items-center justify-center gap-4 px-8 text-center">
|
|
83
|
+
<div className="flex flex-col items-center justify-center gap-4">
|
|
84
|
+
<GradientCircle name={currentOrg.label} size="2xl" showInitial />
|
|
85
|
+
<Stack align="center" gap={2}>
|
|
86
|
+
<Heading variant="xl">Create new workspace</Heading>
|
|
87
|
+
<div className="max-w-64">
|
|
88
|
+
<Text muted>
|
|
89
|
+
Workspaces are used to organize your SDK targets into logical
|
|
90
|
+
groups.
|
|
91
|
+
</Text>
|
|
92
|
+
</div>
|
|
93
|
+
</Stack>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<Separator orientation="vertical" />
|
|
98
|
+
|
|
99
|
+
<div className="flex w-2/3 flex-col items-center justify-center px-8">
|
|
100
|
+
<div className="flex max-w-lg flex-col">
|
|
101
|
+
<div className="flex flex-col gap-4">
|
|
102
|
+
<Stack align="start" gap={2}>
|
|
103
|
+
<Text variant="lg">Choose your workspace name</Text>
|
|
104
|
+
<Text muted>
|
|
105
|
+
Enter a name for your new workspace. Names must be in slug
|
|
106
|
+
format; only lowercase letters, numbers, and hyphens are
|
|
107
|
+
allowed.
|
|
108
|
+
</Text>
|
|
109
|
+
</Stack>
|
|
110
|
+
</div>
|
|
111
|
+
<div className="flex flex-col">
|
|
112
|
+
<div
|
|
113
|
+
className="focus-within:outline-muted/50 shadow-muted bg-input/10 border-neutral-softest ease-in-out-expo mt-5 flex w-full max-w-[660px] flex-row items-center justify-stretch gap-2 rounded-md border px-4 py-1 transition-[border-color] duration-500 focus-within:shadow-sm focus-within:outline focus-within:outline-1 focus-within:outline-offset-0 data-[invalid=true]:border-red-400/75"
|
|
114
|
+
onClick={focusInput}
|
|
115
|
+
data-invalid={isInvalid}
|
|
116
|
+
>
|
|
117
|
+
{allOrgs.length > 1 ? (
|
|
118
|
+
<OrgSelector
|
|
119
|
+
orgs={allOrgs}
|
|
120
|
+
selectedOrg={currentOrg}
|
|
121
|
+
onSelect={setCurrentOrg}
|
|
122
|
+
/>
|
|
123
|
+
) : (
|
|
124
|
+
<span
|
|
125
|
+
title={currentOrg.slug}
|
|
126
|
+
className={cn(
|
|
127
|
+
'text-foreground/80 text-lg font-semibold whitespace-pre select-none',
|
|
128
|
+
currentOrg.slug.length > 20 &&
|
|
129
|
+
'max-w-40 truncate whitespace-pre'
|
|
130
|
+
)}
|
|
131
|
+
>
|
|
132
|
+
{currentOrg.slug}
|
|
133
|
+
</span>
|
|
134
|
+
)}
|
|
135
|
+
<span className="text-muted-foreground/50 mx-2 text-lg select-none">
|
|
136
|
+
/
|
|
137
|
+
</span>
|
|
138
|
+
<div className="flex w-full">
|
|
139
|
+
<input
|
|
140
|
+
ref={createInputRef}
|
|
141
|
+
type="text"
|
|
142
|
+
pattern="^[a-z0-9]+(?:-[a-z0-9]+)*$"
|
|
143
|
+
role="textbox"
|
|
144
|
+
name="workspaceName"
|
|
145
|
+
placeholder="your-new-workspace"
|
|
146
|
+
value={newWorkspaceName}
|
|
147
|
+
onChange={handleChange}
|
|
148
|
+
className="border-neutral-softest text-foreground/80 placeholder:text-muted-foreground/50 ring-offset-background text-md flex h-10 w-full min-w-fit flex-1 flex-grow bg-transparent px-2 py-1.5 pl-0 text-lg outline-none"
|
|
149
|
+
/>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<div className="mt-2.5 min-h-6 self-start">
|
|
154
|
+
{newWorkspaceName &&
|
|
155
|
+
!newWorkspaceName.match(/^[a-z0-9]+(?:-[a-z0-9]+)*$/) && (
|
|
156
|
+
<div className="text-sm text-red-400/75">
|
|
157
|
+
Workspace names can only contain lowercase letters,
|
|
158
|
+
numbers, and hyphens
|
|
159
|
+
</div>
|
|
160
|
+
)}
|
|
161
|
+
{error && (
|
|
162
|
+
<div className="text-sm text-red-400/75">{error}</div>
|
|
163
|
+
)}
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<div className="border-neutral-softest bg-background flex border-t px-8 py-4">
|
|
171
|
+
{backButtonEnabled && onBack && (
|
|
172
|
+
<Button variant="secondary" onClick={onBack}>
|
|
173
|
+
<Icon name="chevron-left" size="small" />
|
|
174
|
+
Back
|
|
175
|
+
</Button>
|
|
176
|
+
)}
|
|
177
|
+
<div className="ml-auto">
|
|
178
|
+
<Button
|
|
179
|
+
variant="secondary"
|
|
180
|
+
disabled={
|
|
181
|
+
!newWorkspaceName ||
|
|
182
|
+
!newWorkspaceName.match(/^[a-z0-9]+(?:-[a-z0-9]+)*$/)
|
|
183
|
+
}
|
|
184
|
+
onClick={handleSubmit}
|
|
185
|
+
>
|
|
186
|
+
{isSubmitting ? (
|
|
187
|
+
<Icon name="loader" className="animate-spin" />
|
|
188
|
+
) : (
|
|
189
|
+
'Create'
|
|
190
|
+
)}
|
|
191
|
+
</Button>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
</Command>
|
|
195
|
+
)
|
|
196
|
+
}
|