@compsych/mobile-ui 1.0.22 → 1.0.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compsych/mobile-ui",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "ComPsych React Native Design System — 19 mobile UI components with self-contained design tokens",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -13,7 +13,8 @@
13
13
  "test": "jest",
14
14
  "test:watch": "jest --watch",
15
15
  "test:coverage": "jest --coverage",
16
- "prepare": "husky"
16
+ "prepare": "husky",
17
+ "postinstall": "rm -rf node_modules/react-native node_modules/react node_modules/react-native-linear-gradient node_modules/react-native-svg node_modules/lucide-react-native"
17
18
  },
18
19
  "keywords": [
19
20
  "react-native",
@@ -32,17 +33,12 @@
32
33
  "peerDependencies": {
33
34
  "@expo/vector-icons": ">=15.0.0",
34
35
  "expo-font": ">=13.0.0",
35
- "expo-linear-gradient": ">=13.0.0",
36
36
  "lucide-react-native": ">=0.400.0",
37
37
  "react": ">=18.0.0",
38
38
  "react-native": ">=0.73.0",
39
+ "react-native-linear-gradient": ">=2.0.0",
39
40
  "react-native-svg": ">=15.0.0"
40
41
  },
41
- "peerDependenciesMeta": {
42
- "expo-linear-gradient": {
43
- "optional": true
44
- }
45
- },
46
42
  "devDependencies": {
47
43
  "@babel/core": "^7.25.2",
48
44
  "@babel/plugin-transform-flow-strip-types": "^7.27.1",
@@ -39,19 +39,21 @@ export function ChatBubble({
39
39
  const tokenStyles = useMemo(
40
40
  () => ({
41
41
  bubble: {
42
- maxWidth: '80%' as const,
43
- paddingVertical: dim.spacing.padding.sysPadding8,
44
- paddingHorizontal: dim.spacing.padding.sysPadding12,
45
- borderRadius: dim.borderRadius.sysRadiusMd,
42
+ paddingVertical: dim.spacing.padding.sysPadding12,
43
+ paddingHorizontal: dim.spacing.padding.sysPadding16,
46
44
  },
47
45
  bubbleIncoming: {
48
- backgroundColor: cr.surface.surfaceContainer.sysSurfaceContainerLowest,
49
- borderWidth: dim.borderWidth.sysStrokeThin,
50
- borderColor: cr.outline.sysOutlineVariant,
46
+ backgroundColor: cr.surface.surfaceContainer.sysSurfaceContainer,
47
+ borderTopLeftRadius: dim.borderRadius.sysRadiusLg,
48
+ borderTopRightRadius: dim.borderRadius.sysRadiusLg,
51
49
  borderBottomLeftRadius: dim.borderRadius.sysRadiusXs,
50
+ borderBottomRightRadius: dim.borderRadius.sysRadiusXs,
52
51
  },
53
52
  bubbleOutgoing: {
54
53
  backgroundColor: cr.accent.primary.sysPrimary,
54
+ borderTopLeftRadius: dim.borderRadius.sysRadiusXl,
55
+ borderTopRightRadius: dim.borderRadius.sysRadiusXl,
56
+ borderBottomLeftRadius: dim.borderRadius.sysRadiusXl,
55
57
  borderBottomRightRadius: dim.borderRadius.sysRadiusXs,
56
58
  },
57
59
  metaRow: {
@@ -84,7 +86,7 @@ export function ChatBubble({
84
86
  style,
85
87
  ]}
86
88
  >
87
- <View>
89
+ <View style={styles.bubbleWrapper}>
88
90
  {/* Bubble */}
89
91
  <View
90
92
  style={[
@@ -94,11 +96,7 @@ export function ChatBubble({
94
96
  : tokenStyles.bubbleIncoming,
95
97
  ]}
96
98
  >
97
- <BodyText
98
- variant="medium"
99
- color={textColor}
100
- style={styles.messageText}
101
- >
99
+ <BodyText variant="medium" color={textColor}>
102
100
  {message}
103
101
  </BodyText>
104
102
  </View>
@@ -168,9 +166,11 @@ const styles = StyleSheet.create({
168
166
  },
169
167
  rowIncoming: {
170
168
  justifyContent: 'flex-start',
169
+ paddingRight: 64,
171
170
  },
172
171
  rowOutgoing: {
173
172
  justifyContent: 'flex-end',
173
+ paddingLeft: 32,
174
174
  },
175
175
  metaIncoming: {
176
176
  justifyContent: 'flex-start',
@@ -178,9 +178,7 @@ const styles = StyleSheet.create({
178
178
  metaOutgoing: {
179
179
  justifyContent: 'flex-end',
180
180
  },
181
- messageText: {
182
- flexShrink: 1,
183
- },
181
+ bubbleWrapper: {},
184
182
  pressed: {
185
183
  opacity: 0.6,
186
184
  },
@@ -32,6 +32,8 @@ export interface ChatInputProps {
32
32
  attachments?: ChatAttachment[];
33
33
  onRemoveAttachment?: (index: number) => void;
34
34
  disabled?: boolean;
35
+ /** Optional status banner rendered above the input (e.g. <StatusChip />) */
36
+ statusChip?: React.ReactNode;
35
37
  style?: StyleProp<ViewStyle>;
36
38
  }
37
39
 
@@ -44,6 +46,7 @@ export function ChatInput({
44
46
  attachments,
45
47
  onRemoveAttachment,
46
48
  disabled = false,
49
+ statusChip,
47
50
  style,
48
51
  }: ChatInputProps) {
49
52
  const { colorRoles: cr, dimensions: dim, iconography: ico } = useTheme();
@@ -111,128 +114,134 @@ export function ChatInput({
111
114
  );
112
115
 
113
116
  return (
114
- <View style={[styles.wrapper, disabled && styles.disabled, style]}>
115
- {/* Attachment strip */}
116
- {hasAttachments && (
117
- <ScrollView
118
- horizontal
119
- showsHorizontalScrollIndicator={false}
120
- style={styles.attachStrip}
121
- contentContainerStyle={styles.attachStripContent}
117
+ <View style={[styles.wrapper, style]}>
118
+ {/* Status chip */}
119
+ {statusChip}
120
+
121
+ {/* Input row */}
122
+ <View style={[disabled && styles.disabled]}>
123
+ {/* Attachment strip */}
124
+ {hasAttachments && (
125
+ <ScrollView
126
+ horizontal
127
+ showsHorizontalScrollIndicator={false}
128
+ style={styles.attachStrip}
129
+ contentContainerStyle={styles.attachStripContent}
130
+ >
131
+ {attachments!.map((att, i) => (
132
+ <View key={i} style={styles.attachItem}>
133
+ <Image
134
+ source={att.source ?? { uri: att.uri }}
135
+ style={tokenStyles.attachThumb}
136
+ resizeMode="cover"
137
+ accessibilityLabel={`Attachment ${i + 1}`}
138
+ />
139
+ {onRemoveAttachment && (
140
+ <Pressable
141
+ onPress={() => onRemoveAttachment(i)}
142
+ style={styles.removeBtn}
143
+ accessibilityRole="button"
144
+ accessibilityLabel={`Remove attachment ${i + 1}`}
145
+ >
146
+ <Ionicons
147
+ name="close-circle"
148
+ size={16}
149
+ color={cr.surface.surface.sysOnSurfaceVariant}
150
+ />
151
+ </Pressable>
152
+ )}
153
+ </View>
154
+ ))}
155
+ </ScrollView>
156
+ )}
157
+
158
+ {/* Input row */}
159
+ <View
160
+ style={[
161
+ tokenStyles.container,
162
+ styles.inputRow,
163
+ isFocused && {
164
+ // focus ring via transparent primary halo
165
+ shadowColor: cr.accent.primary.sysPrimary,
166
+ shadowOffset: { width: 0, height: 0 },
167
+ shadowOpacity: 0.08,
168
+ shadowRadius: 4,
169
+ },
170
+ ]}
122
171
  >
123
- {attachments!.map((att, i) => (
124
- <View key={i} style={styles.attachItem}>
125
- <Image
126
- source={att.source ?? { uri: att.uri }}
127
- style={tokenStyles.attachThumb}
128
- resizeMode="cover"
129
- accessibilityLabel={`Attachment ${i + 1}`}
172
+ {/* Attach button */}
173
+ {onAttach && (
174
+ <Pressable
175
+ onPress={onAttach}
176
+ disabled={disabled}
177
+ style={({ pressed }) => [
178
+ tokenStyles.attachBtn,
179
+ pressed && styles.pressed,
180
+ ]}
181
+ accessibilityRole="button"
182
+ accessibilityLabel="Add attachment"
183
+ >
184
+ <Ionicons
185
+ name="add"
186
+ size={ico.sysSizeSm}
187
+ color={
188
+ disabled
189
+ ? cr.surface.surface.sysOnSurfaceVariant
190
+ : cr.surface.surface.sysOnSurface
191
+ }
130
192
  />
131
- {onRemoveAttachment && (
132
- <Pressable
133
- onPress={() => onRemoveAttachment(i)}
134
- style={styles.removeBtn}
135
- accessibilityRole="button"
136
- accessibilityLabel={`Remove attachment ${i + 1}`}
137
- >
138
- <Ionicons
139
- name="close-circle"
140
- size={16}
141
- color={cr.surface.surface.sysOnSurfaceVariant}
142
- />
143
- </Pressable>
144
- )}
145
- </View>
146
- ))}
147
- </ScrollView>
148
- )}
193
+ </Pressable>
194
+ )}
149
195
 
150
- {/* Input row */}
151
- <View
152
- style={[
153
- tokenStyles.container,
154
- styles.inputRow,
155
- isFocused && {
156
- // focus ring via transparent primary halo
157
- shadowColor: cr.accent.primary.sysPrimary,
158
- shadowOffset: { width: 0, height: 0 },
159
- shadowOpacity: 0.08,
160
- shadowRadius: 4,
161
- },
162
- ]}
163
- >
164
- {/* Attach button */}
165
- {onAttach && (
196
+ {/* Text field */}
197
+ <TextInput
198
+ ref={inputRef}
199
+ value={value}
200
+ onChangeText={onChangeText}
201
+ placeholder={placeholder}
202
+ placeholderTextColor={cr.surface.surface.sysOnSurfaceVariant}
203
+ editable={!disabled}
204
+ multiline
205
+ onFocus={() => setIsFocused(true)}
206
+ onBlur={() => setIsFocused(false)}
207
+ onContentSizeChange={handleContentSizeChange}
208
+ scrollEnabled={inputHeight >= 120}
209
+ style={[
210
+ styles.textInput,
211
+ {
212
+ color: cr.surface.surface.sysOnSurface,
213
+ fontSize: 16,
214
+ lineHeight: 24,
215
+ height: inputHeight,
216
+ },
217
+ ]}
218
+ accessibilityLabel={placeholder}
219
+ returnKeyType="default"
220
+ />
221
+
222
+ {/* Send button */}
166
223
  <Pressable
167
- onPress={onAttach}
168
- disabled={disabled}
224
+ onPress={canSend ? onSend : undefined}
225
+ disabled={!canSend}
169
226
  style={({ pressed }) => [
170
- tokenStyles.attachBtn,
171
- pressed && styles.pressed,
227
+ tokenStyles.sendBtn,
228
+ pressed && canSend && styles.pressed,
172
229
  ]}
173
230
  accessibilityRole="button"
174
- accessibilityLabel="Add attachment"
231
+ accessibilityLabel="Send"
232
+ accessibilityState={{ disabled: !canSend }}
175
233
  >
176
234
  <Ionicons
177
- name="add"
235
+ name="arrow-up"
178
236
  size={ico.sysSizeSm}
179
237
  color={
180
- disabled
181
- ? cr.surface.surface.sysOnSurfaceVariant
182
- : cr.surface.surface.sysOnSurface
238
+ canSend
239
+ ? cr.custom.warning.sysOnWarning
240
+ : cr.surface.surface.sysOnSurfaceVariant
183
241
  }
184
242
  />
185
243
  </Pressable>
186
- )}
187
-
188
- {/* Text field */}
189
- <TextInput
190
- ref={inputRef}
191
- value={value}
192
- onChangeText={onChangeText}
193
- placeholder={placeholder}
194
- placeholderTextColor={cr.surface.surface.sysOnSurfaceVariant}
195
- editable={!disabled}
196
- multiline
197
- onFocus={() => setIsFocused(true)}
198
- onBlur={() => setIsFocused(false)}
199
- onContentSizeChange={handleContentSizeChange}
200
- scrollEnabled={inputHeight >= 120}
201
- style={[
202
- styles.textInput,
203
- {
204
- color: cr.surface.surface.sysOnSurface,
205
- fontSize: 16,
206
- lineHeight: 24,
207
- height: inputHeight,
208
- },
209
- ]}
210
- accessibilityLabel={placeholder}
211
- returnKeyType="default"
212
- />
213
-
214
- {/* Send button */}
215
- <Pressable
216
- onPress={canSend ? onSend : undefined}
217
- disabled={!canSend}
218
- style={({ pressed }) => [
219
- tokenStyles.sendBtn,
220
- pressed && canSend && styles.pressed,
221
- ]}
222
- accessibilityRole="button"
223
- accessibilityLabel="Send"
224
- accessibilityState={{ disabled: !canSend }}
225
- >
226
- <Ionicons
227
- name="arrow-up"
228
- size={ico.sysSizeSm}
229
- color={
230
- canSend
231
- ? cr.custom.warning.sysOnWarning
232
- : cr.surface.surface.sysOnSurfaceVariant
233
- }
234
- />
235
- </Pressable>
244
+ </View>
236
245
  </View>
237
246
  </View>
238
247
  );
@@ -240,7 +249,7 @@ export function ChatInput({
240
249
 
241
250
  const styles = StyleSheet.create({
242
251
  wrapper: {
243
- gap: 4,
252
+ gap: 12,
244
253
  },
245
254
  disabled: {
246
255
  opacity: 0.48,
@@ -1,6 +1,7 @@
1
1
  import React, { useMemo } from 'react';
2
2
 
3
3
  import {
4
+ I18nManager,
4
5
  Image,
5
6
  type ImageSourcePropType,
6
7
  Pressable,
@@ -10,7 +11,7 @@ import {
10
11
  ViewStyle,
11
12
  } from 'react-native';
12
13
 
13
- import { LinearGradient } from 'expo-linear-gradient';
14
+ import LinearGradient from 'react-native-linear-gradient';
14
15
 
15
16
  import { type IconName, SIZE_MAP, resolveIcon } from '../../icons';
16
17
  import { useTheme } from '../../theme';
@@ -202,13 +203,14 @@ export function ServiceCard({
202
203
  borderWidth: v.borderWidth,
203
204
  borderRadius:
204
205
  variant === 'doubled' ? dim.borderRadius.sysRadiusXl : s.borderRadius,
205
- paddingHorizontal:
206
- variant === 'doubled' ? dim.spacing.padding.sysPadding8 : s.paddingH,
207
- paddingVertical:
208
- variant === 'doubled' ? dim.spacing.padding.sysPadding8 : s.paddingV,
206
+
209
207
  opacity: disabled ? 0.48 : 1,
210
208
  },
211
209
  v.elevated && ELEVATION,
210
+ variant !== 'doubled' && {
211
+ paddingHorizontal: s.paddingH,
212
+ paddingVertical: s.paddingV,
213
+ },
212
214
  fullWidth && { alignSelf: 'stretch' as const },
213
215
  isRow ? styles.rowRoot : styles.colRoot,
214
216
  !isRow && variant !== 'image' && { gap: s.gap },
@@ -240,10 +242,13 @@ export function ServiceCard({
240
242
  const doubledInnerStyle: StyleProp<ViewStyle> = [
241
243
  styles.colRoot,
242
244
  {
243
- borderRadius: s.borderRadius,
244
- paddingHorizontal: s.paddingH,
245
- paddingVertical: s.paddingV,
246
- gap: dim.spacing.padding.sysPadding24,
245
+ alignSelf: 'stretch',
246
+ borderRadius: dim.borderRadius.sysRadiusXl,
247
+ borderColor: cr.accent.primary.sysOnPrimary,
248
+ borderWidth: dim.spacing.padding.sysPadding8,
249
+ paddingHorizontal: dim.spacing.padding.sysPadding8,
250
+ paddingVertical: dim.spacing.padding.sysPadding16,
251
+ gap: dim.spacing.padding.sysPadding12,
247
252
  },
248
253
  ];
249
254
 
@@ -267,17 +272,21 @@ export function ServiceCard({
267
272
  </>
268
273
  );
269
274
 
270
- inner = gradient ? (
271
- <LinearGradient
272
- colors={gradient}
273
- start={gradientStart ?? { x: 0, y: 0 }}
274
- end={gradientEnd ?? { x: 0, y: 1 }}
275
- style={doubledInnerStyle}
275
+ inner = (
276
+ <View
277
+ style={[doubledInnerStyle, !gradient && { backgroundColor: v.innerBg }]}
276
278
  >
277
- {doubledInnerContent}
278
- </LinearGradient>
279
- ) : (
280
- <View style={[doubledInnerStyle, { backgroundColor: v.innerBg }]}>
279
+ {gradient && (
280
+ <LinearGradient
281
+ colors={gradient}
282
+ start={gradientStart ?? { x: 0, y: 0 }}
283
+ end={gradientEnd ?? { x: 0, y: 1 }}
284
+ style={[
285
+ StyleSheet.absoluteFillObject,
286
+ { borderRadius: dim.borderRadius.sysRadiusLg },
287
+ ]}
288
+ />
289
+ )}
281
290
  {doubledInnerContent}
282
291
  </View>
283
292
  );
@@ -296,7 +305,11 @@ export function ServiceCard({
296
305
  accessible={false}
297
306
  />
298
307
  <LinearGradient
299
- colors={['rgba(0,0,0,0.4)', 'rgba(0,0,0,0.05)', 'rgba(0,0,0,0.5)']}
308
+ colors={[
309
+ 'rgba(0,0,0,0.4)',
310
+ 'rgba(0,0,0,0.05)',
311
+ 'rgba(0,0,0,0.5)',
312
+ ]}
300
313
  locations={[0.012, 0.268, 1]}
301
314
  style={[
302
315
  { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 },
@@ -313,7 +326,11 @@ export function ServiceCard({
313
326
  <View style={{ flex: 1 }}>{textBlock}</View>
314
327
  {onPress && ChevronRight && (
315
328
  <View style={styles.imageArrowButton}>
316
- <ChevronRight size={16} color={cr.surface.surface.sysOnSurface} strokeWidth={1.5} />
329
+ <ChevronRight
330
+ size={16}
331
+ color={cr.surface.surface.sysOnSurface}
332
+ strokeWidth={1.5}
333
+ />
317
334
  </View>
318
335
  )}
319
336
  </View>
@@ -352,7 +369,9 @@ export function ServiceCard({
352
369
  style={{
353
370
  position: 'absolute',
354
371
  bottom: s.paddingV,
355
- right: s.paddingH,
372
+ ...(I18nManager.isRTL
373
+ ? { left: s.paddingH }
374
+ : { right: s.paddingH }),
356
375
  }}
357
376
  pointerEvents="none"
358
377
  >
@@ -0,0 +1,47 @@
1
+ import React from 'react';
2
+
3
+ import { render, screen } from '@testing-library/react-native';
4
+
5
+ import { ThemeProvider } from '../../theme';
6
+ import { StatusChip } from './index';
7
+
8
+ function renderWithTheme(ui: React.ReactElement) {
9
+ return render(<ThemeProvider>{ui}</ThemeProvider>);
10
+ }
11
+
12
+ describe('StatusChip', () => {
13
+ it('renders the label', () => {
14
+ renderWithTheme(<StatusChip label="10 minutes remaining" />);
15
+ expect(screen.getByText('10 minutes remaining')).toBeTruthy();
16
+ });
17
+
18
+ it('renders warning variant by default', () => {
19
+ renderWithTheme(<StatusChip label="Warning" />);
20
+ expect(screen.getByText('Warning')).toBeTruthy();
21
+ });
22
+
23
+ it('renders error variant', () => {
24
+ renderWithTheme(<StatusChip label="Session ended" variant="error" />);
25
+ expect(screen.getByText('Session ended')).toBeTruthy();
26
+ });
27
+
28
+ it('accepts a custom icon', () => {
29
+ renderWithTheme(<StatusChip label="Custom" icon="TimerIcon" />);
30
+ expect(screen.getByText('Custom')).toBeTruthy();
31
+ });
32
+
33
+ it('renders with left alignment', () => {
34
+ renderWithTheme(<StatusChip label="Left" align="left" />);
35
+ expect(screen.getByText('Left')).toBeTruthy();
36
+ });
37
+
38
+ it('renders with right alignment', () => {
39
+ renderWithTheme(<StatusChip label="Right" align="right" />);
40
+ expect(screen.getByText('Right')).toBeTruthy();
41
+ });
42
+
43
+ it('renders fullWidth', () => {
44
+ renderWithTheme(<StatusChip label="Full" fullWidth />);
45
+ expect(screen.getByText('Full')).toBeTruthy();
46
+ });
47
+ });
@@ -0,0 +1,90 @@
1
+ import React from 'react';
2
+
3
+ import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native';
4
+
5
+ import { type IconName, SIZE_MAP, resolveIcon } from '../../icons';
6
+ import { useTheme } from '../../theme';
7
+ import { BodyText } from '../BodyText';
8
+
9
+ export type StatusChipVariant = 'warning' | 'error';
10
+ export type StatusChipAlign = 'left' | 'center' | 'right';
11
+
12
+ export interface StatusChipProps {
13
+ label: string;
14
+ variant?: StatusChipVariant;
15
+ /** Icon name — defaults to ClockFadingIcon (warning) or ClockAlertIcon (error) */
16
+ icon?: IconName;
17
+ align?: StatusChipAlign;
18
+ fullWidth?: boolean;
19
+ style?: StyleProp<ViewStyle>;
20
+ }
21
+
22
+ const DEFAULT_ICONS: Record<StatusChipVariant, IconName> = {
23
+ warning: 'ClockFadingIcon',
24
+ error: 'ClockAlertIcon',
25
+ };
26
+
27
+ const JUSTIFY: Record<StatusChipAlign, 'flex-start' | 'center' | 'flex-end'> = {
28
+ left: 'flex-start',
29
+ center: 'center',
30
+ right: 'flex-end',
31
+ };
32
+
33
+ export function StatusChip({
34
+ label,
35
+ variant = 'warning',
36
+ icon,
37
+ align = 'center',
38
+ fullWidth = false,
39
+ style,
40
+ }: StatusChipProps) {
41
+ const { colorRoles: cr } = useTheme();
42
+
43
+ const colors =
44
+ variant === 'warning'
45
+ ? {
46
+ bg: cr.custom.warning.sysWarningContainer,
47
+ text: cr.custom.warning.sysOnWarningContainer,
48
+ }
49
+ : {
50
+ bg: cr.error.sysErrorContainer,
51
+ text: cr.error.sysOnErrorContainer,
52
+ };
53
+
54
+ const iconName = icon ?? DEFAULT_ICONS[variant];
55
+ const IconComponent = resolveIcon(iconName);
56
+ const { size: iconPx, strokeWidth: iconSW } = SIZE_MAP['small'];
57
+
58
+ return (
59
+ <View
60
+ style={[
61
+ styles.chip,
62
+ {
63
+ backgroundColor: colors.bg,
64
+ justifyContent: JUSTIFY[align],
65
+ alignSelf: fullWidth ? ('stretch' as const) : ('flex-start' as const),
66
+ },
67
+ style,
68
+ ]}
69
+ >
70
+ {IconComponent && (
71
+ <IconComponent size={iconPx} strokeWidth={iconSW} color={colors.text} />
72
+ )}
73
+ <BodyText variant="labelMedium" color={colors.text} numberOfLines={1}>
74
+ {label}
75
+ </BodyText>
76
+ </View>
77
+ );
78
+ }
79
+
80
+ const styles = StyleSheet.create({
81
+ chip: {
82
+ flexDirection: 'row',
83
+ alignItems: 'center',
84
+ borderRadius: 9999,
85
+ paddingHorizontal: 16,
86
+ paddingVertical: 4,
87
+ gap: 8,
88
+ height: 32,
89
+ },
90
+ });
package/src/index.ts CHANGED
@@ -132,6 +132,13 @@ export type {
132
132
  export { ChatInput } from './components/ChatInput';
133
133
  export type { ChatInputProps, ChatAttachment } from './components/ChatInput';
134
134
 
135
+ export { StatusChip } from './components/StatusChip';
136
+ export type {
137
+ StatusChipProps,
138
+ StatusChipVariant,
139
+ StatusChipAlign,
140
+ } from './components/StatusChip';
141
+
135
142
  export { BodyText } from './components/BodyText';
136
143
  export type { BodyTextProps, BodyVariant } from './components/BodyText';
137
144