@carto/ps-react-ui 4.3.10 → 4.4.0-chat-ui.0
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/dist/chat.js +798 -0
- package/dist/chat.js.map +1 -0
- package/dist/components.js +260 -240
- package/dist/components.js.map +1 -1
- package/dist/types/chat/bubbles/chat-error-message.d.ts +2 -0
- package/dist/types/chat/bubbles/chat-suggestion-button.d.ts +2 -0
- package/dist/types/chat/bubbles/chat-user-message.d.ts +2 -0
- package/dist/types/chat/bubbles/index.d.ts +4 -0
- package/dist/types/chat/const.d.ts +3 -0
- package/dist/types/chat/containers/chat-content.d.ts +2 -0
- package/dist/types/chat/containers/chat-footer.d.ts +2 -0
- package/dist/types/chat/containers/chat-header.d.ts +2 -0
- package/dist/types/chat/containers/chat-starter.d.ts +2 -0
- package/dist/types/chat/containers/index.d.ts +4 -0
- package/dist/types/chat/containers/styles.d.ts +101 -0
- package/dist/types/chat/feedback/chat-loader.d.ts +2 -0
- package/dist/types/chat/feedback/chat-rating-action.d.ts +2 -0
- package/dist/types/chat/feedback/chat-thinking.d.ts +2 -0
- package/dist/types/chat/feedback/chat-tools.d.ts +2 -0
- package/dist/types/chat/feedback/index.d.ts +5 -0
- package/dist/types/chat/feedback/styles.d.ts +65 -0
- package/dist/types/chat/index.d.ts +16 -0
- package/dist/types/chat/types.d.ts +99 -0
- package/dist/types/components/copy-button/copy-button.d.ts +2 -0
- package/dist/types/components/copy-button/types.d.ts +6 -0
- package/dist/types/components/index.d.ts +2 -0
- package/dist/widgets/actions.js +22 -21
- package/dist/widgets/actions.js.map +1 -1
- package/dist/widgets/bar.js +7 -6
- package/dist/widgets/bar.js.map +1 -1
- package/dist/widgets/category.js +9 -8
- package/dist/widgets/category.js.map +1 -1
- package/dist/widgets/formula.js +11 -10
- package/dist/widgets/formula.js.map +1 -1
- package/dist/widgets/histogram.js +7 -6
- package/dist/widgets/histogram.js.map +1 -1
- package/dist/widgets/markdown.js +9 -8
- package/dist/widgets/markdown.js.map +1 -1
- package/dist/widgets/pie.js +7 -6
- package/dist/widgets/pie.js.map +1 -1
- package/dist/widgets/scatterplot.js +7 -6
- package/dist/widgets/scatterplot.js.map +1 -1
- package/dist/widgets/spread.js +57 -56
- package/dist/widgets/spread.js.map +1 -1
- package/dist/widgets/table.js +67 -66
- package/dist/widgets/table.js.map +1 -1
- package/dist/widgets/timeseries.js +23 -22
- package/dist/widgets/timeseries.js.map +1 -1
- package/dist/widgets/wrapper.js +21 -20
- package/dist/widgets/wrapper.js.map +1 -1
- package/package.json +7 -3
- package/src/chat/bubbles/chat-agent-message.test.tsx +30 -0
- package/src/chat/bubbles/chat-agent-message.tsx +11 -0
- package/src/chat/bubbles/chat-error-message.test.tsx +40 -0
- package/src/chat/bubbles/chat-error-message.tsx +47 -0
- package/src/chat/bubbles/chat-suggestion-button.test.tsx +24 -0
- package/src/chat/bubbles/chat-suggestion-button.tsx +27 -0
- package/src/chat/bubbles/chat-user-message.test.tsx +27 -0
- package/src/chat/bubbles/chat-user-message.tsx +27 -0
- package/src/chat/bubbles/index.ts +4 -0
- package/src/chat/bubbles/styles.ts +106 -0
- package/src/chat/const.ts +3 -0
- package/src/chat/containers/chat-content.test.tsx +15 -0
- package/src/chat/containers/chat-content.tsx +32 -0
- package/src/chat/containers/chat-footer.test.tsx +34 -0
- package/src/chat/containers/chat-footer.tsx +78 -0
- package/src/chat/containers/chat-header.test.tsx +28 -0
- package/src/chat/containers/chat-header.tsx +29 -0
- package/src/chat/containers/chat-starter.test.tsx +32 -0
- package/src/chat/containers/chat-starter.tsx +75 -0
- package/src/chat/containers/index.ts +4 -0
- package/src/chat/containers/styles.ts +107 -0
- package/src/chat/feedback/chat-actions-container.test.tsx +64 -0
- package/src/chat/feedback/chat-actions-container.tsx +7 -0
- package/src/chat/feedback/chat-loader.test.tsx +10 -0
- package/src/chat/feedback/chat-loader.tsx +31 -0
- package/src/chat/feedback/chat-rating-action.tsx +43 -0
- package/src/chat/feedback/chat-thinking.test.tsx +15 -0
- package/src/chat/feedback/chat-thinking.tsx +23 -0
- package/src/chat/feedback/chat-tools.test.tsx +23 -0
- package/src/chat/feedback/chat-tools.tsx +54 -0
- package/src/chat/feedback/index.ts +5 -0
- package/src/chat/feedback/styles.ts +80 -0
- package/src/chat/index.ts +45 -0
- package/src/chat/types.ts +124 -0
- package/src/components/copy-button/copy-button.test.tsx +41 -0
- package/src/components/copy-button/copy-button.tsx +31 -0
- package/src/components/copy-button/types.ts +10 -0
- package/src/components/index.ts +3 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Accordion,
|
|
3
|
+
AccordionDetails,
|
|
4
|
+
AccordionSummary,
|
|
5
|
+
Box,
|
|
6
|
+
CircularProgress,
|
|
7
|
+
Typography,
|
|
8
|
+
} from '@mui/material'
|
|
9
|
+
import {
|
|
10
|
+
CheckCircleOutline,
|
|
11
|
+
ErrorOutline,
|
|
12
|
+
ExpandMore,
|
|
13
|
+
} from '@mui/icons-material'
|
|
14
|
+
import type { ChatToolsProps, ChatToolItem } from '../types'
|
|
15
|
+
import { ChatThinking } from './chat-thinking'
|
|
16
|
+
import { styles } from './styles'
|
|
17
|
+
|
|
18
|
+
function ToolStatusIcon({ status }: { status: ChatToolItem['status'] }) {
|
|
19
|
+
switch (status) {
|
|
20
|
+
case 'loading':
|
|
21
|
+
return <CircularProgress size={16} />
|
|
22
|
+
case 'thinking':
|
|
23
|
+
return <ChatThinking>...</ChatThinking>
|
|
24
|
+
case 'complete':
|
|
25
|
+
return <CheckCircleOutline fontSize='small' color='success' />
|
|
26
|
+
case 'error':
|
|
27
|
+
return <ErrorOutline fontSize='small' color='error' />
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function ChatTools({ tools, labels = {}, sx }: ChatToolsProps) {
|
|
32
|
+
return (
|
|
33
|
+
<Box sx={{ ...styles.tools, ...sx }}>
|
|
34
|
+
{labels.title && (
|
|
35
|
+
<Typography variant='caption' color='text.secondary' sx={{ mb: 1 }}>
|
|
36
|
+
{labels.title}
|
|
37
|
+
</Typography>
|
|
38
|
+
)}
|
|
39
|
+
{tools.map((tool) => (
|
|
40
|
+
<Accordion key={tool.id} disableGutters elevation={0}>
|
|
41
|
+
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
42
|
+
<Box sx={styles.toolHeader}>
|
|
43
|
+
<Box sx={styles.toolStatusIcon}>
|
|
44
|
+
<ToolStatusIcon status={tool.status} />
|
|
45
|
+
</Box>
|
|
46
|
+
<Typography variant='body2'>{tool.name}</Typography>
|
|
47
|
+
</Box>
|
|
48
|
+
</AccordionSummary>
|
|
49
|
+
{tool.content && <AccordionDetails>{tool.content}</AccordionDetails>}
|
|
50
|
+
</Accordion>
|
|
51
|
+
))}
|
|
52
|
+
</Box>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type SxProps, type Theme } from '@mui/material'
|
|
2
|
+
import { keyframes } from '@mui/material/styles'
|
|
3
|
+
|
|
4
|
+
const shimmer = keyframes`
|
|
5
|
+
0% {
|
|
6
|
+
background-position: 150% 0;
|
|
7
|
+
}
|
|
8
|
+
100% {
|
|
9
|
+
background-position: -50% 0;
|
|
10
|
+
}
|
|
11
|
+
`
|
|
12
|
+
|
|
13
|
+
const breatheOuter = keyframes`
|
|
14
|
+
0% { transform: scale(0.68); opacity: 0.7; }
|
|
15
|
+
40% { transform: scale(1); opacity: 1; }
|
|
16
|
+
80% { transform: scale(0.6); opacity: 0.65; }
|
|
17
|
+
100% { transform: scale(0.68); opacity: 0.7; }
|
|
18
|
+
`
|
|
19
|
+
|
|
20
|
+
const breatheInner = keyframes`
|
|
21
|
+
0% { transform: scale(1); }
|
|
22
|
+
40% { transform: scale(0.75); }
|
|
23
|
+
80% { transform: scale(1.125); }
|
|
24
|
+
100% { transform: scale(1); }
|
|
25
|
+
`
|
|
26
|
+
|
|
27
|
+
export const styles = {
|
|
28
|
+
thinking: {
|
|
29
|
+
display: 'flex',
|
|
30
|
+
alignItems: 'center',
|
|
31
|
+
minHeight: ({ spacing }) => spacing(3),
|
|
32
|
+
background: ({ palette }) => `linear-gradient(
|
|
33
|
+
90deg,
|
|
34
|
+
${palette.text.disabled} 25%,
|
|
35
|
+
${palette.text.secondary} 50%,
|
|
36
|
+
${palette.text.disabled} 75%
|
|
37
|
+
)`,
|
|
38
|
+
backgroundSize: '200% 100%',
|
|
39
|
+
backgroundClip: 'text',
|
|
40
|
+
WebkitBackgroundClip: 'text',
|
|
41
|
+
WebkitTextFillColor: 'transparent',
|
|
42
|
+
animation: `${shimmer} 2s ease-in-out infinite`,
|
|
43
|
+
animationDuration: '2s',
|
|
44
|
+
},
|
|
45
|
+
loader: {
|
|
46
|
+
display: 'flex',
|
|
47
|
+
alignItems: 'center',
|
|
48
|
+
justifyContent: 'center',
|
|
49
|
+
position: 'relative',
|
|
50
|
+
p: 1,
|
|
51
|
+
},
|
|
52
|
+
loaderOuterCircle: {
|
|
53
|
+
position: 'absolute',
|
|
54
|
+
inset: 0,
|
|
55
|
+
margin: 'auto',
|
|
56
|
+
borderRadius: '50%',
|
|
57
|
+
backgroundColor: ({ palette }) => palette.action.disabled,
|
|
58
|
+
animation: `${breatheOuter} 1s ease-in-out infinite`,
|
|
59
|
+
},
|
|
60
|
+
loaderInnerCircle: {
|
|
61
|
+
position: 'absolute',
|
|
62
|
+
inset: 0,
|
|
63
|
+
margin: 'auto',
|
|
64
|
+
borderRadius: '50%',
|
|
65
|
+
backgroundColor: ({ palette }) => palette.text.primary,
|
|
66
|
+
animation: `${breatheInner} 1s ease-in-out infinite`,
|
|
67
|
+
},
|
|
68
|
+
tools: {
|
|
69
|
+
width: '100%',
|
|
70
|
+
},
|
|
71
|
+
toolHeader: {
|
|
72
|
+
display: 'flex',
|
|
73
|
+
alignItems: 'center',
|
|
74
|
+
gap: ({ spacing }) => spacing(1),
|
|
75
|
+
},
|
|
76
|
+
toolStatusIcon: {
|
|
77
|
+
display: 'flex',
|
|
78
|
+
alignItems: 'center',
|
|
79
|
+
},
|
|
80
|
+
} satisfies Record<string, SxProps<Theme>>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
export type {
|
|
3
|
+
ChatSxProps,
|
|
4
|
+
ChatErrorAction,
|
|
5
|
+
ChatUserMessageProps,
|
|
6
|
+
ChatAgentMessageProps,
|
|
7
|
+
ChatErrorMessageProps,
|
|
8
|
+
ChatSuggestionButtonProps,
|
|
9
|
+
ChatThinkingProps,
|
|
10
|
+
ChatLoaderProps,
|
|
11
|
+
ChatContentProps,
|
|
12
|
+
ChatHeaderProps,
|
|
13
|
+
ChatFooterProps,
|
|
14
|
+
ChatStarterItem,
|
|
15
|
+
ChatStarterProps,
|
|
16
|
+
ChatActionsContainerProps,
|
|
17
|
+
ChatRatingActionProps,
|
|
18
|
+
ChatToolItem,
|
|
19
|
+
ChatToolsProps,
|
|
20
|
+
} from './types'
|
|
21
|
+
|
|
22
|
+
// Constants
|
|
23
|
+
export { CHAT_MAX_WIDTH, CHAT_SCROLL_DELAY, CHAT_DIVIDER_DELAY } from './const'
|
|
24
|
+
|
|
25
|
+
// Messages
|
|
26
|
+
export { ChatUserMessage } from './bubbles/chat-user-message'
|
|
27
|
+
export { ChatAgentMessage } from './bubbles/chat-agent-message'
|
|
28
|
+
export { ChatErrorMessage } from './bubbles/chat-error-message'
|
|
29
|
+
export { ChatSuggestionButton } from './bubbles/chat-suggestion-button'
|
|
30
|
+
export { ChatMessageOverflow } from './bubbles/styles'
|
|
31
|
+
|
|
32
|
+
// Feedback
|
|
33
|
+
export { ChatThinking } from './feedback/chat-thinking'
|
|
34
|
+
export { ChatLoader } from './feedback/chat-loader'
|
|
35
|
+
|
|
36
|
+
// Containers
|
|
37
|
+
export { ChatContent } from './containers/chat-content'
|
|
38
|
+
export { ChatHeader } from './containers/chat-header'
|
|
39
|
+
export { ChatFooter } from './containers/chat-footer'
|
|
40
|
+
export { ChatStarter } from './containers/chat-starter'
|
|
41
|
+
|
|
42
|
+
// Feedback
|
|
43
|
+
export { ChatActionsContainer } from './feedback/chat-actions-container'
|
|
44
|
+
export { ChatRatingAction } from './feedback/chat-rating-action'
|
|
45
|
+
export { ChatTools } from './feedback/chat-tools'
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import type { ButtonBaseProps, SxProps, Theme } from '@mui/material'
|
|
3
|
+
|
|
4
|
+
// === Shared base props ===
|
|
5
|
+
export interface ChatSxProps {
|
|
6
|
+
sx?: SxProps<Theme>
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// === Error types ===
|
|
10
|
+
export interface ChatErrorAction {
|
|
11
|
+
label: string
|
|
12
|
+
onClick: () => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// === Message props ===
|
|
16
|
+
export interface ChatUserMessageProps extends ChatSxProps {
|
|
17
|
+
children: ReactNode
|
|
18
|
+
/** enabled to render text with a lighter color for indicating things like an error sending the message */
|
|
19
|
+
muted?: boolean
|
|
20
|
+
/** content to render on top of the message for user attachments */
|
|
21
|
+
topContext?: ReactNode
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ChatAgentMessageProps extends ChatSxProps {
|
|
25
|
+
children: ReactNode
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ChatErrorMessageProps extends ChatSxProps {
|
|
29
|
+
errors: string[]
|
|
30
|
+
icon?: ReactNode
|
|
31
|
+
actions?: ChatErrorAction[]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ChatSuggestionButtonProps
|
|
35
|
+
extends ChatSxProps, Omit<ButtonBaseProps, 'children'> {
|
|
36
|
+
label: ReactNode
|
|
37
|
+
color?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// === Feedback props ===
|
|
41
|
+
export interface ChatThinkingProps extends ChatSxProps {
|
|
42
|
+
duration?: number
|
|
43
|
+
children?: ReactNode
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ChatLoaderProps extends ChatSxProps {
|
|
47
|
+
size?: number
|
|
48
|
+
labels?: {
|
|
49
|
+
loading?: string
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// === Layout props ===
|
|
54
|
+
export interface ChatContentProps extends ChatSxProps {
|
|
55
|
+
children: ReactNode
|
|
56
|
+
labels?: {
|
|
57
|
+
jumpToLatest?: string
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// === Container props ===
|
|
62
|
+
export interface ChatHeaderProps extends ChatSxProps {
|
|
63
|
+
leftSlot?: ReactNode
|
|
64
|
+
title: ReactNode
|
|
65
|
+
rightSlot?: ReactNode
|
|
66
|
+
onClose?: () => void
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ChatFooterProps extends ChatSxProps {
|
|
70
|
+
value: string
|
|
71
|
+
onChange: (value: string) => void
|
|
72
|
+
onSend: () => void
|
|
73
|
+
onStop?: () => void
|
|
74
|
+
isGenerating?: boolean
|
|
75
|
+
disabled?: boolean
|
|
76
|
+
placeholder?: string
|
|
77
|
+
labels?: {
|
|
78
|
+
send?: string
|
|
79
|
+
stop?: string
|
|
80
|
+
}
|
|
81
|
+
caption?: ReactNode
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// === Extras props ===
|
|
85
|
+
export interface ChatStarterItem {
|
|
86
|
+
label: string
|
|
87
|
+
color?: string
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface ChatStarterProps extends ChatSxProps {
|
|
91
|
+
icon?: ReactNode
|
|
92
|
+
title?: ReactNode
|
|
93
|
+
description?: ReactNode
|
|
94
|
+
items: string[] | ChatStarterItem[]
|
|
95
|
+
size?: 'small' | 'medium'
|
|
96
|
+
onSelect?: (prompt: string) => void
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface ChatActionsContainerProps extends ChatSxProps {
|
|
100
|
+
children: ReactNode
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface ChatRatingActionProps {
|
|
104
|
+
onRatingChange?: (rating: 'up' | 'down' | null) => void
|
|
105
|
+
rating?: 'up' | 'down' | null
|
|
106
|
+
labels?: {
|
|
107
|
+
thumbUp?: string
|
|
108
|
+
thumbDown?: string
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface ChatToolItem {
|
|
113
|
+
id: string
|
|
114
|
+
name: string
|
|
115
|
+
status: 'loading' | 'thinking' | 'complete' | 'error'
|
|
116
|
+
content?: ReactNode
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface ChatToolsProps extends ChatSxProps {
|
|
120
|
+
tools: ChatToolItem[]
|
|
121
|
+
labels?: {
|
|
122
|
+
title?: string
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, test, expect, vi } from 'vitest'
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react'
|
|
3
|
+
import { CopyButton } from './copy-button'
|
|
4
|
+
|
|
5
|
+
vi.mock('@carto/ps-utils', () => ({
|
|
6
|
+
copy: vi.fn(() => Promise.resolve()),
|
|
7
|
+
}))
|
|
8
|
+
|
|
9
|
+
describe('CopyButton', () => {
|
|
10
|
+
test('renders copy button', () => {
|
|
11
|
+
render(
|
|
12
|
+
<CopyButton copyText='hello' onSuccess={vi.fn()} onError={vi.fn()} />,
|
|
13
|
+
)
|
|
14
|
+
expect(screen.getByRole('button')).toBeTruthy()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('renders with custom aria-label', () => {
|
|
18
|
+
render(
|
|
19
|
+
<CopyButton
|
|
20
|
+
copyText='hello'
|
|
21
|
+
onSuccess={vi.fn()}
|
|
22
|
+
onError={vi.fn()}
|
|
23
|
+
aria-label='Copy response'
|
|
24
|
+
/>,
|
|
25
|
+
)
|
|
26
|
+
expect(screen.getByLabelText('Copy response')).toBeTruthy()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('calls copy utility when clicked', async () => {
|
|
30
|
+
const { copy } = await import('@carto/ps-utils')
|
|
31
|
+
render(
|
|
32
|
+
<CopyButton
|
|
33
|
+
copyText='hello world'
|
|
34
|
+
onSuccess={vi.fn()}
|
|
35
|
+
onError={vi.fn()}
|
|
36
|
+
/>,
|
|
37
|
+
)
|
|
38
|
+
fireEvent.click(screen.getByRole('button'))
|
|
39
|
+
expect(copy).toHaveBeenCalledWith('hello world')
|
|
40
|
+
})
|
|
41
|
+
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { IconButton } from '@mui/material'
|
|
2
|
+
import { ContentCopyOutlined } from '@mui/icons-material'
|
|
3
|
+
import type { CopyButtonProps } from './types'
|
|
4
|
+
import { copy } from '@carto/ps-utils'
|
|
5
|
+
|
|
6
|
+
export function CopyButton({
|
|
7
|
+
copyText,
|
|
8
|
+
onSuccess,
|
|
9
|
+
onError,
|
|
10
|
+
'aria-label': ariaLabel,
|
|
11
|
+
...props
|
|
12
|
+
}: CopyButtonProps) {
|
|
13
|
+
async function onCopy() {
|
|
14
|
+
try {
|
|
15
|
+
await copy(copyText)
|
|
16
|
+
onSuccess?.()
|
|
17
|
+
} catch (err) {
|
|
18
|
+
onError?.(err as Error)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return (
|
|
22
|
+
<IconButton
|
|
23
|
+
size='small'
|
|
24
|
+
onClick={() => void onCopy()}
|
|
25
|
+
aria-label={ariaLabel ?? 'Copy'}
|
|
26
|
+
{...props}
|
|
27
|
+
>
|
|
28
|
+
<ContentCopyOutlined fontSize='small' />
|
|
29
|
+
</IconButton>
|
|
30
|
+
)
|
|
31
|
+
}
|
package/src/components/index.ts
CHANGED
|
@@ -62,3 +62,6 @@ export type { BasemapsUIProps } from './basemaps/types'
|
|
|
62
62
|
|
|
63
63
|
export { Tooltip, setTooltipEnterDelay } from './tooltip/tooltip'
|
|
64
64
|
export { SmartTooltip } from './smart-tooltip/smart-tooltip'
|
|
65
|
+
|
|
66
|
+
export type { CopyButtonProps } from './copy-button/types'
|
|
67
|
+
export { CopyButton } from './copy-button/copy-button'
|