react-native-nitro-tracing 0.6.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 (233) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/LICENSE +21 -0
  3. package/NitroTracing.podspec +32 -0
  4. package/README.md +75 -0
  5. package/android/CMakeLists.txt +49 -0
  6. package/android/build.gradle +133 -0
  7. package/android/fix-prefab.gradle +51 -0
  8. package/android/gradle.properties +5 -0
  9. package/android/proguard-rules.pro +5 -0
  10. package/android/src/main/AndroidManifest.xml +2 -0
  11. package/android/src/main/cpp/cpp-adapter.cpp +47 -0
  12. package/android/src/main/java/com/margelo/nitro/nitrotracing/HermesSamplingGuard.kt +43 -0
  13. package/android/src/main/java/com/margelo/nitro/nitrotracing/NitroTracingFrameSource.kt +36 -0
  14. package/android/src/main/java/com/margelo/nitro/nitrotracing/NitroTracingPackage.kt +31 -0
  15. package/client.ts +15 -0
  16. package/cpp/HermesSampling.cpp +26 -0
  17. package/cpp/HermesSampling.hpp +10 -0
  18. package/cpp/HybridRecording.cpp +66 -0
  19. package/cpp/HybridRecording.hpp +30 -0
  20. package/cpp/HybridTraceSpan.cpp +10 -0
  21. package/cpp/HybridTraceSpan.hpp +17 -0
  22. package/cpp/HybridTracing.cpp +10 -0
  23. package/cpp/HybridTracing.hpp +10 -0
  24. package/cpp/NativeConversions.cpp +77 -0
  25. package/cpp/NativeConversions.hpp +15 -0
  26. package/cpp/core/NativeSampler.cpp +152 -0
  27. package/cpp/core/NativeSampler.hpp +56 -0
  28. package/cpp/core/Recorder.cpp +218 -0
  29. package/cpp/core/Recorder.hpp +60 -0
  30. package/cpp/core/Retention.cpp +26 -0
  31. package/cpp/core/Retention.hpp +7 -0
  32. package/cpp/core/TraceJson.cpp +214 -0
  33. package/cpp/core/TraceJson.hpp +7 -0
  34. package/cpp/core/TraceTypes.hpp +62 -0
  35. package/expo.ts +2 -0
  36. package/ios/NitroTracingFrameSource.mm +35 -0
  37. package/lib/commonjs/client.cjs +767 -0
  38. package/lib/commonjs/client.cjs.map +7 -0
  39. package/lib/commonjs/expo.cjs +88 -0
  40. package/lib/commonjs/expo.cjs.map +7 -0
  41. package/lib/commonjs/index.cjs +28 -0
  42. package/lib/commonjs/index.cjs.map +7 -0
  43. package/lib/commonjs/performance.cjs +112 -0
  44. package/lib/commonjs/performance.cjs.map +7 -0
  45. package/lib/commonjs/plugins.cjs +477 -0
  46. package/lib/commonjs/plugins.cjs.map +7 -0
  47. package/lib/commonjs/react.cjs +3833 -0
  48. package/lib/commonjs/react.cjs.map +7 -0
  49. package/lib/commonjs/release-profiler.cjs +142 -0
  50. package/lib/commonjs/release-profiler.cjs.map +7 -0
  51. package/lib/commonjs/sentry.cjs +188 -0
  52. package/lib/commonjs/sentry.cjs.map +7 -0
  53. package/lib/module/client.mjs +768 -0
  54. package/lib/module/client.mjs.map +7 -0
  55. package/lib/module/expo.mjs +62 -0
  56. package/lib/module/expo.mjs.map +7 -0
  57. package/lib/module/index.mjs +7 -0
  58. package/lib/module/index.mjs.map +7 -0
  59. package/lib/module/performance.mjs +89 -0
  60. package/lib/module/performance.mjs.map +7 -0
  61. package/lib/module/plugins.mjs +461 -0
  62. package/lib/module/plugins.mjs.map +7 -0
  63. package/lib/module/react.mjs +3851 -0
  64. package/lib/module/react.mjs.map +7 -0
  65. package/lib/module/release-profiler.mjs +137 -0
  66. package/lib/module/release-profiler.mjs.map +7 -0
  67. package/lib/module/sentry.mjs +165 -0
  68. package/lib/module/sentry.mjs.map +7 -0
  69. package/lib/typescript/client.d.ts +4 -0
  70. package/lib/typescript/expo.d.ts +2 -0
  71. package/lib/typescript/performance.d.ts +2 -0
  72. package/lib/typescript/plugins.d.ts +12 -0
  73. package/lib/typescript/react.d.ts +5 -0
  74. package/lib/typescript/release-profiler.d.ts +2 -0
  75. package/lib/typescript/sentry.d.ts +2 -0
  76. package/lib/typescript/src/client/TracingSink.d.ts +10 -0
  77. package/lib/typescript/src/client/createTraceClient.d.ts +82 -0
  78. package/lib/typescript/src/client/playground.d.ts +12 -0
  79. package/lib/typescript/src/expo/ExpoAdapter.d.ts +15 -0
  80. package/lib/typescript/src/expo/index.d.ts +2 -0
  81. package/lib/typescript/src/index.d.ts +6 -0
  82. package/lib/typescript/src/plugins/TracePlugin.d.ts +22 -0
  83. package/lib/typescript/src/plugins/errors.d.ts +15 -0
  84. package/lib/typescript/src/plugins/nativeMetrics.d.ts +13 -0
  85. package/lib/typescript/src/plugins/navigation.d.ts +29 -0
  86. package/lib/typescript/src/plugins/network.d.ts +23 -0
  87. package/lib/typescript/src/plugins/orderSpans.d.ts +3 -0
  88. package/lib/typescript/src/plugins/performance.d.ts +13 -0
  89. package/lib/typescript/src/plugins/releaseProfiler.d.ts +25 -0
  90. package/lib/typescript/src/plugins/runtimeMetrics.d.ts +17 -0
  91. package/lib/typescript/src/plugins/sentry.d.ts +15 -0
  92. package/lib/typescript/src/plugins/startPlugins.d.ts +4 -0
  93. package/lib/typescript/src/react/DetailView.d.ts +11 -0
  94. package/lib/typescript/src/react/ExplorerFilters.d.ts +9 -0
  95. package/lib/typescript/src/react/ExplorerView.d.ts +9 -0
  96. package/lib/typescript/src/react/InspectorControls.d.ts +229 -0
  97. package/lib/typescript/src/react/InspectorHeader.d.ts +8 -0
  98. package/lib/typescript/src/react/InspectorSheet.d.ts +16 -0
  99. package/lib/typescript/src/react/MetricsView.d.ts +7 -0
  100. package/lib/typescript/src/react/SummaryView.d.ts +11 -0
  101. package/lib/typescript/src/react/TimelineView.d.ts +8 -0
  102. package/lib/typescript/src/react/TraceInspector.d.ts +11 -0
  103. package/lib/typescript/src/react/TraceOverlay.d.ts +17 -0
  104. package/lib/typescript/src/react/explorerModel.d.ts +38 -0
  105. package/lib/typescript/src/react/labels.d.ts +203 -0
  106. package/lib/typescript/src/react/native.d.ts +42 -0
  107. package/lib/typescript/src/react/readSnapshot.d.ts +4 -0
  108. package/lib/typescript/src/react/store.d.ts +9 -0
  109. package/lib/typescript/src/react/topics.d.ts +80 -0
  110. package/lib/typescript/src/react/useAppReadyMetric.d.ts +3 -0
  111. package/lib/typescript/src/react/useLiveRecording.d.ts +4 -0
  112. package/lib/typescript/src/react/useTraceViewer.d.ts +140 -0
  113. package/lib/typescript/src/react/viewerModel.d.ts +59 -0
  114. package/lib/typescript/src/specs/Recording.nitro.d.ts +31 -0
  115. package/lib/typescript/src/specs/TraceSpan.nitro.d.ts +14 -0
  116. package/lib/typescript/src/specs/Tracing.nitro.d.ts +17 -0
  117. package/lib/typescript/src/types/CompletedSpanOptions.d.ts +15 -0
  118. package/lib/typescript/src/types/EventContext.d.ts +8 -0
  119. package/lib/typescript/src/types/MarkEvent.d.ts +4 -0
  120. package/lib/typescript/src/types/MarkOptions.d.ts +6 -0
  121. package/lib/typescript/src/types/MetricEvent.d.ts +8 -0
  122. package/lib/typescript/src/types/MetricOptions.d.ts +10 -0
  123. package/lib/typescript/src/types/NativeSamplingOptions.d.ts +7 -0
  124. package/lib/typescript/src/types/ReadOptions.d.ts +7 -0
  125. package/lib/typescript/src/types/RecordingOptions.d.ts +11 -0
  126. package/lib/typescript/src/types/RecordingStats.d.ts +25 -0
  127. package/lib/typescript/src/types/SpanEvent.d.ts +13 -0
  128. package/lib/typescript/src/types/SpanOptions.d.ts +6 -0
  129. package/lib/typescript/src/types/SpanOutcome.d.ts +2 -0
  130. package/lib/typescript/src/types/TraceAttribute.d.ts +7 -0
  131. package/lib/typescript/src/types/TraceAttributes.d.ts +3 -0
  132. package/lib/typescript/src/types/TraceContext.d.ts +10 -0
  133. package/lib/typescript/src/types/TracePage.d.ts +18 -0
  134. package/lib/typescript/src/types.d.ts +17 -0
  135. package/nitro.json +20 -0
  136. package/nitrogen/generated/.gitattributes +1 -0
  137. package/nitrogen/generated/android/NitroTracing+autolinking.cmake +83 -0
  138. package/nitrogen/generated/android/NitroTracing+autolinking.gradle +27 -0
  139. package/nitrogen/generated/android/NitroTracingOnLoad.cpp +49 -0
  140. package/nitrogen/generated/android/NitroTracingOnLoad.hpp +34 -0
  141. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotracing/NitroTracingOnLoad.kt +35 -0
  142. package/nitrogen/generated/ios/NitroTracing+autolinking.rb +62 -0
  143. package/nitrogen/generated/ios/NitroTracing-Swift-Cxx-Bridge.cpp +17 -0
  144. package/nitrogen/generated/ios/NitroTracing-Swift-Cxx-Bridge.hpp +27 -0
  145. package/nitrogen/generated/ios/NitroTracing-Swift-Cxx-Umbrella.hpp +38 -0
  146. package/nitrogen/generated/ios/NitroTracingAutolinking.mm +35 -0
  147. package/nitrogen/generated/ios/NitroTracingAutolinking.swift +16 -0
  148. package/nitrogen/generated/shared/c++/CompletedSpanOptions.hpp +122 -0
  149. package/nitrogen/generated/shared/c++/HybridRecordingSpec.cpp +31 -0
  150. package/nitrogen/generated/shared/c++/HybridRecordingSpec.hpp +100 -0
  151. package/nitrogen/generated/shared/c++/HybridTraceSpanSpec.cpp +23 -0
  152. package/nitrogen/generated/shared/c++/HybridTraceSpanSpec.hpp +65 -0
  153. package/nitrogen/generated/shared/c++/HybridTracingSpec.cpp +22 -0
  154. package/nitrogen/generated/shared/c++/HybridTracingSpec.hpp +68 -0
  155. package/nitrogen/generated/shared/c++/MarkEvent.hpp +102 -0
  156. package/nitrogen/generated/shared/c++/MarkOptions.hpp +99 -0
  157. package/nitrogen/generated/shared/c++/MetricEvent.hpp +110 -0
  158. package/nitrogen/generated/shared/c++/MetricOptions.hpp +107 -0
  159. package/nitrogen/generated/shared/c++/NativeSamplingOptions.hpp +87 -0
  160. package/nitrogen/generated/shared/c++/ReadOptions.hpp +87 -0
  161. package/nitrogen/generated/shared/c++/RecordingOptions.hpp +95 -0
  162. package/nitrogen/generated/shared/c++/RecordingStats.hpp +123 -0
  163. package/nitrogen/generated/shared/c++/SpanEvent.hpp +121 -0
  164. package/nitrogen/generated/shared/c++/SpanOptions.hpp +99 -0
  165. package/nitrogen/generated/shared/c++/SpanOutcome.hpp +84 -0
  166. package/nitrogen/generated/shared/c++/TraceAttribute.hpp +87 -0
  167. package/nitrogen/generated/shared/c++/TracePage.hpp +111 -0
  168. package/package.json +236 -0
  169. package/performance.ts +2 -0
  170. package/plugins.ts +24 -0
  171. package/react-native.config.js +16 -0
  172. package/react.ts +10 -0
  173. package/release-profiler.ts +6 -0
  174. package/sentry.ts +2 -0
  175. package/src/client/TracingSink.ts +25 -0
  176. package/src/client/createTraceClient.ts +439 -0
  177. package/src/client/playground.ts +143 -0
  178. package/src/expo/ExpoAdapter.ts +71 -0
  179. package/src/expo/index.ts +2 -0
  180. package/src/index.ts +8 -0
  181. package/src/plugins/TracePlugin.ts +22 -0
  182. package/src/plugins/errors.ts +69 -0
  183. package/src/plugins/nativeMetrics.ts +26 -0
  184. package/src/plugins/navigation.ts +111 -0
  185. package/src/plugins/network.ts +178 -0
  186. package/src/plugins/orderSpans.ts +22 -0
  187. package/src/plugins/performance.ts +120 -0
  188. package/src/plugins/releaseProfiler.ts +123 -0
  189. package/src/plugins/runtimeMetrics.ts +131 -0
  190. package/src/plugins/sentry.ts +176 -0
  191. package/src/plugins/startPlugins.ts +58 -0
  192. package/src/react/DetailView.tsx +514 -0
  193. package/src/react/ExplorerFilters.tsx +151 -0
  194. package/src/react/ExplorerView.tsx +243 -0
  195. package/src/react/InspectorControls.tsx +651 -0
  196. package/src/react/InspectorHeader.tsx +171 -0
  197. package/src/react/InspectorSheet.tsx +89 -0
  198. package/src/react/MetricsView.tsx +182 -0
  199. package/src/react/SummaryView.tsx +121 -0
  200. package/src/react/TimelineView.tsx +87 -0
  201. package/src/react/TraceInspector.tsx +196 -0
  202. package/src/react/TraceOverlay.tsx +585 -0
  203. package/src/react/explorerModel.ts +285 -0
  204. package/src/react/labels.ts +234 -0
  205. package/src/react/native.tsx +159 -0
  206. package/src/react/readSnapshot.ts +58 -0
  207. package/src/react/store.ts +31 -0
  208. package/src/react/topics.ts +427 -0
  209. package/src/react/useAppReadyMetric.ts +10 -0
  210. package/src/react/useLiveRecording.ts +43 -0
  211. package/src/react/useTraceViewer.ts +333 -0
  212. package/src/react/viewerModel.ts +172 -0
  213. package/src/specs/Recording.nitro.ts +40 -0
  214. package/src/specs/TraceSpan.nitro.ts +14 -0
  215. package/src/specs/Tracing.nitro.ts +14 -0
  216. package/src/types/CompletedSpanOptions.ts +15 -0
  217. package/src/types/EventContext.ts +8 -0
  218. package/src/types/MarkEvent.ts +3 -0
  219. package/src/types/MarkOptions.ts +6 -0
  220. package/src/types/MetricEvent.ts +8 -0
  221. package/src/types/MetricOptions.ts +10 -0
  222. package/src/types/NativeSamplingOptions.ts +7 -0
  223. package/src/types/ReadOptions.ts +7 -0
  224. package/src/types/RecordingOptions.ts +11 -0
  225. package/src/types/RecordingStats.ts +25 -0
  226. package/src/types/SpanEvent.ts +13 -0
  227. package/src/types/SpanOptions.ts +6 -0
  228. package/src/types/SpanOutcome.ts +2 -0
  229. package/src/types/TraceAttribute.ts +7 -0
  230. package/src/types/TraceAttributes.ts +4 -0
  231. package/src/types/TraceContext.ts +10 -0
  232. package/src/types/TracePage.ts +18 -0
  233. package/src/types.ts +17 -0
@@ -0,0 +1,243 @@
1
+ import { LegendList } from '@legendapp/list/react-native'
2
+ import React, { useCallback, useState } from 'react'
3
+ import { Pressable, StyleSheet, Text, View } from 'react-native'
4
+ import { ExplorerFilters } from './ExplorerFilters'
5
+ import { useMenu } from './native'
6
+ import type { InspectorTranslator } from './labels'
7
+ import type { Viewer } from './useTraceViewer'
8
+ import { sourceOf, type Event, type Sort, type Trace } from './explorerModel'
9
+ import {
10
+ apple,
11
+ palette,
12
+ Row,
13
+ SearchField,
14
+ Segmented,
15
+ } from './InspectorControls'
16
+
17
+ const toggle = (values: string[], value: string) =>
18
+ values.includes(value)
19
+ ? values.filter((item) => item !== value)
20
+ : [...values, value]
21
+ const isTrace = (item: Trace | Event): item is Trace => 'spans' in item
22
+ const keyOf = (item: Trace | Event) =>
23
+ isTrace(item)
24
+ ? item.id
25
+ : // Sequence is unique per event; span ids can repeat if a collector reuses one.
26
+ `${'spanId' in item ? 'span' : 'mark'}:${item.sequence}`
27
+ export const sortsFor = (mode: Viewer['mode']): Sort[] =>
28
+ mode === 'traces'
29
+ ? ['newest', 'oldest', 'longest', 'errors', 'count', 'name']
30
+ : mode === 'spans'
31
+ ? ['newest', 'oldest', 'longest', 'shortest', 'name']
32
+ : ['newest', 'oldest', 'name']
33
+
34
+ export function ExplorerView({ v, t }: { v: Viewer; t: InspectorTranslator }) {
35
+ const [filters, setFilters] = useState(false)
36
+ const menu = useMenu()
37
+ const chips = [
38
+ ...v.query.outcomes.map((value) => ({
39
+ key: `outcome:${value}`,
40
+ label: t(
41
+ value === 'success' && v.mode === 'traces'
42
+ ? 'noErrors'
43
+ : (value as 'success')
44
+ ),
45
+ clear: () => v.updateQuery({ outcomes: toggle(v.query.outcomes, value) }),
46
+ })),
47
+ ...v.query.sources.map((value) => ({
48
+ key: `source:${value}`,
49
+ label: value,
50
+ clear: () => v.updateQuery({ sources: toggle(v.query.sources, value) }),
51
+ })),
52
+ ...(['minDuration', 'from', 'to', 'correlation'] as const)
53
+ .filter((key) => v.query[key])
54
+ .map((key) => ({
55
+ key,
56
+ label: `${t(key)}: ${v.query[key]}`,
57
+ clear: () => v.updateQuery({ [key]: '' }),
58
+ })),
59
+ ]
60
+ const { open } = v
61
+ const renderItem = useCallback(
62
+ ({ item }: { item: Trace | Event }) => {
63
+ if (isTrace(item))
64
+ return (
65
+ <Row
66
+ title={item.name}
67
+ subtitle={t('traceRow', {
68
+ spans: item.spans.length,
69
+ errors: item.errors,
70
+ source: item.sources.join(', '),
71
+ })}
72
+ value={`${item.duration.toFixed(0)} ms`}
73
+ tint={item.errors ? palette.error : undefined}
74
+ onPress={() => open({ kind: 'trace', value: item })}
75
+ />
76
+ )
77
+ const span = 'spanId' in item ? item : undefined
78
+ return (
79
+ <Row
80
+ title={item.name}
81
+ subtitle={`${span ? t(span.outcome) : t('marks')} · ${sourceOf(item)} · ${(item.timestampMs / 1000).toFixed(2)} s`}
82
+ value={span ? `${span.durationMs.toFixed(0)} ms` : undefined}
83
+ tint={span?.outcome === 'error' ? palette.error : undefined}
84
+ onPress={() =>
85
+ open(
86
+ span
87
+ ? { kind: 'span', value: span }
88
+ : { kind: 'mark', value: item as never }
89
+ )
90
+ }
91
+ />
92
+ )
93
+ },
94
+ [open, t]
95
+ )
96
+ return (
97
+ <View style={styles.root}>
98
+ <SearchField
99
+ value={v.query.search}
100
+ onChange={(search) => v.updateQuery({ search })}
101
+ placeholder={t('exploreSearch')}
102
+ />
103
+ <Segmented
104
+ options={(['traces', 'spans', 'marks'] as const).map((mode) => ({
105
+ value: mode,
106
+ label: t(mode),
107
+ }))}
108
+ value={v.mode}
109
+ onChange={v.setMode}
110
+ />
111
+ <View style={styles.toolbar}>
112
+ <Pressable
113
+ accessibilityRole="button"
114
+ onPress={() =>
115
+ menu.open({
116
+ title: t('sort'),
117
+ cancelLabel: t('cancel'),
118
+ options: sortsFor(v.mode).map((sort) => ({
119
+ label: t(sort),
120
+ selected: sort === v.query.sort,
121
+ onPress: () => v.updateQuery({ sort }),
122
+ })),
123
+ })
124
+ }
125
+ hitSlop={8}
126
+ >
127
+ <Text style={styles.toolbarButton}>
128
+ {t('sortBy', { sort: t(v.query.sort) })} ⌄
129
+ </Text>
130
+ </Pressable>
131
+ <Text style={styles.count}>
132
+ {t('resultCount', { matching: v.results.length, loaded: v.loaded })}
133
+ </Text>
134
+ <Pressable
135
+ accessibilityRole="button"
136
+ onPress={() => {
137
+ v.setHolding(true)
138
+ setFilters(true)
139
+ }}
140
+ hitSlop={8}
141
+ >
142
+ <Text style={styles.toolbarButton}>
143
+ {t('filter')}
144
+ {chips.length ? ` (${chips.length})` : ''} ⌄
145
+ </Text>
146
+ </Pressable>
147
+ </View>
148
+ {chips.length > 0 && (
149
+ <View style={styles.chips}>
150
+ {chips.map((chip) => (
151
+ <Pressable
152
+ key={chip.key}
153
+ accessibilityRole="button"
154
+ accessibilityLabel={`${chip.label}, ${t('reset')}`}
155
+ onPress={chip.clear}
156
+ style={styles.chip}
157
+ >
158
+ <Text style={styles.chipText}>{chip.label} ✕</Text>
159
+ </Pressable>
160
+ ))}
161
+ <Pressable
162
+ accessibilityRole="button"
163
+ onPress={v.resetQuery}
164
+ style={styles.chipClear}
165
+ >
166
+ <Text style={styles.toolbarButton}>{t('reset')}</Text>
167
+ </Pressable>
168
+ </View>
169
+ )}
170
+ {v.mode === 'traces' && v.uncorrelated > 0 && !chips.length && (
171
+ <Text style={styles.hint}>
172
+ {t('uncorrelatedHint', { count: v.uncorrelated })}
173
+ </Text>
174
+ )}
175
+ <LegendList
176
+ recycleItems
177
+ contentInsetAdjustmentBehavior="automatic"
178
+ key={v.mode}
179
+ style={styles.list}
180
+ data={v.results as (Trace | Event)[]}
181
+ keyExtractor={keyOf}
182
+ estimatedItemSize={64}
183
+ keyboardShouldPersistTaps="handled"
184
+ keyboardDismissMode="on-drag"
185
+ initialScrollOffset={v.offsets.current[v.mode] ?? 0}
186
+ onScroll={(event) => {
187
+ v.offsets.current[v.mode] = event.nativeEvent.contentOffset.y
188
+ }}
189
+ scrollEventThrottle={100}
190
+ onScrollBeginDrag={() => v.setHolding(true)}
191
+ ListEmptyComponent={<Text style={styles.empty}>{t('noResults')}</Text>}
192
+ renderItem={renderItem}
193
+ />
194
+ {menu.element}
195
+ {filters && (
196
+ <ExplorerFilters v={v} t={t} close={() => setFilters(false)} />
197
+ )}
198
+ </View>
199
+ )
200
+ }
201
+
202
+ const styles = StyleSheet.create({
203
+ root: { flex: 1 },
204
+ toolbar: {
205
+ flexDirection: 'row',
206
+ alignItems: 'center',
207
+ justifyContent: 'space-between',
208
+ paddingHorizontal: 16,
209
+ paddingTop: 12,
210
+ paddingBottom: 8,
211
+ gap: 8,
212
+ },
213
+ toolbarButton: { fontSize: 15, color: palette.accent, fontWeight: '500' },
214
+ count: { flex: 1, textAlign: 'center', fontSize: 13, color: apple.secondary },
215
+ chips: {
216
+ flexDirection: 'row',
217
+ flexWrap: 'wrap',
218
+ gap: 6,
219
+ paddingHorizontal: 16,
220
+ paddingBottom: 8,
221
+ },
222
+ chip: {
223
+ paddingHorizontal: 10,
224
+ paddingVertical: 4,
225
+ borderRadius: 14,
226
+ backgroundColor: palette.selected,
227
+ },
228
+ chipText: { fontSize: 13, color: palette.accent },
229
+ chipClear: { paddingHorizontal: 4, paddingVertical: 4 },
230
+ hint: {
231
+ fontSize: 13,
232
+ color: apple.secondary,
233
+ paddingHorizontal: 16,
234
+ paddingBottom: 8,
235
+ },
236
+ list: { flex: 1, backgroundColor: apple.card },
237
+ empty: {
238
+ fontSize: 15,
239
+ color: apple.secondary,
240
+ textAlign: 'center',
241
+ padding: 32,
242
+ },
243
+ })