@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.
Files changed (89) hide show
  1. package/dist/chat.js +798 -0
  2. package/dist/chat.js.map +1 -0
  3. package/dist/components.js +260 -240
  4. package/dist/components.js.map +1 -1
  5. package/dist/types/chat/bubbles/chat-error-message.d.ts +2 -0
  6. package/dist/types/chat/bubbles/chat-suggestion-button.d.ts +2 -0
  7. package/dist/types/chat/bubbles/chat-user-message.d.ts +2 -0
  8. package/dist/types/chat/bubbles/index.d.ts +4 -0
  9. package/dist/types/chat/const.d.ts +3 -0
  10. package/dist/types/chat/containers/chat-content.d.ts +2 -0
  11. package/dist/types/chat/containers/chat-footer.d.ts +2 -0
  12. package/dist/types/chat/containers/chat-header.d.ts +2 -0
  13. package/dist/types/chat/containers/chat-starter.d.ts +2 -0
  14. package/dist/types/chat/containers/index.d.ts +4 -0
  15. package/dist/types/chat/containers/styles.d.ts +101 -0
  16. package/dist/types/chat/feedback/chat-loader.d.ts +2 -0
  17. package/dist/types/chat/feedback/chat-rating-action.d.ts +2 -0
  18. package/dist/types/chat/feedback/chat-thinking.d.ts +2 -0
  19. package/dist/types/chat/feedback/chat-tools.d.ts +2 -0
  20. package/dist/types/chat/feedback/index.d.ts +5 -0
  21. package/dist/types/chat/feedback/styles.d.ts +65 -0
  22. package/dist/types/chat/index.d.ts +16 -0
  23. package/dist/types/chat/types.d.ts +99 -0
  24. package/dist/types/components/copy-button/copy-button.d.ts +2 -0
  25. package/dist/types/components/copy-button/types.d.ts +6 -0
  26. package/dist/types/components/index.d.ts +2 -0
  27. package/dist/widgets/actions.js +22 -21
  28. package/dist/widgets/actions.js.map +1 -1
  29. package/dist/widgets/bar.js +7 -6
  30. package/dist/widgets/bar.js.map +1 -1
  31. package/dist/widgets/category.js +9 -8
  32. package/dist/widgets/category.js.map +1 -1
  33. package/dist/widgets/formula.js +11 -10
  34. package/dist/widgets/formula.js.map +1 -1
  35. package/dist/widgets/histogram.js +7 -6
  36. package/dist/widgets/histogram.js.map +1 -1
  37. package/dist/widgets/markdown.js +9 -8
  38. package/dist/widgets/markdown.js.map +1 -1
  39. package/dist/widgets/pie.js +7 -6
  40. package/dist/widgets/pie.js.map +1 -1
  41. package/dist/widgets/scatterplot.js +7 -6
  42. package/dist/widgets/scatterplot.js.map +1 -1
  43. package/dist/widgets/spread.js +57 -56
  44. package/dist/widgets/spread.js.map +1 -1
  45. package/dist/widgets/table.js +67 -66
  46. package/dist/widgets/table.js.map +1 -1
  47. package/dist/widgets/timeseries.js +23 -22
  48. package/dist/widgets/timeseries.js.map +1 -1
  49. package/dist/widgets/wrapper.js +21 -20
  50. package/dist/widgets/wrapper.js.map +1 -1
  51. package/package.json +7 -3
  52. package/src/chat/bubbles/chat-agent-message.test.tsx +30 -0
  53. package/src/chat/bubbles/chat-agent-message.tsx +11 -0
  54. package/src/chat/bubbles/chat-error-message.test.tsx +40 -0
  55. package/src/chat/bubbles/chat-error-message.tsx +47 -0
  56. package/src/chat/bubbles/chat-suggestion-button.test.tsx +24 -0
  57. package/src/chat/bubbles/chat-suggestion-button.tsx +27 -0
  58. package/src/chat/bubbles/chat-user-message.test.tsx +27 -0
  59. package/src/chat/bubbles/chat-user-message.tsx +27 -0
  60. package/src/chat/bubbles/index.ts +4 -0
  61. package/src/chat/bubbles/styles.ts +106 -0
  62. package/src/chat/const.ts +3 -0
  63. package/src/chat/containers/chat-content.test.tsx +15 -0
  64. package/src/chat/containers/chat-content.tsx +32 -0
  65. package/src/chat/containers/chat-footer.test.tsx +34 -0
  66. package/src/chat/containers/chat-footer.tsx +78 -0
  67. package/src/chat/containers/chat-header.test.tsx +28 -0
  68. package/src/chat/containers/chat-header.tsx +29 -0
  69. package/src/chat/containers/chat-starter.test.tsx +32 -0
  70. package/src/chat/containers/chat-starter.tsx +75 -0
  71. package/src/chat/containers/index.ts +4 -0
  72. package/src/chat/containers/styles.ts +107 -0
  73. package/src/chat/feedback/chat-actions-container.test.tsx +64 -0
  74. package/src/chat/feedback/chat-actions-container.tsx +7 -0
  75. package/src/chat/feedback/chat-loader.test.tsx +10 -0
  76. package/src/chat/feedback/chat-loader.tsx +31 -0
  77. package/src/chat/feedback/chat-rating-action.tsx +43 -0
  78. package/src/chat/feedback/chat-thinking.test.tsx +15 -0
  79. package/src/chat/feedback/chat-thinking.tsx +23 -0
  80. package/src/chat/feedback/chat-tools.test.tsx +23 -0
  81. package/src/chat/feedback/chat-tools.tsx +54 -0
  82. package/src/chat/feedback/index.ts +5 -0
  83. package/src/chat/feedback/styles.ts +80 -0
  84. package/src/chat/index.ts +45 -0
  85. package/src/chat/types.ts +124 -0
  86. package/src/components/copy-button/copy-button.test.tsx +41 -0
  87. package/src/components/copy-button/copy-button.tsx +31 -0
  88. package/src/components/copy-button/types.ts +10 -0
  89. 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,5 @@
1
+ export { ChatThinking } from './chat-thinking'
2
+ export { ChatLoader } from './chat-loader'
3
+ export { ChatActionsContainer } from './chat-actions-container'
4
+ export { ChatRatingAction } from './chat-rating-action'
5
+ export { ChatTools } from './chat-tools'
@@ -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
+ }
@@ -0,0 +1,10 @@
1
+ import type { IconButtonProps } from '@mui/material/IconButton'
2
+
3
+ export interface CopyButtonProps extends Omit<
4
+ IconButtonProps,
5
+ 'onError' | 'onClick'
6
+ > {
7
+ copyText: string
8
+ onSuccess?: () => void
9
+ onError?: (err: Error) => void
10
+ }
@@ -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'