@mereb/app-feed 0.0.4 → 0.0.5

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.
@@ -0,0 +1,60 @@
1
+ import { gql } from '@apollo/client';
2
+ export { FeedDocument } from '@mereb/shared-graphql';
3
+ export const CreatePostDocument = gql `
4
+ mutation CreatePost($body: String!, $mediaKeys: [String!]) {
5
+ createPost(body: $body, mediaKeys: $mediaKeys) {
6
+ id
7
+ body
8
+ createdAt
9
+ author {
10
+ id
11
+ handle
12
+ }
13
+ }
14
+ }
15
+ `;
16
+ export const FEED_PAGE_SIZE = 20;
17
+ const MINUTE = 60 * 1000;
18
+ const HOUR = 60 * MINUTE;
19
+ const DAY = 24 * HOUR;
20
+ export function parseFeedTimestamp(value) {
21
+ if (!value) {
22
+ return null;
23
+ }
24
+ const asNumber = Number(value);
25
+ if (!Number.isNaN(asNumber) && asNumber > 0) {
26
+ const date = new Date(asNumber);
27
+ return Number.isNaN(date.getTime()) ? null : date;
28
+ }
29
+ const date = new Date(value);
30
+ return Number.isNaN(date.getTime()) ? null : date;
31
+ }
32
+ export function formatRelativeFeedTimestamp(value, now = Date.now()) {
33
+ const date = parseFeedTimestamp(value);
34
+ if (!date) {
35
+ return 'Just now';
36
+ }
37
+ const diff = now - date.getTime();
38
+ if (diff < MINUTE) {
39
+ return 'Just now';
40
+ }
41
+ if (diff < HOUR) {
42
+ const mins = Math.round(diff / MINUTE);
43
+ return `${mins} min${mins === 1 ? '' : 's'} ago`;
44
+ }
45
+ if (diff < DAY) {
46
+ const hours = Math.round(diff / HOUR);
47
+ return `${hours} hr${hours === 1 ? '' : 's'} ago`;
48
+ }
49
+ return date.toLocaleDateString(undefined, {
50
+ year: date.getFullYear() === new Date().getFullYear() ? undefined : 'numeric',
51
+ month: 'short',
52
+ day: 'numeric'
53
+ });
54
+ }
55
+ export function selectFeedNodes(data) {
56
+ if (!data?.feedHome?.edges?.length) {
57
+ return [];
58
+ }
59
+ return data.feedHome.edges.map((edge) => edge.node);
60
+ }
@@ -1,5 +1,5 @@
1
1
  type FeedScreenProps = {
2
2
  onSelectAuthor?: (handle: string) => void;
3
3
  };
4
- export declare function FeedScreen({ onSelectAuthor }: Readonly<FeedScreenProps>): import("react").JSX.Element;
4
+ export declare function FeedScreen({ onSelectAuthor }: Readonly<FeedScreenProps>): import("react/jsx-runtime.js").JSX.Element;
5
5
  export {};
package/dist/index.js ADDED
@@ -0,0 +1,155 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useMemo } from 'react';
3
+ import { FlatList, RefreshControl, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
4
+ import { useFeedQuery } from '@mereb/shared-graphql';
5
+ import { tokens } from '@mereb/tokens/native';
6
+ import { cardRecipes } from '@mereb/ui-shared';
7
+ import { formatRelativeFeedTimestamp, selectFeedNodes } from './headless.js';
8
+ function Separator({ style }) {
9
+ return _jsx(View, { style: style });
10
+ }
11
+ /* Top-level wrapper so FlatList receives a stable component reference */
12
+ function SeparatorWithStyle() {
13
+ return _jsx(Separator, { style: styles.separator });
14
+ }
15
+ function PostCard({ post, onSelectAuthor }) {
16
+ const handlePress = useCallback(() => {
17
+ onSelectAuthor?.(post.author.handle);
18
+ }, [onSelectAuthor, post.author.handle]);
19
+ return (_jsxs(View, { style: styles.card, children: [_jsxs(View, { style: styles.metaRow, children: [onSelectAuthor ? (_jsx(TouchableOpacity, { onPress: handlePress, accessibilityRole: "button", children: _jsxs(Text, { style: styles.handleLink, children: ["@", post.author.handle] }) })) : (_jsxs(Text, { style: styles.handle, children: ["@", post.author.handle] })), _jsx(Text, { style: styles.metaSeparator, children: "\u2022" }), _jsx(Text, { style: styles.timestamp, children: formatRelativeFeedTimestamp(post.createdAt) })] }), _jsx(Text, { style: styles.body, children: post.body })] }));
20
+ }
21
+ const FALLBACK_SUGGESTIONS = [
22
+ { handle: 'mereb-team', hint: 'Product updates' },
23
+ { handle: 'designers', hint: 'Design drops' },
24
+ { handle: 'dev-journal', hint: 'Engineering notes' }
25
+ ];
26
+ function SuggestionsSection({ suggestions, onSelectAuthor }) {
27
+ if (suggestions.length === 0)
28
+ return null;
29
+ return (_jsxs(View, { style: styles.suggestionsCard, children: [_jsx(Text, { style: styles.suggestionsTitle, children: "Discover people to follow" }), _jsx(Text, { style: styles.suggestionsBody, children: "Follow a few profiles to keep your feed fresh." }), _jsx(View, { style: styles.suggestionsList, children: suggestions.map((sugg) => (_jsxs(TouchableOpacity, { style: styles.suggestionPill, onPress: () => onSelectAuthor?.(sugg.handle), accessibilityRole: "button", children: [_jsxs(Text, { style: styles.suggestionHandle, children: ["@", sugg.handle] }), sugg.hint ? (_jsx(Text, { style: styles.suggestionHint, children: sugg.hint })) : null] }, sugg.handle))) })] }));
30
+ }
31
+ export function FeedScreen({ onSelectAuthor }) {
32
+ const { data, loading, error, refetch } = useFeedQuery();
33
+ const posts = useMemo(() => selectFeedNodes(data), [data]);
34
+ const suggestions = useMemo(() => {
35
+ const seen = new Set();
36
+ const derived = [];
37
+ for (const post of posts) {
38
+ if (seen.has(post.author.handle))
39
+ continue;
40
+ seen.add(post.author.handle);
41
+ derived.push({ handle: post.author.handle });
42
+ }
43
+ const combined = [...derived, ...FALLBACK_SUGGESTIONS].filter((s) => !seen.has(s.handle) // avoid dupes with fallbacks
44
+ );
45
+ return combined.slice(0, 5);
46
+ }, [posts]);
47
+ const shouldShowSuggestions = !loading && posts.length < 5;
48
+ const handleRefresh = useCallback(() => {
49
+ void refetch();
50
+ }, [refetch]);
51
+ if (error) {
52
+ return (_jsx(View, { style: styles.centered, children: _jsx(Text, { style: styles.errorText, children: "Failed to load feed" }) }));
53
+ }
54
+ return (_jsx(FlatList, { data: posts, keyExtractor: (item) => item.id, contentContainerStyle: styles.listContent, ItemSeparatorComponent: SeparatorWithStyle, refreshControl: _jsx(RefreshControl, { refreshing: loading, onRefresh: handleRefresh }), ListHeaderComponent: shouldShowSuggestions ? (_jsx(SuggestionsSection, { suggestions: suggestions, onSelectAuthor: onSelectAuthor })) : null, renderItem: ({ item }) => (_jsx(PostCard, { post: item, onSelectAuthor: onSelectAuthor })), ListEmptyComponent: loading ? null : (_jsxs(View, { style: styles.centered, children: [_jsx(Text, { style: styles.emptyTitle, children: "No posts yet" }), _jsx(Text, { style: styles.emptyBody, children: "Pull down to refresh or check back later for new updates." })] })) }));
55
+ }
56
+ const { color, spacing, radius, shadow } = tokens;
57
+ const feedCardRecipe = cardRecipes.elevated;
58
+ const styles = StyleSheet.create({
59
+ listContent: {
60
+ padding: spacing.lg,
61
+ gap: spacing.md
62
+ },
63
+ separator: {
64
+ height: 0
65
+ },
66
+ card: {
67
+ backgroundColor: color[feedCardRecipe.backgroundColor],
68
+ borderRadius: radius[feedCardRecipe.radius],
69
+ padding: spacing[feedCardRecipe.padding],
70
+ ...shadow[feedCardRecipe.elevation]
71
+ },
72
+ metaRow: {
73
+ flexDirection: 'row',
74
+ alignItems: 'center',
75
+ marginBottom: spacing.sm
76
+ },
77
+ handle: {
78
+ fontWeight: '600',
79
+ color: color.text
80
+ },
81
+ handleLink: {
82
+ fontWeight: '600',
83
+ color: color.primary
84
+ },
85
+ metaSeparator: {
86
+ marginHorizontal: spacing.xs,
87
+ color: color.textMuted
88
+ },
89
+ timestamp: {
90
+ color: color.textMuted
91
+ },
92
+ body: {
93
+ fontSize: 16,
94
+ lineHeight: 22,
95
+ color: color.text
96
+ },
97
+ centered: {
98
+ flex: 1,
99
+ padding: spacing.xxxl,
100
+ alignItems: 'center',
101
+ justifyContent: 'center'
102
+ },
103
+ errorText: {
104
+ fontSize: 16,
105
+ color: '#d22c2c'
106
+ },
107
+ emptyTitle: {
108
+ fontSize: 18,
109
+ fontWeight: '600',
110
+ color: color.text,
111
+ marginBottom: spacing.sm
112
+ },
113
+ emptyBody: {
114
+ fontSize: 14,
115
+ color: color.textMuted,
116
+ textAlign: 'center'
117
+ },
118
+ suggestionsCard: {
119
+ backgroundColor: color.surface,
120
+ borderRadius: radius.lg,
121
+ padding: spacing.lg,
122
+ marginBottom: spacing.lg
123
+ },
124
+ suggestionsTitle: {
125
+ fontSize: 18,
126
+ fontWeight: '700',
127
+ color: color.text,
128
+ marginBottom: spacing.xs
129
+ },
130
+ suggestionsBody: {
131
+ color: color.textMuted,
132
+ marginBottom: spacing.md
133
+ },
134
+ suggestionsList: {
135
+ flexDirection: 'row',
136
+ flexWrap: 'wrap',
137
+ gap: spacing.sm
138
+ },
139
+ suggestionPill: {
140
+ paddingVertical: spacing.xs,
141
+ paddingHorizontal: spacing.md,
142
+ borderRadius: radius.pill ?? radius.lg,
143
+ backgroundColor: color.neutralMuted ?? color.surfaceMuted,
144
+ flexDirection: 'column',
145
+ gap: spacing.xxs
146
+ },
147
+ suggestionHandle: {
148
+ color: color.text,
149
+ fontWeight: '600'
150
+ },
151
+ suggestionHint: {
152
+ color: color.textMuted,
153
+ fontSize: 12
154
+ }
155
+ });
@@ -0,0 +1 @@
1
+ export * from './index.js';
package/dist/native.js ADDED
@@ -0,0 +1 @@
1
+ export * from './index.js';
package/package.json CHANGED
@@ -1,28 +1,34 @@
1
1
  {
2
2
  "name": "@mereb/app-feed",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Feed experience building blocks for Mereb apps",
5
5
  "type": "module",
6
- "main": "src/index.tsx",
7
- "types": "src/index.tsx",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "types": "./src/index.tsx",
11
- "default": "./src/index.tsx"
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./native": {
15
+ "types": "./dist/native.d.ts",
16
+ "import": "./dist/native.js",
17
+ "default": "./dist/native.js"
12
18
  },
13
19
  "./headless": {
14
- "types": "./src/headless.ts",
15
- "default": "./src/headless.ts"
20
+ "types": "./dist/headless.d.ts",
21
+ "import": "./dist/headless.js",
22
+ "default": "./dist/headless.js"
16
23
  }
17
24
  },
18
25
  "files": [
19
- "src",
20
26
  "dist",
21
27
  "package.json"
22
28
  ],
23
29
  "dependencies": {
24
- "@mereb/shared-graphql": "^0.0.9",
25
- "@mereb/ui-shared": "^0.0.1",
30
+ "@mereb/shared-graphql": "^0.0.11",
31
+ "@mereb/ui-shared": "^0.0.3",
26
32
  "@mereb/tokens": "^0.0.4"
27
33
  },
28
34
  "peerDependencies": {
@@ -33,10 +39,16 @@
33
39
  },
34
40
  "devDependencies": {
35
41
  "@types/react": "~18.2.79",
36
- "typescript": ">=5.3.3"
42
+ "husky": "^9.1.7",
43
+ "typescript": ">=5.3.3",
44
+ "vitest": "^3.2.4"
37
45
  },
38
46
  "scripts": {
47
+ "clean": "node -e \"const fs=require('node:fs'); fs.rmSync('dist',{recursive:true,force:true}); fs.rmSync('tsconfig.tsbuildinfo',{force:true});\"",
39
48
  "typecheck": "tsc --noEmit --project tsconfig.json",
40
- "build": "tsc --project tsconfig.json"
49
+ "test": "pnpm exec vitest run --config vitest.config.ts",
50
+ "test:watch": "pnpm exec vitest --config vitest.config.ts",
51
+ "build": "pnpm run clean && tsc --project tsconfig.json",
52
+ "version:bump": "node ./scripts/bump-version.mjs"
41
53
  }
42
54
  }
package/src/headless.ts DELETED
@@ -1,86 +0,0 @@
1
- import { gql } from '@apollo/client';
2
- import type { FeedQuery } from '@mereb/shared-graphql';
3
- export { FeedDocument } from '@mereb/shared-graphql';
4
-
5
- export type FeedNode = FeedQuery['feedHome']['edges'][number]['node'];
6
- export type CreatePostResponse = {
7
- createPost: {
8
- id: string;
9
- body: string;
10
- createdAt: string;
11
- author: {
12
- id: string;
13
- handle: string;
14
- };
15
- };
16
- };
17
- export type CreatePostVariables = {
18
- body: string;
19
- mediaKeys?: string[];
20
- };
21
-
22
- export const CreatePostDocument = gql`
23
- mutation CreatePost($body: String!, $mediaKeys: [String!]) {
24
- createPost(body: $body, mediaKeys: $mediaKeys) {
25
- id
26
- body
27
- createdAt
28
- author {
29
- id
30
- handle
31
- }
32
- }
33
- }
34
- `;
35
-
36
- export const FEED_PAGE_SIZE = 20;
37
-
38
- const MINUTE = 60 * 1000;
39
- const HOUR = 60 * MINUTE;
40
- const DAY = 24 * HOUR;
41
-
42
- export function parseFeedTimestamp(value?: string | null): Date | null {
43
- if (!value) {
44
- return null;
45
- }
46
- const asNumber = Number(value);
47
- if (!Number.isNaN(asNumber) && asNumber > 0) {
48
- const date = new Date(asNumber);
49
- return Number.isNaN(date.getTime()) ? null : date;
50
- }
51
- const date = new Date(value);
52
- return Number.isNaN(date.getTime()) ? null : date;
53
- }
54
-
55
- export function formatRelativeFeedTimestamp(value?: string | null, now: number = Date.now()): string {
56
- const date = parseFeedTimestamp(value);
57
- if (!date) {
58
- return 'Just now';
59
- }
60
-
61
- const diff = now - date.getTime();
62
- if (diff < MINUTE) {
63
- return 'Just now';
64
- }
65
- if (diff < HOUR) {
66
- const mins = Math.round(diff / MINUTE);
67
- return `${mins} min${mins === 1 ? '' : 's'} ago`;
68
- }
69
- if (diff < DAY) {
70
- const hours = Math.round(diff / HOUR);
71
- return `${hours} hr${hours === 1 ? '' : 's'} ago`;
72
- }
73
-
74
- return date.toLocaleDateString(undefined, {
75
- year: date.getFullYear() === new Date().getFullYear() ? undefined : 'numeric',
76
- month: 'short',
77
- day: 'numeric'
78
- });
79
- }
80
-
81
- export function selectFeedNodes(data?: FeedQuery | null): FeedNode[] {
82
- if (!data?.feedHome?.edges?.length) {
83
- return [];
84
- }
85
- return data.feedHome.edges.map((edge) => edge.node);
86
- }
package/src/index.tsx DELETED
@@ -1,261 +0,0 @@
1
- import {useCallback, useMemo} from 'react';
2
- import {FlatList, RefreshControl, StyleSheet, Text, TouchableOpacity, View, type ViewStyle} from 'react-native';
3
- import {useFeedQuery} from '@mereb/shared-graphql';
4
- import {tokens} from '@mereb/tokens/native';
5
- import {cardRecipes} from '@mereb/ui-shared';
6
- import {type FeedNode, formatRelativeFeedTimestamp, selectFeedNodes} from './headless.js';
7
-
8
- type SeparatorProps = {
9
- style?: ViewStyle;
10
- };
11
-
12
- function Separator({style}: Readonly<SeparatorProps>) {
13
- return <View style={style}/>;
14
- }
15
-
16
- /* Top-level wrapper so FlatList receives a stable component reference */
17
- function SeparatorWithStyle() {
18
- return <Separator style={styles.separator} />;
19
- }
20
-
21
- type PostCardProps = {
22
- post: FeedNode;
23
- onSelectAuthor?: (handle: string) => void;
24
- };
25
-
26
- function PostCard({post, onSelectAuthor}: Readonly<PostCardProps>) {
27
- const handlePress = useCallback(() => {
28
- onSelectAuthor?.(post.author.handle);
29
- }, [onSelectAuthor, post.author.handle]);
30
-
31
- return (
32
- <View style={styles.card}>
33
- <View style={styles.metaRow}>
34
- {onSelectAuthor ? (
35
- <TouchableOpacity onPress={handlePress} accessibilityRole="button">
36
- <Text style={styles.handleLink}>@{post.author.handle}</Text>
37
- </TouchableOpacity>
38
- ) : (
39
- <Text style={styles.handle}>@{post.author.handle}</Text>
40
- )}
41
- <Text style={styles.metaSeparator}>•</Text>
42
- <Text style={styles.timestamp}>{formatRelativeFeedTimestamp(post.createdAt)}</Text>
43
- </View>
44
- <Text style={styles.body}>{post.body}</Text>
45
- </View>
46
- );
47
- }
48
-
49
- type FeedScreenProps = {
50
- onSelectAuthor?: (handle: string) => void;
51
- };
52
-
53
- type Suggestion = {
54
- handle: string;
55
- hint?: string;
56
- };
57
-
58
- const FALLBACK_SUGGESTIONS: Suggestion[] = [
59
- {handle: 'mereb-team', hint: 'Product updates'},
60
- {handle: 'designers', hint: 'Design drops'},
61
- {handle: 'dev-journal', hint: 'Engineering notes'}
62
- ];
63
-
64
- function SuggestionsSection({
65
- suggestions,
66
- onSelectAuthor
67
- }: Readonly<{
68
- suggestions: Suggestion[];
69
- onSelectAuthor?: (handle: string) => void;
70
- }>) {
71
- if (suggestions.length === 0) return null;
72
-
73
- return (
74
- <View style={styles.suggestionsCard}>
75
- <Text style={styles.suggestionsTitle}>Discover people to follow</Text>
76
- <Text style={styles.suggestionsBody}>
77
- Follow a few profiles to keep your feed fresh.
78
- </Text>
79
- <View style={styles.suggestionsList}>
80
- {suggestions.map((sugg) => (
81
- <TouchableOpacity
82
- key={sugg.handle}
83
- style={styles.suggestionPill}
84
- onPress={() => onSelectAuthor?.(sugg.handle)}
85
- accessibilityRole="button"
86
- >
87
- <Text style={styles.suggestionHandle}>@{sugg.handle}</Text>
88
- {sugg.hint ? (
89
- <Text style={styles.suggestionHint}>{sugg.hint}</Text>
90
- ) : null}
91
- </TouchableOpacity>
92
- ))}
93
- </View>
94
- </View>
95
- );
96
- }
97
-
98
- export function FeedScreen({onSelectAuthor}: Readonly<FeedScreenProps>) {
99
- const {data, loading, error, refetch} = useFeedQuery();
100
-
101
- const posts = useMemo(() => selectFeedNodes(data), [data]);
102
- const suggestions = useMemo(() => {
103
- const seen = new Set<string>();
104
- const derived: Suggestion[] = [];
105
- for (const post of posts) {
106
- if (seen.has(post.author.handle)) continue;
107
- seen.add(post.author.handle);
108
- derived.push({handle: post.author.handle});
109
- }
110
- const combined = [...derived, ...FALLBACK_SUGGESTIONS].filter(
111
- (s) => !seen.has(s.handle) // avoid dupes with fallbacks
112
- );
113
- return combined.slice(0, 5);
114
- }, [posts]);
115
- const shouldShowSuggestions = !loading && posts.length < 5;
116
-
117
- const handleRefresh = useCallback(() => {
118
- void refetch();
119
- }, [refetch]);
120
-
121
- if (error) {
122
- return (
123
- <View style={styles.centered}>
124
- <Text style={styles.errorText}>Failed to load feed</Text>
125
- </View>
126
- );
127
- }
128
-
129
- return (
130
- <FlatList
131
- data={posts}
132
- keyExtractor={(item) => item.id}
133
- contentContainerStyle={styles.listContent}
134
- ItemSeparatorComponent={SeparatorWithStyle}
135
- refreshControl={<RefreshControl refreshing={loading} onRefresh={handleRefresh}/>}
136
- ListHeaderComponent={
137
- shouldShowSuggestions ? (
138
- <SuggestionsSection
139
- suggestions={suggestions}
140
- onSelectAuthor={onSelectAuthor}
141
- />
142
- ) : null
143
- }
144
- renderItem={({item}) => (
145
- <PostCard post={item} onSelectAuthor={onSelectAuthor}/>
146
- )}
147
- ListEmptyComponent={
148
- loading ? null : (
149
- <View style={styles.centered}>
150
- <Text style={styles.emptyTitle}>No posts yet</Text>
151
- <Text style={styles.emptyBody}>
152
- Pull down to refresh or check back later for new updates.
153
- </Text>
154
- </View>
155
- )
156
- }
157
- />
158
- );
159
- }
160
-
161
- const {color, spacing, radius, shadow} = tokens;
162
- const feedCardRecipe = cardRecipes.elevated;
163
-
164
- const styles = StyleSheet.create({
165
- listContent: {
166
- padding: spacing.lg,
167
- gap: spacing.md
168
- },
169
- separator: {
170
- height: 0
171
- },
172
- card: {
173
- backgroundColor: color[feedCardRecipe.backgroundColor],
174
- borderRadius: radius[feedCardRecipe.radius],
175
- padding: spacing[feedCardRecipe.padding],
176
- ...shadow[feedCardRecipe.elevation]
177
- },
178
- metaRow: {
179
- flexDirection: 'row',
180
- alignItems: 'center',
181
- marginBottom: spacing.sm
182
- },
183
- handle: {
184
- fontWeight: '600',
185
- color: color.text
186
- },
187
- handleLink: {
188
- fontWeight: '600',
189
- color: color.primary
190
- },
191
- metaSeparator: {
192
- marginHorizontal: spacing.xs,
193
- color: color.textMuted
194
- },
195
- timestamp: {
196
- color: color.textMuted
197
- },
198
- body: {
199
- fontSize: 16,
200
- lineHeight: 22,
201
- color: color.text
202
- },
203
- centered: {
204
- flex: 1,
205
- padding: spacing.xxxl,
206
- alignItems: 'center',
207
- justifyContent: 'center'
208
- },
209
- errorText: {
210
- fontSize: 16,
211
- color: '#d22c2c'
212
- },
213
- emptyTitle: {
214
- fontSize: 18,
215
- fontWeight: '600',
216
- color: color.text,
217
- marginBottom: spacing.sm
218
- },
219
- emptyBody: {
220
- fontSize: 14,
221
- color: color.textMuted,
222
- textAlign: 'center'
223
- },
224
- suggestionsCard: {
225
- backgroundColor: color.surface,
226
- borderRadius: radius.lg,
227
- padding: spacing.lg,
228
- marginBottom: spacing.lg
229
- },
230
- suggestionsTitle: {
231
- fontSize: 18,
232
- fontWeight: '700',
233
- color: color.text,
234
- marginBottom: spacing.xs
235
- },
236
- suggestionsBody: {
237
- color: color.textMuted,
238
- marginBottom: spacing.md
239
- },
240
- suggestionsList: {
241
- flexDirection: 'row',
242
- flexWrap: 'wrap',
243
- gap: spacing.sm
244
- },
245
- suggestionPill: {
246
- paddingVertical: spacing.xs,
247
- paddingHorizontal: spacing.md,
248
- borderRadius: radius.pill ?? radius.lg,
249
- backgroundColor: color.neutralMuted ?? color.surfaceMuted,
250
- flexDirection: 'column',
251
- gap: spacing.xxs
252
- },
253
- suggestionHandle: {
254
- color: color.text,
255
- fontWeight: '600'
256
- },
257
- suggestionHint: {
258
- color: color.textMuted,
259
- fontSize: 12
260
- }
261
- });
File without changes