@carto/ps-react-ui 4.14.0 → 4.15.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 (39) hide show
  1. package/dist/chat.js +494 -466
  2. package/dist/chat.js.map +1 -1
  3. package/dist/components.js +910 -900
  4. package/dist/components.js.map +1 -1
  5. package/dist/markdown-DdqANCiN.js +102 -0
  6. package/dist/markdown-DdqANCiN.js.map +1 -0
  7. package/dist/markdown-content-Dk2DSgbf.js +10287 -0
  8. package/dist/markdown-content-Dk2DSgbf.js.map +1 -0
  9. package/dist/types/chat/bubbles/chat-agent-message-markdown.d.ts +12 -0
  10. package/dist/types/chat/bubbles/chat-agent-message.d.ts +9 -0
  11. package/dist/types/chat/bubbles/index.d.ts +1 -0
  12. package/dist/types/chat/index.d.ts +2 -1
  13. package/dist/types/chat/types.d.ts +9 -0
  14. package/dist/types/components/index.d.ts +2 -1
  15. package/dist/types/components/lasso-tool/message.d.ts +7 -0
  16. package/dist/types/components/lasso-tool/types.d.ts +17 -2
  17. package/dist/types/widgets-v2/markdown/markdown-content.d.ts +7 -1
  18. package/dist/widgets-v2/markdown.js +9 -8
  19. package/dist/widgets-v2/markdown.js.map +1 -1
  20. package/dist/widgets-v2.js +23 -22
  21. package/dist/widgets-v2.js.map +1 -1
  22. package/package.json +3 -2
  23. package/src/chat/bubbles/chat-agent-message-markdown.test.tsx +38 -0
  24. package/src/chat/bubbles/chat-agent-message-markdown.tsx +34 -0
  25. package/src/chat/bubbles/chat-agent-message.test.tsx +12 -0
  26. package/src/chat/bubbles/chat-agent-message.tsx +21 -1
  27. package/src/chat/bubbles/index.ts +1 -0
  28. package/src/chat/index.ts +2 -0
  29. package/src/chat/types.ts +10 -0
  30. package/src/components/index.ts +2 -0
  31. package/src/components/lasso-tool/layer-filters.test.tsx +13 -0
  32. package/src/components/lasso-tool/layer-filters.tsx +25 -19
  33. package/src/components/lasso-tool/message.tsx +36 -0
  34. package/src/components/lasso-tool/types.ts +20 -1
  35. package/src/widgets-v2/markdown/markdown-content.test.tsx +10 -0
  36. package/src/widgets-v2/markdown/markdown-content.tsx +9 -0
  37. package/src/widgets-v2/wrapper/widget-wrapper.tsx +4 -1
  38. package/dist/markdown-BD1jcknS.js +0 -8326
  39. package/dist/markdown-BD1jcknS.js.map +0 -1
@@ -1,11 +1,31 @@
1
+ import { forwardRef } from 'react'
1
2
  import { Box, styled, type BoxProps } from '@mui/material'
3
+ import type { ChatAgentMessageProps } from '../types'
2
4
  import { styles } from './styles'
3
5
 
4
6
  const InnerBox = ({ className, ...props }: BoxProps) => (
5
7
  <Box className={`PsChat--agent-message ${className ?? ''}`} {...props} />
6
8
  )
7
9
 
8
- export const ChatAgentMessage = styled(InnerBox)(({ theme }) =>
10
+ const StyledAgentMessage = styled(InnerBox)(({ theme }) =>
9
11
  styles.agentMessageContainer(theme),
10
12
  )
13
+
14
+ /**
15
+ * Agent reply bubble. A styles container that positions the reply and its
16
+ * composed pieces (text, loaders, tool traces, …); it renders `children`
17
+ * verbatim and forwards its `ref` to the underlying element. For Markdown
18
+ * replies, compose `ChatAgentMessageMarkdown` instead of teaching this
19
+ * container about the markdown engine.
20
+ */
21
+ export const ChatAgentMessage = forwardRef<
22
+ HTMLDivElement,
23
+ ChatAgentMessageProps
24
+ >(function ChatAgentMessage({ children, ...props }, ref) {
25
+ return (
26
+ <StyledAgentMessage ref={ref} {...props}>
27
+ {children}
28
+ </StyledAgentMessage>
29
+ )
30
+ })
11
31
  ChatAgentMessage.displayName = 'ChatAgentMessage'
@@ -1,4 +1,5 @@
1
1
  export { ChatUserMessage } from './chat-user-message'
2
2
  export { ChatAgentMessage } from './chat-agent-message'
3
+ export { ChatAgentMessageMarkdown } from './chat-agent-message-markdown'
3
4
  export { ChatErrorMessage } from './chat-error-message'
4
5
  export { ChatSuggestionButton } from './chat-suggestion-button'
package/src/chat/index.ts CHANGED
@@ -4,6 +4,7 @@ export type {
4
4
  ChatErrorAction,
5
5
  ChatUserMessageProps,
6
6
  ChatAgentMessageProps,
7
+ ChatAgentMessageMarkdownProps,
7
8
  ChatErrorMessageProps,
8
9
  ChatSuggestionButtonProps,
9
10
  ChatThinkingProps,
@@ -37,6 +38,7 @@ export { useTypewriter } from './use-typewriter'
37
38
  // Messages
38
39
  export { ChatUserMessage } from './bubbles/chat-user-message'
39
40
  export { ChatAgentMessage } from './bubbles/chat-agent-message'
41
+ export { ChatAgentMessageMarkdown } from './bubbles/chat-agent-message-markdown'
40
42
  export { ChatErrorMessage } from './bubbles/chat-error-message'
41
43
  export { ChatSuggestionButton } from './bubbles/chat-suggestion-button'
42
44
  export { ChatMessageOverflow } from './bubbles/styles'
package/src/chat/types.ts CHANGED
@@ -25,6 +25,16 @@ export interface ChatAgentMessageProps extends ChatSxProps {
25
25
  children: ReactNode
26
26
  }
27
27
 
28
+ export interface ChatAgentMessageMarkdownProps extends ChatSxProps {
29
+ /**
30
+ * Markdown source — rendered as GitHub-Flavored Markdown (tables, task lists,
31
+ * strikethrough) through the library's sanitized renderer, inside a
32
+ * `ChatAgentMessage`. Raw HTML is skipped and images are stripped, since the
33
+ * content is model output.
34
+ */
35
+ children: string
36
+ }
37
+
28
38
  export interface ChatErrorMessageProps extends ChatSxProps {
29
39
  errors: string[]
30
40
  icon?: ReactNode
@@ -28,11 +28,13 @@ export type {
28
28
  LassoGeometryType,
29
29
  LassoLayerFiltersUIProps,
30
30
  LassoLayerFilterItem,
31
+ LassoMessageUIProps,
31
32
  } from './lasso-tool/types'
32
33
  export { LassoToolsUI } from './lasso-tool/lasso-tool'
33
34
  export { LassoToolsInlineUI } from './lasso-tool/lasso-tool-inline'
34
35
  export { LassoGeometryToolbarUI } from './lasso-tool/geometry-toolbar'
35
36
  export { LassoLayerFiltersUI } from './lasso-tool/layer-filters'
37
+ export { LassoMessageUI } from './lasso-tool/message'
36
38
  export {
37
39
  DEFAULT_LASSO_TOOLS_MODES_MAPPING,
38
40
  LASSO_TOOLS_LABELS,
@@ -83,6 +83,19 @@ describe('LassoLayerFiltersUI', () => {
83
83
  ).toBeDefined()
84
84
  })
85
85
 
86
+ it('hides "All selected" while searching', () => {
87
+ setup()
88
+ expect(
89
+ within(dialog()).getByRole('checkbox', { name: 'All selected' }),
90
+ ).toBeDefined()
91
+ fireEvent.change(within(dialog()).getByRole('textbox'), {
92
+ target: { value: 'road' },
93
+ })
94
+ expect(
95
+ within(dialog()).queryByRole('checkbox', { name: 'All selected' }),
96
+ ).toBeNull()
97
+ })
98
+
86
99
  it('does not render when closed', () => {
87
100
  setup({ open: false })
88
101
  expect(screen.queryByRole('dialog')).toBeNull()
@@ -166,25 +166,31 @@ export function LassoLayerFiltersUI({
166
166
  '& .MuiDivider-root': { my: 0 },
167
167
  }}
168
168
  >
169
- <ListItemButton dense sx={rowSx} onClick={toggleAll}>
170
- <ListItemIcon sx={checkboxSlotSx}>
171
- <Checkbox
172
- edge='start'
173
- tabIndex={-1}
174
- disableRipple
175
- sx={{ p: 0.5 }}
176
- checked={allChecked}
177
- indeterminate={!allChecked && someChecked}
178
- inputProps={{ 'aria-labelledby': `${labelId}-all` }}
179
- />
180
- </ListItemIcon>
181
- <ListItemText
182
- id={`${labelId}-all`}
183
- primary={allSelectedLabel}
184
- primaryTypographyProps={{ variant: 'body2' }}
185
- />
186
- </ListItemButton>
187
- <Divider />
169
+ {/* "All selected" master row only makes sense over the full
170
+ list — hide it while a search is narrowing the results. */}
171
+ {!normalized && (
172
+ <>
173
+ <ListItemButton dense sx={rowSx} onClick={toggleAll}>
174
+ <ListItemIcon sx={checkboxSlotSx}>
175
+ <Checkbox
176
+ edge='start'
177
+ tabIndex={-1}
178
+ disableRipple
179
+ sx={{ p: 0.5 }}
180
+ checked={allChecked}
181
+ indeterminate={!allChecked && someChecked}
182
+ inputProps={{ 'aria-labelledby': `${labelId}-all` }}
183
+ />
184
+ </ListItemIcon>
185
+ <ListItemText
186
+ id={`${labelId}-all`}
187
+ primary={allSelectedLabel}
188
+ primaryTypographyProps={{ variant: 'body2' }}
189
+ />
190
+ </ListItemButton>
191
+ <Divider />
192
+ </>
193
+ )}
188
194
  {filtered.length === 0 ? (
189
195
  <Typography variant='body2' color='text.secondary' sx={{ p: 2 }}>
190
196
  {empty}
@@ -0,0 +1,36 @@
1
+ import { Alert, AlertTitle, Snackbar } from '@mui/material'
2
+ import type { LassoMessageUIProps } from './types'
3
+
4
+ /**
5
+ * Presentational counterpart of `LassoTools.Message` (`@carto/ps-react-maps`),
6
+ * positioned by that render-props component. Auto-hides after 5 seconds via
7
+ * MUI `Snackbar` (calls `onDismiss`, which removes the message from the store).
8
+ */
9
+ export function LassoMessageUI({
10
+ message,
11
+ onDismiss,
12
+ AlertProps,
13
+ SnackbarProps,
14
+ }: LassoMessageUIProps) {
15
+ return (
16
+ <Snackbar
17
+ open
18
+ autoHideDuration={5000}
19
+ // 'clickaway' is ignored so map interactions don't dismiss the message.
20
+ onClose={(_, reason) => reason !== 'clickaway' && onDismiss()}
21
+ // Stay in flow — the maps-side `LassoTools.Message` owns positioning.
22
+ sx={{ position: 'static', transform: 'none' }}
23
+ {...SnackbarProps}
24
+ >
25
+ <Alert
26
+ severity={message.severity ?? 'info'}
27
+ action={message.action}
28
+ onClose={onDismiss}
29
+ {...AlertProps}
30
+ >
31
+ <AlertTitle>{message.title}</AlertTitle>
32
+ {message.content}
33
+ </Alert>
34
+ </Snackbar>
35
+ )
36
+ }
@@ -1,11 +1,14 @@
1
1
  import type {
2
+ AlertColor,
3
+ AlertProps,
2
4
  Box,
3
5
  ChipProps,
4
6
  DialogProps,
5
7
  Paper,
8
+ SnackbarProps,
6
9
  TooltipProps,
7
10
  } from '@mui/material'
8
- import type { ComponentProps, ComponentType } from 'react'
11
+ import type { ComponentProps, ComponentType, ReactNode } from 'react'
9
12
  import type {
10
13
  LassoToolsMode,
11
14
  LassoToolsModes,
@@ -153,6 +156,22 @@ export interface LassoGeometryToolbarUIProps {
153
156
  PaperProps?: ComponentProps<typeof Paper>
154
157
  }
155
158
 
159
+ export interface LassoMessageUIProps {
160
+ /** Message from the lasso tools store (`LassoToolsMessage` in `@carto/ps-react-maps`). */
161
+ message: {
162
+ id: string
163
+ title: string
164
+ /** Body rendered under the title. */
165
+ content?: ReactNode
166
+ /** @default 'info' */
167
+ severity?: AlertColor
168
+ action?: ReactNode
169
+ }
170
+ onDismiss: () => void
171
+ AlertProps?: Omit<AlertProps, 'severity' | 'onClose' | 'children' | 'action'>
172
+ SnackbarProps?: Omit<SnackbarProps, 'open' | 'onClose' | 'children'>
173
+ }
174
+
156
175
  export interface LassoLayerFilterItem {
157
176
  id: string
158
177
  label: string
@@ -31,6 +31,16 @@ describe('<MarkdownContent>', () => {
31
31
  expect(img?.getAttribute('alt')).toBe('alt')
32
32
  })
33
33
 
34
+ it('parses GFM tables only when gfm=true', () => {
35
+ const table = '| a | b |\n| --- | --- |\n| 1 | 2 |'
36
+ const { container: plain } = render(<MarkdownContent content={table} />)
37
+ // Without the flag, GFM syntax stays CommonMark text — no table element.
38
+ expect(plain.querySelector('table')).toBeNull()
39
+ const { container } = render(<MarkdownContent content={table} gfm />)
40
+ expect(container.querySelector('table')).not.toBeNull()
41
+ expect(container.querySelector('td')?.textContent).toBe('1')
42
+ })
43
+
34
44
  it('parses raw HTML when allowHtml=true', () => {
35
45
  const { container } = render(
36
46
  <MarkdownContent content={'<span>raw <em>html</em></span>'} allowHtml />,
@@ -1,6 +1,7 @@
1
1
  import ReactMarkdown, { type Components } from 'react-markdown'
2
2
  import rehypeRaw from 'rehype-raw'
3
3
  import rehypeSanitize from 'rehype-sanitize'
4
+ import remarkGfm from 'remark-gfm'
4
5
 
5
6
  export interface MarkdownContentProps {
6
7
  /** Markdown source. */
@@ -21,6 +22,12 @@ export interface MarkdownContentProps {
21
22
  * Defaults to `false` so caption slots stay text-only by default.
22
23
  */
23
24
  allowImages?: boolean
25
+ /**
26
+ * When `true`, enables GitHub Flavored Markdown (tables, strikethrough,
27
+ * task lists, autolinks) via `remark-gfm`. Defaults to `false` so existing
28
+ * consumers keep CommonMark-only parsing.
29
+ */
30
+ gfm?: boolean
24
31
  }
25
32
 
26
33
  // Allowlist of URL schemes considered safe for `<a href>` and `<img src>`.
@@ -56,6 +63,7 @@ export function MarkdownContent({
56
63
  components,
57
64
  allowHtml = false,
58
65
  allowImages = false,
66
+ gfm = false,
59
67
  }: MarkdownContentProps) {
60
68
  const disallowed = allowImages ? undefined : ['img']
61
69
  return (
@@ -63,6 +71,7 @@ export function MarkdownContent({
63
71
  components={components}
64
72
  disallowedElements={disallowed}
65
73
  skipHtml={!allowHtml}
74
+ remarkPlugins={gfm ? [remarkGfm] : undefined}
66
75
  rehypePlugins={allowHtml ? [rehypeRaw, rehypeSanitize] : undefined}
67
76
  urlTransform={safeUrlTransform}
68
77
  >
@@ -141,7 +141,10 @@ export function Wrapper({
141
141
  }
142
142
  aria-label={ariaLabel}
143
143
  aria-disabled={disabled || undefined}
144
- sx={{ ...styles.summary, ...(disabled ? styles.summaryDisabled : null) }}
144
+ sx={{
145
+ ...styles.summary,
146
+ ...(disabled ? styles.summaryDisabled : null),
147
+ }}
145
148
  >
146
149
  <SmartTooltip title={title}>
147
150
  {({ ref }) => (