@carto/ps-react-ui 4.9.1 → 4.11.1

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 (207) hide show
  1. package/dist/category-Dnd2_j0x.js +719 -0
  2. package/dist/category-Dnd2_j0x.js.map +1 -0
  3. package/dist/change-column-DjjwoPt1.js +1143 -0
  4. package/dist/change-column-DjjwoPt1.js.map +1 -0
  5. package/dist/chat.js +1507 -0
  6. package/dist/chat.js.map +1 -0
  7. package/dist/components.js +122 -120
  8. package/dist/components.js.map +1 -1
  9. package/dist/copy-button-DGL1tyli.js +26 -0
  10. package/dist/copy-button-DGL1tyli.js.map +1 -0
  11. package/dist/{data-zoom-layout-0QSptXG_.js → data-zoom-layout-CkVnm6ej.js} +3 -3
  12. package/dist/{data-zoom-layout-0QSptXG_.js.map → data-zoom-layout-CkVnm6ej.js.map} +1 -1
  13. package/dist/{download-config-CzmjOT2T.js → download-config-oJIFZ2WC.js} +9 -8
  14. package/dist/{download-config-CzmjOT2T.js.map → download-config-oJIFZ2WC.js.map} +1 -1
  15. package/dist/{png-item-CS4z1iSH.js → png-item-BE9uEqlD.js} +2 -2
  16. package/dist/png-item-BE9uEqlD.js.map +1 -0
  17. package/dist/{spread-Y9R1f5dm.js → spread-DYNpzgh_.js} +10 -11
  18. package/dist/{spread-Y9R1f5dm.js.map → spread-DYNpzgh_.js.map} +1 -1
  19. package/dist/{table-CQCAnDLb.js → table-C9IMbTr0.js} +50 -53
  20. package/dist/table-C9IMbTr0.js.map +1 -0
  21. package/dist/types/chat/bubbles/chat-error-message.d.ts +2 -0
  22. package/dist/types/chat/bubbles/chat-suggestion-button.d.ts +2 -0
  23. package/dist/types/chat/bubbles/chat-user-message.d.ts +2 -0
  24. package/dist/types/chat/bubbles/index.d.ts +4 -0
  25. package/dist/types/chat/const.d.ts +4 -0
  26. package/dist/types/chat/containers/chat-content.d.ts +2 -0
  27. package/dist/types/chat/containers/chat-footer.d.ts +2 -0
  28. package/dist/types/chat/containers/chat-header.d.ts +2 -0
  29. package/dist/types/chat/containers/chat-starter.d.ts +2 -0
  30. package/dist/types/chat/containers/index.d.ts +4 -0
  31. package/dist/types/chat/containers/styles.d.ts +93 -0
  32. package/dist/types/chat/feedback/chat-loader.d.ts +2 -0
  33. package/dist/types/chat/feedback/chat-rating-action.d.ts +2 -0
  34. package/dist/types/chat/feedback/chat-thinking.d.ts +2 -0
  35. package/dist/types/chat/feedback/chat-tool-code-area.d.ts +2 -0
  36. package/dist/types/chat/feedback/chat-tool-full-view-dialog.d.ts +2 -0
  37. package/dist/types/chat/feedback/chat-tool-group.d.ts +2 -0
  38. package/dist/types/chat/feedback/chat-tool-trace.d.ts +3 -0
  39. package/dist/types/chat/feedback/get-tool-label.d.ts +2 -0
  40. package/dist/types/chat/feedback/index.d.ts +8 -0
  41. package/dist/types/chat/feedback/styles.d.ts +211 -0
  42. package/dist/types/chat/index.d.ts +20 -0
  43. package/dist/types/chat/types.d.ts +184 -0
  44. package/dist/types/chat/use-typewriter.d.ts +30 -0
  45. package/dist/types/components/copy-button/copy-button.d.ts +2 -0
  46. package/dist/types/components/copy-button/types.d.ts +6 -0
  47. package/dist/types/components/index.d.ts +2 -0
  48. package/dist/types/widgets/actions/brush-toggle/style.d.ts +1 -1
  49. package/dist/types/widgets/actions/shared/styles.d.ts +1 -1
  50. package/dist/types/widgets/actions/zoom-toggle/style.d.ts +1 -1
  51. package/dist/types/widgets/echart/types.d.ts +1 -1
  52. package/dist/types/widgets/toolbar-actions/styles.d.ts +1 -1
  53. package/dist/types/widgets-v2/actions/brush-toggle/style.d.ts +1 -1
  54. package/dist/types/widgets-v2/actions/change-column/style.d.ts +1 -1
  55. package/dist/types/widgets-v2/actions/fullscreen/style.d.ts +1 -1
  56. package/dist/types/widgets-v2/actions/index.d.ts +1 -0
  57. package/dist/types/widgets-v2/actions/lock-selection/style.d.ts +1 -1
  58. package/dist/types/widgets-v2/actions/relative-data/style.d.ts +1 -1
  59. package/dist/types/widgets-v2/actions/searcher/style.d.ts +1 -1
  60. package/dist/types/widgets-v2/actions/show-all/index.d.ts +2 -0
  61. package/dist/types/widgets-v2/actions/show-all/labels.d.ts +5 -0
  62. package/dist/types/widgets-v2/actions/show-all/show-all.d.ts +33 -0
  63. package/dist/types/widgets-v2/actions/show-all/style.d.ts +8 -0
  64. package/dist/types/widgets-v2/actions/stack-toggle/style.d.ts +1 -1
  65. package/dist/types/widgets-v2/actions/zoom-toggle/style.d.ts +1 -1
  66. package/dist/types/widgets-v2/category/category-ui.d.ts +9 -2
  67. package/dist/types/widgets-v2/category/category.d.ts +9 -2
  68. package/dist/types/widgets-v2/category/components/category-row-other.d.ts +19 -6
  69. package/dist/types/widgets-v2/category/style.d.ts +21 -2
  70. package/dist/types/widgets-v2/category/types.d.ts +2 -0
  71. package/dist/types/widgets-v2/index.d.ts +3 -2
  72. package/dist/types/widgets-v2/selection-summary/labels.d.ts +7 -2
  73. package/dist/types/widgets-v2/selection-summary/selection-summary.d.ts +13 -6
  74. package/dist/types/widgets-v2/selection-summary/style.d.ts +15 -0
  75. package/dist/widgets/actions.js +115 -114
  76. package/dist/widgets/actions.js.map +1 -1
  77. package/dist/widgets/bar.js +1 -1
  78. package/dist/widgets/category.js +9 -8
  79. package/dist/widgets/category.js.map +1 -1
  80. package/dist/widgets/formula.js +11 -10
  81. package/dist/widgets/formula.js.map +1 -1
  82. package/dist/widgets/histogram.js +7 -6
  83. package/dist/widgets/histogram.js.map +1 -1
  84. package/dist/widgets/markdown.js +9 -8
  85. package/dist/widgets/markdown.js.map +1 -1
  86. package/dist/widgets/pie.js +1 -1
  87. package/dist/widgets/scatterplot.js +1 -1
  88. package/dist/widgets/spread.js +9 -8
  89. package/dist/widgets/spread.js.map +1 -1
  90. package/dist/widgets/table.js +17 -16
  91. package/dist/widgets/table.js.map +1 -1
  92. package/dist/widgets/timeseries.js +1 -1
  93. package/dist/widgets/utils.js +1 -1
  94. package/dist/widgets/wrapper.js +3 -2
  95. package/dist/widgets/wrapper.js.map +1 -1
  96. package/dist/widgets-v2/actions.js +41 -37
  97. package/dist/widgets-v2/bar.js +9 -10
  98. package/dist/widgets-v2/bar.js.map +1 -1
  99. package/dist/widgets-v2/category.js +25 -26
  100. package/dist/widgets-v2/category.js.map +1 -1
  101. package/dist/widgets-v2/formula.js +3 -3
  102. package/dist/widgets-v2/histogram.js +11 -13
  103. package/dist/widgets-v2/histogram.js.map +1 -1
  104. package/dist/widgets-v2/markdown.js +26 -27
  105. package/dist/widgets-v2/markdown.js.map +1 -1
  106. package/dist/widgets-v2/pie.js +8 -10
  107. package/dist/widgets-v2/pie.js.map +1 -1
  108. package/dist/widgets-v2/scatterplot.js +10 -12
  109. package/dist/widgets-v2/scatterplot.js.map +1 -1
  110. package/dist/widgets-v2/spread.js +15 -16
  111. package/dist/widgets-v2/spread.js.map +1 -1
  112. package/dist/widgets-v2/table.js +39 -40
  113. package/dist/widgets-v2/table.js.map +1 -1
  114. package/dist/widgets-v2/timeseries.js +9 -11
  115. package/dist/widgets-v2/timeseries.js.map +1 -1
  116. package/dist/widgets-v2/utils.js +1 -1
  117. package/dist/widgets-v2.js +284 -282
  118. package/dist/widgets-v2.js.map +1 -1
  119. package/package.json +5 -1
  120. package/src/chat/bubbles/chat-agent-message.test.tsx +30 -0
  121. package/src/chat/bubbles/chat-agent-message.tsx +11 -0
  122. package/src/chat/bubbles/chat-error-message.test.tsx +40 -0
  123. package/src/chat/bubbles/chat-error-message.tsx +47 -0
  124. package/src/chat/bubbles/chat-suggestion-button.test.tsx +24 -0
  125. package/src/chat/bubbles/chat-suggestion-button.tsx +27 -0
  126. package/src/chat/bubbles/chat-user-message.test.tsx +27 -0
  127. package/src/chat/bubbles/chat-user-message.tsx +27 -0
  128. package/src/chat/bubbles/index.ts +4 -0
  129. package/src/chat/bubbles/styles.ts +148 -0
  130. package/src/chat/const.ts +4 -0
  131. package/src/chat/containers/chat-content.test.tsx +269 -0
  132. package/src/chat/containers/chat-content.tsx +142 -0
  133. package/src/chat/containers/chat-footer.test.tsx +34 -0
  134. package/src/chat/containers/chat-footer.tsx +78 -0
  135. package/src/chat/containers/chat-header.test.tsx +28 -0
  136. package/src/chat/containers/chat-header.tsx +29 -0
  137. package/src/chat/containers/chat-starter.test.tsx +32 -0
  138. package/src/chat/containers/chat-starter.tsx +75 -0
  139. package/src/chat/containers/index.ts +4 -0
  140. package/src/chat/containers/styles.ts +96 -0
  141. package/src/chat/feedback/chat-actions-container.test.tsx +64 -0
  142. package/src/chat/feedback/chat-actions-container.tsx +7 -0
  143. package/src/chat/feedback/chat-loader.test.tsx +10 -0
  144. package/src/chat/feedback/chat-loader.tsx +31 -0
  145. package/src/chat/feedback/chat-rating-action.tsx +43 -0
  146. package/src/chat/feedback/chat-thinking.test.tsx +15 -0
  147. package/src/chat/feedback/chat-thinking.tsx +23 -0
  148. package/src/chat/feedback/chat-tool-code-area.test.tsx +23 -0
  149. package/src/chat/feedback/chat-tool-code-area.tsx +71 -0
  150. package/src/chat/feedback/chat-tool-full-view-dialog.test.tsx +39 -0
  151. package/src/chat/feedback/chat-tool-full-view-dialog.tsx +121 -0
  152. package/src/chat/feedback/chat-tool-group.test.tsx +84 -0
  153. package/src/chat/feedback/chat-tool-group.tsx +156 -0
  154. package/src/chat/feedback/chat-tool-trace.test.tsx +81 -0
  155. package/src/chat/feedback/chat-tool-trace.tsx +192 -0
  156. package/src/chat/feedback/get-tool-label.test.tsx +91 -0
  157. package/src/chat/feedback/get-tool-label.ts +13 -0
  158. package/src/chat/feedback/index.ts +8 -0
  159. package/src/chat/feedback/styles.ts +229 -0
  160. package/src/chat/index.ts +59 -0
  161. package/src/chat/types.ts +215 -0
  162. package/src/chat/use-typewriter.test.tsx +38 -0
  163. package/src/chat/use-typewriter.ts +82 -0
  164. package/src/components/copy-button/copy-button.test.tsx +41 -0
  165. package/src/components/copy-button/copy-button.tsx +31 -0
  166. package/src/components/copy-button/types.ts +10 -0
  167. package/src/components/index.ts +3 -0
  168. package/src/widgets/echart/types.ts +1 -1
  169. package/src/widgets-v2/actions/brush-toggle/brush-toggle.tsx +1 -1
  170. package/src/widgets-v2/actions/change-column/sortable-column-item.tsx +1 -1
  171. package/src/widgets-v2/actions/download/download.tsx +1 -1
  172. package/src/widgets-v2/actions/download/icons.tsx +1 -1
  173. package/src/widgets-v2/actions/fullscreen/fullscreen.tsx +3 -3
  174. package/src/widgets-v2/actions/index.ts +8 -0
  175. package/src/widgets-v2/actions/lock-selection/lock-selection.tsx +2 -2
  176. package/src/widgets-v2/actions/relative-data/relative-data.tsx +1 -1
  177. package/src/widgets-v2/actions/searcher/searcher-toggle.tsx +1 -1
  178. package/src/widgets-v2/actions/searcher/searcher.tsx +2 -2
  179. package/src/widgets-v2/actions/show-all/index.ts +7 -0
  180. package/src/widgets-v2/actions/show-all/labels.ts +8 -0
  181. package/src/widgets-v2/actions/show-all/show-all.test.tsx +50 -0
  182. package/src/widgets-v2/actions/show-all/show-all.tsx +72 -0
  183. package/src/widgets-v2/actions/show-all/style.ts +8 -0
  184. package/src/widgets-v2/actions/stack-toggle/stack-toggle.tsx +1 -1
  185. package/src/widgets-v2/actions/zoom-toggle/zoom-toggle.tsx +1 -1
  186. package/src/widgets-v2/category/category-ui.test.tsx +26 -10
  187. package/src/widgets-v2/category/category-ui.tsx +13 -3
  188. package/src/widgets-v2/category/category.test.tsx +4 -4
  189. package/src/widgets-v2/category/category.tsx +10 -1
  190. package/src/widgets-v2/category/components/category-row-other.test.tsx +36 -7
  191. package/src/widgets-v2/category/components/category-row-other.tsx +64 -13
  192. package/src/widgets-v2/category/style.ts +35 -4
  193. package/src/widgets-v2/category/types.ts +2 -0
  194. package/src/widgets-v2/index.ts +3 -0
  195. package/src/widgets-v2/selection-summary/labels.ts +8 -4
  196. package/src/widgets-v2/selection-summary/selection-summary.test.tsx +15 -9
  197. package/src/widgets-v2/selection-summary/selection-summary.tsx +42 -22
  198. package/src/widgets-v2/selection-summary/style.ts +15 -0
  199. package/src/widgets-v2/table/table-ui.tsx +4 -4
  200. package/src/widgets-v2/toolbox/toolbox.tsx +1 -1
  201. package/src/widgets-v2/wrapper/widget-wrapper.tsx +1 -1
  202. package/dist/category-DwaeYjpX.js +0 -656
  203. package/dist/category-DwaeYjpX.js.map +0 -1
  204. package/dist/change-column-B4IT0rh6.js +0 -1110
  205. package/dist/change-column-B4IT0rh6.js.map +0 -1
  206. package/dist/png-item-CS4z1iSH.js.map +0 -1
  207. package/dist/table-CQCAnDLb.js.map +0 -1
@@ -0,0 +1,91 @@
1
+ import { describe, test, expect } from 'vitest'
2
+ import { getToolLabel } from './get-tool-label'
3
+ import type { ChatToolItem } from '../types'
4
+
5
+ describe('getToolLabel', () => {
6
+ test('uses runningLabel when status is running', () => {
7
+ const tool: ChatToolItem = {
8
+ id: '1',
9
+ name: 'execute_sql',
10
+ status: 'running',
11
+ runningLabel: 'Running query',
12
+ }
13
+ expect(getToolLabel(tool)).toBe('Running query')
14
+ })
15
+
16
+ test('falls back to capitalized name when running without runningLabel', () => {
17
+ const tool: ChatToolItem = {
18
+ id: '1',
19
+ name: 'execute_sql',
20
+ status: 'running',
21
+ }
22
+ expect(getToolLabel(tool)).toBe('Execute sql')
23
+ })
24
+
25
+ test('uses label for completed status', () => {
26
+ const tool: ChatToolItem = {
27
+ id: '1',
28
+ name: 'execute_sql',
29
+ status: 'complete',
30
+ label: 'Database query',
31
+ }
32
+ expect(getToolLabel(tool)).toBe('Database query')
33
+ })
34
+
35
+ test('uses label for error status', () => {
36
+ const tool: ChatToolItem = {
37
+ id: '1',
38
+ name: 'execute_sql',
39
+ status: 'error',
40
+ label: 'Database query',
41
+ }
42
+ expect(getToolLabel(tool)).toBe('Database query')
43
+ })
44
+
45
+ test('falls back to capitalized name for non-running without label', () => {
46
+ const tool: ChatToolItem = {
47
+ id: '1',
48
+ name: 'add_marker',
49
+ status: 'complete',
50
+ }
51
+ expect(getToolLabel(tool)).toBe('Add marker')
52
+ })
53
+
54
+ test('replaces underscores with spaces and capitalizes first letter', () => {
55
+ const tool: ChatToolItem = {
56
+ id: '1',
57
+ name: 'geocode_address_v2',
58
+ status: 'complete',
59
+ }
60
+ expect(getToolLabel(tool)).toBe('Geocode address v2')
61
+ })
62
+
63
+ test('preserves names that are already formatted', () => {
64
+ const tool: ChatToolItem = {
65
+ id: '1',
66
+ name: 'Tool #1',
67
+ status: 'complete',
68
+ }
69
+ expect(getToolLabel(tool)).toBe('Tool #1')
70
+ })
71
+
72
+ test('runningLabel does not leak into non-running statuses', () => {
73
+ const tool: ChatToolItem = {
74
+ id: '1',
75
+ name: 'execute_sql',
76
+ status: 'complete',
77
+ runningLabel: 'Running query',
78
+ }
79
+ expect(getToolLabel(tool)).toBe('Execute sql')
80
+ })
81
+
82
+ test('label does not leak into running status', () => {
83
+ const tool: ChatToolItem = {
84
+ id: '1',
85
+ name: 'execute_sql',
86
+ status: 'running',
87
+ label: 'Database query',
88
+ }
89
+ expect(getToolLabel(tool)).toBe('Execute sql')
90
+ })
91
+ })
@@ -0,0 +1,13 @@
1
+ import type { ChatToolItem } from '../types'
2
+
3
+ function capitalize(str: string): string {
4
+ const spaced = str.replace(/_/g, ' ')
5
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1)
6
+ }
7
+
8
+ export function getToolLabel(tool: ChatToolItem): string {
9
+ if (tool.status === 'running') {
10
+ return tool.runningLabel ?? capitalize(tool.name)
11
+ }
12
+ return tool.label ?? capitalize(tool.name)
13
+ }
@@ -0,0 +1,8 @@
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 { ChatToolTrace } from './chat-tool-trace'
6
+ export { ChatToolCodeArea } from './chat-tool-code-area'
7
+ export { ChatToolFullViewDialog } from './chat-tool-full-view-dialog'
8
+ export { ChatToolGroup } from './chat-tool-group'
@@ -0,0 +1,229 @@
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
+ // --- Tool Trace styles ---
69
+ traceHeader: {
70
+ display: 'flex',
71
+ alignItems: 'center',
72
+ padding: 0,
73
+ paddingLeft: ({ spacing }) => spacing(0.5),
74
+ borderRadius: ({ spacing }) => spacing(0.5),
75
+ width: 'fit-content',
76
+ color: ({ palette }) => palette.text.secondary,
77
+ '&:hover': {
78
+ backgroundColor: ({ palette }) => palette.action.hover,
79
+ },
80
+ },
81
+ traceChevron: {
82
+ color: ({ palette }) => palette.text.secondary,
83
+ transition: 'transform 0.2s',
84
+ },
85
+ traceDetailsWrapper: {
86
+ marginTop: ({ spacing }) => spacing(0.5),
87
+ padding: ({ spacing }) => spacing(1.5),
88
+ border: '1px solid',
89
+ borderColor: 'divider',
90
+ borderRadius: ({ spacing }) => spacing(1),
91
+ },
92
+ traceField: {
93
+ display: 'flex',
94
+ alignItems: 'flex-start',
95
+ gap: ({ spacing }) => spacing(1),
96
+ },
97
+ traceFieldLabel: {
98
+ flexShrink: 0,
99
+ },
100
+ traceReference: {
101
+ display: 'flex',
102
+ borderRadius: ({ spacing }) => spacing(0.25),
103
+ padding: ({ spacing }) => spacing(0, 0.5),
104
+ gap: ({ spacing }) => spacing(0.5),
105
+ backgroundColor: ({ palette }) => palette.success.relatedLight,
106
+ color: ({ palette }) => palette.success.dark,
107
+ },
108
+ traceStatusSuccess: {
109
+ color: ({ palette }) => palette.success.main,
110
+ },
111
+ traceStatusError: {
112
+ color: ({ palette }) => palette.error.main,
113
+ },
114
+ // --- Code Area styles ---
115
+ codeArea: {
116
+ position: 'relative',
117
+ width: '100%',
118
+ },
119
+ codeAreaPre: {
120
+ margin: 0,
121
+ padding: ({ spacing }) => spacing(1),
122
+ borderRadius: ({ spacing }) => spacing(0.5),
123
+ backgroundColor: ({ palette }) => palette.background.default,
124
+ fontSize: '0.75rem',
125
+ fontFamily: 'monospace',
126
+ whiteSpace: 'pre-wrap',
127
+ wordBreak: 'break-word',
128
+ overflowY: 'auto',
129
+ },
130
+ codeAreaPreError: {
131
+ borderLeft: ({ palette }) => `3px solid ${palette.error.main}`,
132
+ backgroundColor: ({ palette }) => `${palette.error.main}08`,
133
+ },
134
+ codeAreaFullViewButton: {
135
+ position: 'absolute',
136
+ top: ({ spacing }) => spacing(0.5),
137
+ right: ({ spacing }) => spacing(0.5),
138
+ },
139
+ // --- Full View Dialog styles ---
140
+ fullViewDialog: {
141
+ margin: ({ spacing }) => spacing(5),
142
+ },
143
+ fullViewPaper: {
144
+ borderRadius: 1,
145
+ },
146
+ fullViewTitle: {
147
+ display: 'flex',
148
+ alignItems: 'center',
149
+ justifyContent: 'space-between',
150
+ padding: ({ spacing }) => spacing(2),
151
+ borderBottom: '1px solid',
152
+ borderBottomColor: 'divider',
153
+ },
154
+ fullViewDialogContent: {
155
+ padding: 0,
156
+ '&:first-of-type': { paddingTop: 0 },
157
+ },
158
+ fullViewPre: {
159
+ margin: 0,
160
+ padding: ({ spacing }) => spacing(1),
161
+ background: ({ palette, spacing }) =>
162
+ `linear-gradient(to right, ${palette.action.hover} calc(${spacing(1)} + 3em), ${palette.background.default} calc(${spacing(1)} + 3em))`,
163
+ fontFamily: 'monospace',
164
+ fontSize: '0.8125rem',
165
+ whiteSpace: 'pre-wrap',
166
+ wordBreak: 'break-word',
167
+ lineHeight: 1.6,
168
+ counterReset: 'line',
169
+ },
170
+ fullViewLine: {
171
+ display: 'block',
172
+ '&::before': {
173
+ counterIncrement: 'line',
174
+ content: 'counter(line)',
175
+ display: 'inline-block',
176
+ width: '2em',
177
+ marginRight: '2em',
178
+ textAlign: 'right',
179
+ color: ({ palette }) => palette.text.secondary,
180
+ userSelect: 'none',
181
+ },
182
+ },
183
+ groupHeader: {
184
+ textAlign: 'left',
185
+ display: 'flex',
186
+ alignItems: 'center',
187
+ borderRadius: 0,
188
+ gap: ({ spacing }) => spacing(0.5),
189
+ },
190
+ errorBadge: {
191
+ color: ({ palette }) => palette.error.main,
192
+ fontWeight: 600,
193
+ display: 'flex',
194
+ alignItems: 'center',
195
+ gap: ({ spacing }) => spacing(0.25),
196
+ },
197
+ // --- Syntax highlighting token styles ---
198
+ syntaxToken_key: {
199
+ color: '#881280',
200
+ },
201
+ syntaxToken_string: {
202
+ color: '#c41a16',
203
+ },
204
+ syntaxToken_number: {
205
+ color: '#1c00cf',
206
+ },
207
+ syntaxToken_boolean: {
208
+ color: '#1c00cf',
209
+ },
210
+ syntaxToken_null: {
211
+ color: '#808080',
212
+ },
213
+ syntaxToken_punctuation: {
214
+ color: ({ palette }) => palette.text.primary,
215
+ },
216
+ groupListItem: {
217
+ borderBottom: '1px solid',
218
+ borderColor: 'divider',
219
+ '&:first-of-type .MuiButton-root': {
220
+ borderRadius: ({ spacing }) => spacing(1, 1, 0, 0),
221
+ },
222
+ '&:last-of-type:not([aria-expanded=true]) .MuiButton-root': {
223
+ borderRadius: ({ spacing }) => spacing(0, 0, 1, 1),
224
+ },
225
+ '&:last-of-type': {
226
+ borderBottomWidth: 0,
227
+ },
228
+ },
229
+ } satisfies Record<string, SxProps<Theme>>
@@ -0,0 +1,59 @@
1
+ // Types
2
+ export type {
3
+ ChatSxProps,
4
+ ChatErrorAction,
5
+ ChatUserMessageProps,
6
+ ChatAgentMessageProps,
7
+ ChatErrorMessageProps,
8
+ ChatSuggestionButtonProps,
9
+ ChatThinkingProps,
10
+ ChatLoaderProps,
11
+ ChatContentProps,
12
+ ChatContentRef,
13
+ ChatHeaderProps,
14
+ ChatFooterProps,
15
+ ChatStarterItem,
16
+ ChatStarterProps,
17
+ ChatRatingActionProps,
18
+ ChatToolItem,
19
+ ChatToolTraceProps,
20
+ ChatToolCodeAreaProps,
21
+ ChatToolFullViewDialogProps,
22
+ ChatToolGroupProps,
23
+ } from './types'
24
+
25
+ // Constants
26
+ export {
27
+ CHAT_MAX_WIDTH,
28
+ CHAT_SCROLL_DELAY,
29
+ CHAT_DIVIDER_DELAY,
30
+ CHAT_TOOL_CODE_AREA_MAX_HEIGHT,
31
+ } from './const'
32
+
33
+ // Hooks
34
+ export { useTypewriter } from './use-typewriter'
35
+
36
+ // Messages
37
+ export { ChatUserMessage } from './bubbles/chat-user-message'
38
+ export { ChatAgentMessage } from './bubbles/chat-agent-message'
39
+ export { ChatErrorMessage } from './bubbles/chat-error-message'
40
+ export { ChatSuggestionButton } from './bubbles/chat-suggestion-button'
41
+ export { ChatMessageOverflow } from './bubbles/styles'
42
+
43
+ // Feedback
44
+ export { ChatThinking } from './feedback/chat-thinking'
45
+ export { ChatLoader } from './feedback/chat-loader'
46
+
47
+ // Containers
48
+ export { ChatContent } from './containers/chat-content'
49
+ export { ChatHeader } from './containers/chat-header'
50
+ export { ChatFooter } from './containers/chat-footer'
51
+ export { ChatStarter } from './containers/chat-starter'
52
+
53
+ // Feedback
54
+ export { ChatActionsContainer } from './feedback/chat-actions-container'
55
+ export { ChatRatingAction } from './feedback/chat-rating-action'
56
+ export { ChatToolTrace } from './feedback/chat-tool-trace'
57
+ export { ChatToolCodeArea } from './feedback/chat-tool-code-area'
58
+ export { ChatToolFullViewDialog } from './feedback/chat-tool-full-view-dialog'
59
+ export { ChatToolGroup } from './feedback/chat-tool-group'
@@ -0,0 +1,215 @@
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
+ /**
57
+ * Smooth-scrolls to the bottom whenever new content is added — but only if
58
+ * the user was already at (or near) the bottom. Readers who scrolled up to
59
+ * revisit older messages are left alone. Defaults to `true`; pass `false`
60
+ * to opt out and manage scroll yourself via the ref.
61
+ */
62
+ autoScroll?: boolean
63
+ labels?: {
64
+ jumpToLatest?: string
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Imperative handle exposed by `ChatContent` via `ref`. Use it to drive scroll
70
+ * from the parent — for example, calling `scrollToBottom()` when a new agent
71
+ * message arrives.
72
+ */
73
+ export interface ChatContentRef {
74
+ /** Smooth-scrolls the content area to the bottom. */
75
+ scrollToBottom: () => void
76
+ /** Smooth-scrolls the content area to the top. */
77
+ scrollToTop: () => void
78
+ /** `true` when the content area is scrolled to (or near) its bottom edge. */
79
+ isAtBottom: boolean
80
+ /** `true` when the content area is scrolled to (or near) its top edge. */
81
+ isAtTop: boolean
82
+ }
83
+
84
+ // === Container props ===
85
+ export interface ChatHeaderProps extends ChatSxProps {
86
+ leftSlot?: ReactNode
87
+ title: ReactNode
88
+ rightSlot?: ReactNode
89
+ onClose?: () => void
90
+ }
91
+
92
+ export interface ChatFooterProps extends ChatSxProps {
93
+ /** Current value of the chat message area. */
94
+ value: string
95
+ /** Called with the new textarea value on every keystroke. */
96
+ onChange: (value: string) => void
97
+ /** Called when the send button is clicked or Enter is pressed (without Shift). */
98
+ onSend: () => void
99
+ /** Called when the stop button is clicked. Only shown while `isGenerating` is true. */
100
+ onStop?: () => void
101
+ /** When true, swaps the send button for a stop button and disables the textarea. */
102
+ isGenerating?: boolean
103
+ /** Disables the textarea and both send/stop buttons. */
104
+ disabled?: boolean
105
+ /** Placeholder text for the textarea. Defaults to `'Type a message...'`. */
106
+ placeholder?: string
107
+ /** Accessible labels for the send and stop buttons (used as `aria-label`). */
108
+ labels?: {
109
+ /** Defaults to `'Send'`. */
110
+ send?: string
111
+ /** Defaults to `'Stop'`. */
112
+ stop?: string
113
+ }
114
+ /** Helper text rendered under the input. Defaults to an AI disclaimer; pass `null` to hide. */
115
+ caption?: ReactNode
116
+ }
117
+
118
+ // === Extras props ===
119
+ export interface ChatStarterItem {
120
+ label: string
121
+ color?: string
122
+ }
123
+
124
+ export interface ChatStarterProps extends ChatSxProps {
125
+ icon?: ReactNode
126
+ title?: ReactNode
127
+ description?: ReactNode
128
+ items: string[] | ChatStarterItem[]
129
+ size?: 'small' | 'medium'
130
+ onSelect?: (prompt: string) => void
131
+ }
132
+
133
+ export interface ChatRatingActionProps {
134
+ onRatingChange?: (rating: 'up' | 'down' | null) => void
135
+ rating?: 'up' | 'down' | null
136
+ labels?: {
137
+ thumbUp?: string
138
+ thumbDown?: string
139
+ }
140
+ }
141
+
142
+ export interface ChatToolItem {
143
+ id: string
144
+ name: string
145
+ status: 'running' | 'complete' | 'error'
146
+ /** Display label shown while status is 'running'. Falls back to a capitalized `name`. */
147
+ runningLabel?: string
148
+ /** Display label shown for non-running statuses. Falls back to a capitalized `name`. */
149
+ label?: string
150
+ /** Friendly reference name for the tool (e.g. "add_marker"). Displayed with icon. */
151
+ reference?: string
152
+ /** Execution duration in seconds (e.g. 1.8) */
153
+ duration?: number
154
+ /** Input arguments as a JSON string or plain text */
155
+ inputArguments?: string
156
+ /** Output as a JSON string or plain text */
157
+ output?: string
158
+ }
159
+
160
+ export interface ChatToolTraceProps extends ChatSxProps {
161
+ tool: ChatToolItem
162
+ /** Whether the trace accordion is expanded */
163
+ expanded?: boolean
164
+ /** Callback when accordion expansion state changes */
165
+ onExpandedChange?: (expanded: boolean) => void
166
+ labels?: {
167
+ toolExecuted?: string
168
+ reference?: string
169
+ duration?: string
170
+ status?: string
171
+ inputArguments?: string
172
+ output?: string
173
+ fullView?: string
174
+ success?: string
175
+ error?: string
176
+ running?: string
177
+ }
178
+ }
179
+
180
+ export interface ChatToolCodeAreaProps extends ChatSxProps {
181
+ /** Code content to display */
182
+ content: string
183
+ /** Label for the full view dialog title */
184
+ title?: string
185
+ /** Render with error styling (red left border, tinted background) */
186
+ isError?: boolean
187
+ labels?: {
188
+ fullView?: string
189
+ }
190
+ }
191
+
192
+ export interface ChatToolFullViewDialogProps {
193
+ open: boolean
194
+ onClose: () => void
195
+ title: string
196
+ content: string
197
+ }
198
+
199
+ export interface ChatToolGroupProps extends ChatSxProps {
200
+ tools: ChatToolItem[]
201
+ /** Whether the group accordion is expanded */
202
+ expanded?: boolean
203
+ /** Callback when group expansion state changes */
204
+ onExpandedChange?: (expanded: boolean) => void
205
+ /** Map of tool IDs to their individual expanded state. Used to preserve expansion state during grouping. */
206
+ expandedTools?: Record<string, boolean>
207
+ /** Callback when an individual tool's expansion state changes */
208
+ onToolExpandedChange?: (
209
+ value: Record<string, boolean>,
210
+ toolId?: string,
211
+ ) => void
212
+ labels?: ChatToolTraceProps['labels'] & {
213
+ toolsUsed?: string
214
+ }
215
+ }
@@ -0,0 +1,38 @@
1
+ import { describe, expect, test } from 'vitest'
2
+ import { renderHook } from '@testing-library/react'
3
+ import { useTypewriter } from './use-typewriter'
4
+
5
+ describe('useTypewriter', () => {
6
+ test('starts with empty text and isTyping=true when fullText is non-empty', () => {
7
+ const { result } = renderHook(() => useTypewriter('hello'))
8
+ expect(result.current.displayedText).toBe('')
9
+ expect(result.current.isTyping).toBe(true)
10
+ })
11
+
12
+ test('returns the full text immediately when skipAnimation is true', () => {
13
+ const { result } = renderHook(() =>
14
+ useTypewriter('hello', { skipAnimation: true }),
15
+ )
16
+ expect(result.current.displayedText).toBe('hello')
17
+ expect(result.current.isTyping).toBe(false)
18
+ })
19
+
20
+ test('isTyping=false for empty fullText', () => {
21
+ const { result } = renderHook(() => useTypewriter(''))
22
+ expect(result.current.displayedText).toBe('')
23
+ expect(result.current.isTyping).toBe(false)
24
+ })
25
+
26
+ test('skipAnimation captured at mount — toggling later does not retrigger reveal', () => {
27
+ const { result, rerender } = renderHook(
28
+ ({ skip }: { skip: boolean }) =>
29
+ useTypewriter('hello', { skipAnimation: skip }),
30
+ { initialProps: { skip: true } },
31
+ )
32
+ expect(result.current.displayedText).toBe('hello')
33
+
34
+ rerender({ skip: false })
35
+ expect(result.current.displayedText).toBe('hello')
36
+ expect(result.current.isTyping).toBe(false)
37
+ })
38
+ })