@expokit/cli 0.2.0 → 0.4.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 (232) hide show
  1. package/README.md +20 -2
  2. package/bin/expo-kit.js +219 -44
  3. package/bin/lib.mjs +110 -1
  4. package/bin/mcp.mjs +48 -19
  5. package/package.json +9 -4
  6. package/registry/_docs/accordion.json +16 -0
  7. package/registry/_docs/action-sheet.json +28 -0
  8. package/registry/_docs/alert.json +28 -0
  9. package/registry/_docs/assistant-app.json +47 -0
  10. package/registry/_docs/avatar.json +25 -1
  11. package/registry/_docs/badge.json +36 -5
  12. package/registry/_docs/bottom-nav.json +30 -6
  13. package/registry/_docs/bottom-sheet.json +28 -0
  14. package/registry/_docs/bullet.json +3 -0
  15. package/registry/_docs/button-3d.json +18 -2
  16. package/registry/_docs/button.json +16 -0
  17. package/registry/_docs/calendar.json +21 -5
  18. package/registry/_docs/card-stack.json +16 -0
  19. package/registry/_docs/card.json +20 -0
  20. package/registry/_docs/carousel.json +20 -0
  21. package/registry/_docs/chat.json +184 -0
  22. package/registry/_docs/checkbox.json +16 -0
  23. package/registry/_docs/checkout.json +10 -1
  24. package/registry/_docs/chip-group.json +16 -0
  25. package/registry/_docs/confetti.json +23 -3
  26. package/registry/_docs/coupon.json +16 -0
  27. package/registry/_docs/cube-pager.json +16 -0
  28. package/registry/_docs/dashboard.json +3 -0
  29. package/registry/_docs/date-picker.json +24 -4
  30. package/registry/_docs/dialog.json +24 -0
  31. package/registry/_docs/donut-shop.json +88 -0
  32. package/registry/_docs/drawer.json +32 -4
  33. package/registry/_docs/fab.json +24 -0
  34. package/registry/_docs/fintech-app.json +49 -0
  35. package/registry/_docs/flip-card.json +16 -0
  36. package/registry/_docs/floating-input.json +25 -5
  37. package/registry/_docs/food-app.json +47 -0
  38. package/registry/_docs/gift-card.json +16 -0
  39. package/registry/_docs/glass-button.json +24 -0
  40. package/registry/_docs/glass-nav-bar.json +24 -0
  41. package/registry/_docs/glass-search-bar.json +24 -0
  42. package/registry/_docs/glass-segmented.json +28 -0
  43. package/registry/_docs/glass-sheet.json +28 -0
  44. package/registry/_docs/glass-tab-bar.json +24 -0
  45. package/registry/_docs/glass.json +17 -1
  46. package/registry/_docs/grain.json +80 -0
  47. package/registry/_docs/inbox.json +3 -0
  48. package/registry/_docs/isometric-bar.json +3 -0
  49. package/registry/_docs/kit.json +3 -0
  50. package/registry/_docs/liquidglass-tab.json +3 -0
  51. package/registry/_docs/list-item.json +16 -0
  52. package/registry/_docs/loyalty-card.json +16 -0
  53. package/registry/_docs/morphing-button-group.json +96 -0
  54. package/registry/_docs/number-ticker.json +16 -0
  55. package/registry/_docs/offers.json +3 -0
  56. package/registry/_docs/onboarding.json +3 -0
  57. package/registry/_docs/orb-reveal.json +132 -0
  58. package/registry/_docs/otp-input.json +28 -4
  59. package/registry/_docs/parallax-layers.json +20 -0
  60. package/registry/_docs/parallax-scroll.json +24 -0
  61. package/registry/_docs/password-input.json +23 -3
  62. package/registry/_docs/payment-card.json +16 -0
  63. package/registry/_docs/pixel-bar.json +3 -0
  64. package/registry/_docs/polar-area.json +3 -0
  65. package/registry/_docs/profile.json +3 -0
  66. package/registry/_docs/progress-steps.json +24 -0
  67. package/registry/_docs/progress.json +20 -0
  68. package/registry/_docs/qr-code-button.json +122 -0
  69. package/registry/_docs/radio-group.json +20 -0
  70. package/registry/_docs/rating.json +24 -0
  71. package/registry/_docs/ring-carousel.json +20 -0
  72. package/registry/_docs/scratch-card.json +20 -0
  73. package/registry/_docs/search-bar.json +23 -3
  74. package/registry/_docs/search-results.json +3 -0
  75. package/registry/_docs/segmented-control.json +20 -0
  76. package/registry/_docs/select.json +33 -8
  77. package/registry/_docs/settings.json +9 -0
  78. package/registry/_docs/sign-in.json +3 -0
  79. package/registry/_docs/skeleton.json +22 -2
  80. package/registry/_docs/slide-to-confirm.json +24 -0
  81. package/registry/_docs/slider.json +27 -7
  82. package/registry/_docs/spin-wheel.json +24 -0
  83. package/registry/_docs/spinner.json +41 -11
  84. package/registry/_docs/stateful-button.json +184 -0
  85. package/registry/_docs/stepper.json +23 -7
  86. package/registry/_docs/swipe-deck.json +16 -0
  87. package/registry/_docs/swipeable-row.json +24 -1
  88. package/registry/_docs/switch.json +20 -0
  89. package/registry/_docs/tabs.json +24 -0
  90. package/registry/_docs/text-animation.json +141 -0
  91. package/registry/_docs/textarea.json +18 -2
  92. package/registry/_docs/ticket-card.json +20 -0
  93. package/registry/_docs/ticket-shape.json +4 -1
  94. package/registry/_docs/tilt-card.json +20 -4
  95. package/registry/_docs/time-picker.json +22 -6
  96. package/registry/_docs/timeline-chart.json +3 -0
  97. package/registry/_docs/timeline.json +18 -2
  98. package/registry/_docs/toast.json +32 -8
  99. package/registry/_docs/treemap.json +3 -0
  100. package/registry/_docs/waterfall.json +3 -0
  101. package/registry/_docs/wheel-list.json +20 -0
  102. package/registry/accordion/files/src/components/ui/accordion.tsx +5 -4
  103. package/registry/accordion/files/src/lib/kit.tsx +8 -5
  104. package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +9 -8
  105. package/registry/action-sheet/files/src/lib/kit.tsx +8 -5
  106. package/registry/alert/files/src/components/ui/alert.tsx +7 -4
  107. package/registry/alert/files/src/components/ui/button.tsx +4 -3
  108. package/registry/alert/files/src/lib/kit.tsx +8 -5
  109. package/registry/assistant-app.json +49 -0
  110. package/registry/avatar/files/src/components/ui/avatar.tsx +7 -4
  111. package/registry/avatar/files/src/lib/kit.tsx +8 -5
  112. package/registry/badge/files/src/components/ui/badge.tsx +345 -21
  113. package/registry/badge/files/src/lib/kit.tsx +8 -5
  114. package/registry/badge.json +1 -1
  115. package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +14 -7
  116. package/registry/bottom-nav/files/src/lib/kit.tsx +8 -5
  117. package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +7 -6
  118. package/registry/bottom-sheet/files/src/lib/kit.tsx +8 -5
  119. package/registry/button/files/src/components/ui/button.tsx +4 -3
  120. package/registry/button/files/src/lib/kit.tsx +8 -5
  121. package/registry/button-3d/files/src/components/ui/button-3d.tsx +3 -2
  122. package/registry/button-3d/files/src/lib/kit.tsx +8 -5
  123. package/registry/calendar/files/src/components/ui/calendar.tsx +9 -5
  124. package/registry/calendar/files/src/lib/kit.tsx +8 -5
  125. package/registry/card/files/src/components/ui/card.tsx +8 -6
  126. package/registry/card/files/src/lib/kit.tsx +8 -5
  127. package/registry/chat.json +18 -0
  128. package/registry/checkbox/files/src/components/ui/checkbox.tsx +6 -5
  129. package/registry/checkbox/files/src/lib/kit.tsx +8 -5
  130. package/registry/chip-group/files/src/components/ui/chip-group.tsx +4 -2
  131. package/registry/chip-group/files/src/lib/kit.tsx +8 -5
  132. package/registry/confetti/files/src/lib/kit.tsx +8 -5
  133. package/registry/coupon/files/src/components/ui/coupon.tsx +23 -20
  134. package/registry/coupon/files/src/components/ui/flip-card.tsx +1 -1
  135. package/registry/coupon/files/src/components/ui/tilt-card.tsx +2 -2
  136. package/registry/coupon/files/src/lib/kit.tsx +8 -5
  137. package/registry/date-picker/files/src/components/ui/calendar.tsx +9 -5
  138. package/registry/date-picker/files/src/components/ui/date-picker.tsx +2 -2
  139. package/registry/date-picker/files/src/lib/kit.tsx +8 -5
  140. package/registry/dialog/files/src/components/ui/dialog.tsx +12 -6
  141. package/registry/dialog/files/src/lib/kit.tsx +8 -5
  142. package/registry/donut-shop.json +18 -0
  143. package/registry/drawer/files/src/components/ui/drawer.tsx +25 -19
  144. package/registry/drawer/files/src/lib/kit.tsx +8 -5
  145. package/registry/fintech-app.json +57 -0
  146. package/registry/flip-card/files/src/components/ui/flip-card.tsx +1 -1
  147. package/registry/flip-card/files/src/lib/kit.tsx +8 -5
  148. package/registry/floating-input/files/src/components/ui/floating-input.tsx +16 -6
  149. package/registry/floating-input/files/src/lib/kit.tsx +8 -5
  150. package/registry/food-app.json +55 -0
  151. package/registry/grain/files/src/components/ui/grain.tsx +113 -0
  152. package/registry/{tag-input → grain}/files/src/lib/kit.tsx +8 -5
  153. package/registry/grain.json +17 -0
  154. package/registry/kit/files/src/lib/kit.tsx +8 -5
  155. package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +1 -1
  156. package/registry/liquidglass-tab/files/src/lib/tab-bar.ts +1 -1
  157. package/registry/list-item/files/src/components/ui/list-item.tsx +20 -16
  158. package/registry/list-item/files/src/lib/kit.tsx +8 -5
  159. package/registry/morphing-button-group.json +18 -0
  160. package/registry/orb-reveal.json +20 -0
  161. package/registry/otp-input/files/src/components/ui/otp-input.tsx +8 -6
  162. package/registry/otp-input/files/src/lib/kit.tsx +8 -5
  163. package/registry/password-input/files/src/components/ui/floating-input.tsx +16 -6
  164. package/registry/password-input/files/src/components/ui/password-input.tsx +2 -2
  165. package/registry/password-input/files/src/lib/kit.tsx +8 -5
  166. package/registry/payment-card/files/src/components/ui/flip-card.tsx +1 -1
  167. package/registry/payment-card/files/src/components/ui/payment-card.tsx +27 -14
  168. package/registry/payment-card/files/src/components/ui/tilt-card.tsx +2 -2
  169. package/registry/payment-card/files/src/lib/kit.tsx +8 -5
  170. package/registry/progress/files/src/components/ui/progress.tsx +8 -5
  171. package/registry/progress/files/src/lib/kit.tsx +8 -5
  172. package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +15 -11
  173. package/registry/progress-steps/files/src/lib/kit.tsx +8 -5
  174. package/registry/qr-code-button.json +18 -0
  175. package/registry/radio-group/files/src/components/ui/radio-group.tsx +10 -9
  176. package/registry/radio-group/files/src/lib/kit.tsx +8 -5
  177. package/registry/search-bar/files/src/components/ui/search-bar.tsx +13 -11
  178. package/registry/search-bar/files/src/lib/kit.tsx +8 -5
  179. package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +4 -2
  180. package/registry/segmented-control/files/src/lib/kit.tsx +8 -5
  181. package/registry/select/files/src/components/ui/select.tsx +3 -3
  182. package/registry/select/files/src/lib/kit.tsx +8 -5
  183. package/registry/skeleton/files/src/components/ui/skeleton.tsx +4 -4
  184. package/registry/skeleton/files/src/lib/kit.tsx +8 -5
  185. package/registry/slider/files/src/components/ui/slider.tsx +5 -4
  186. package/registry/slider/files/src/lib/kit.tsx +8 -5
  187. package/registry/spinner/files/src/components/ui/spinner.tsx +452 -34
  188. package/registry/spinner/files/src/lib/kit.tsx +8 -5
  189. package/registry/spinner.json +1 -1
  190. package/registry/stateful-button/files/src/components/ui/stateful-button.tsx +532 -0
  191. package/registry/{pagination-dots → stateful-button}/files/src/lib/kit.tsx +8 -5
  192. package/registry/stateful-button.json +18 -0
  193. package/registry/stepper/files/src/components/ui/stepper.tsx +8 -4
  194. package/registry/stepper/files/src/lib/kit.tsx +8 -5
  195. package/registry/switch/files/src/components/ui/switch.tsx +4 -3
  196. package/registry/switch/files/src/lib/kit.tsx +8 -5
  197. package/registry/tabs/files/src/components/ui/tabs.tsx +10 -8
  198. package/registry/tabs/files/src/lib/kit.tsx +8 -5
  199. package/registry/text-animation/files/src/components/ui/text-animation.tsx +650 -0
  200. package/registry/text-animation/files/src/lib/kit.tsx +222 -0
  201. package/registry/text-animation.json +15 -0
  202. package/registry/textarea/files/src/components/ui/textarea.tsx +11 -9
  203. package/registry/textarea/files/src/lib/kit.tsx +8 -5
  204. package/registry/ticket-shape/files/src/lib/kit.tsx +8 -5
  205. package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +2 -2
  206. package/registry/tilt-card/files/src/lib/kit.tsx +8 -5
  207. package/registry/time-picker/files/src/components/ui/time-picker.tsx +2 -2
  208. package/registry/time-picker/files/src/lib/kit.tsx +8 -5
  209. package/registry/timeline/files/src/components/ui/avatar.tsx +7 -4
  210. package/registry/timeline/files/src/components/ui/timeline.tsx +12 -10
  211. package/registry/timeline/files/src/lib/kit.tsx +8 -5
  212. package/registry/toast/files/src/components/ui/button.tsx +4 -3
  213. package/registry/toast/files/src/components/ui/toast.tsx +13 -11
  214. package/registry/toast/files/src/lib/kit.tsx +8 -5
  215. package/schema/components.schema.json +1 -1
  216. package/registry/_docs/color-swatch.json +0 -66
  217. package/registry/_docs/kpi-card.json +0 -11
  218. package/registry/_docs/like-button.json +0 -72
  219. package/registry/_docs/morph-button.json +0 -108
  220. package/registry/_docs/pagination-dots.json +0 -88
  221. package/registry/_docs/shimmer-button.json +0 -78
  222. package/registry/_docs/tag-input.json +0 -128
  223. package/registry/color-swatch.json +0 -17
  224. package/registry/kpi-card/files/src/components/charts/kpi-card.tsx +0 -45
  225. package/registry/kpi-card.json +0 -14
  226. package/registry/like-button.json +0 -18
  227. package/registry/morph-button.json +0 -18
  228. package/registry/pagination-dots/files/src/components/ui/pagination-dots.tsx +0 -262
  229. package/registry/pagination-dots.json +0 -15
  230. package/registry/shimmer-button.json +0 -17
  231. package/registry/tag-input/files/src/components/ui/tag-input.tsx +0 -720
  232. package/registry/tag-input.json +0 -17
@@ -6,6 +6,7 @@
6
6
  "files": [
7
7
  "src/components/ui/toast.tsx"
8
8
  ],
9
+ "summary": "Sonner-style toasts whose tone icon draws itself while a hairline drains away.",
9
10
  "props": [
10
11
  [
11
12
  "toast(title, options?)",
@@ -116,46 +117,69 @@
116
117
  "Style for the <Toaster> host that fills the screen and positions the stack."
117
118
  ]
118
119
  ],
120
+ "behavior": [
121
+ [
122
+ "Touch",
123
+ "Tap the stack to fan it out, swipe a toast sideways or toward the edge to dismiss it; timers and the drain line pause while you touch."
124
+ ],
125
+ [
126
+ "Motion",
127
+ "Tone icons draw their outline inside a halo; toast.promise() closes the spinning arc into the success or error mark. variant=\"island\" morphs like a Dynamic Island."
128
+ ],
129
+ [
130
+ "Reduced motion",
131
+ "Tone icons appear fully drawn without the spin or pop, and the island skips its squash and staggered morph."
132
+ ],
133
+ [
134
+ "Haptics",
135
+ "A success, warning or error notification when a toast of that type appears, landing as the loading circle closes; turn it off with haptics={false}."
136
+ ],
137
+ [
138
+ "Screen readers",
139
+ "Each toast is an alert in a polite live region, labelled by its title and description, with action and Dismiss buttons."
140
+ ]
141
+ ],
142
+ "usage": "import { toast, Toaster } from '@/components/ui/toast';\n\n// once, at the root of your app\n<Toaster />\n\n// anywhere\ntoast.success('Invoice INV-0042 sent', { description: 'Maya will get it by email.' });",
119
143
  "examples": [
120
144
  {
121
145
  "name": "Default",
122
146
  "title": "Default",
123
- "code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Frame({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n height: 300,\n overflow: 'hidden',\n padding: 16,\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nexport default function Default() {\n const to = { toaster: 'demo-default' };\n return (\n <Frame>\n <View style={row}>\n <Button\n size=\"sm\"\n variant=\"accent\"\n onPress={() => toast('Event created', { ...to, description: 'Friday, June 14 at 9:00 AM' })}\n >\n Default\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onPress={() => toast.success('Changes saved', { ...to, description: 'Synced to all your devices.' })}\n >\n Success\n </Button>\n <Button size=\"sm\" variant=\"outline\" onPress={() => toast.warning('Storage almost full', { ...to, description: '92% of 15 GB used.' })}>\n Warning\n </Button>\n <Button size=\"sm\" variant=\"outline\" onPress={() => toast.error('Could not reach server', to)}>\n Error\n </Button>\n </View>\n <Toaster id=\"demo-default\" />\n </Frame>\n );\n}\n"
147
+ "code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. In screen mode it is the screen. */\nfunction Frame({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : {\n height: 300,\n overflow: 'hidden',\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nexport default function Default({ screen }: DemoProps) {\n const to = { toaster: 'demo-default' };\n return (\n <Frame screen={screen}>\n <View style={row}>\n <Button\n size=\"sm\"\n variant=\"accent\"\n onPress={() => toast('Event created', { ...to, description: 'Friday, June 14 at 9:00 AM' })}\n >\n Default\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onPress={() => toast.success('Changes saved', { ...to, description: 'Synced to all your devices.' })}\n >\n Success\n </Button>\n <Button size=\"sm\" variant=\"outline\" onPress={() => toast.warning('Storage almost full', { ...to, description: '92% of 15 GB used.' })}>\n Warning\n </Button>\n <Button size=\"sm\" variant=\"outline\" onPress={() => toast.error('Could not reach server', to)}>\n Error\n </Button>\n </View>\n <Toaster id=\"demo-default\" />\n </Frame>\n );\n}\n"
124
148
  },
125
149
  {
126
150
  "name": "RichColors",
127
151
  "title": "Rich Colors",
128
- "code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Frame({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n height: 300,\n overflow: 'hidden',\n padding: 16,\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nexport default function RichColors() {\n const { colors } = useKit();\n const to = { toaster: 'demo-rich' };\n return (\n <Frame>\n <View style={row}>\n <Button\n size=\"sm\"\n variant=\"soft\"\n color={colors.success}\n onPress={() => toast.success('Payment received', { ...to, description: '₹18,500 from Acme Inc.' })}\n >\n Success\n </Button>\n <Button\n size=\"sm\"\n variant=\"soft\"\n color={colors.danger}\n onPress={() => toast.error('Card declined', { ...to, description: 'Try another payment method.' })}\n >\n Error\n </Button>\n <Button size=\"sm\" variant=\"soft\" color={colors.warning} onPress={() => toast.warning('You are offline', { ...to, description: 'Changes will sync when you reconnect.' })}>\n Warning\n </Button>\n <Button size=\"sm\" variant=\"soft\" onPress={() => toast.info('New version available', { ...to, description: 'Restart to update to 4.2.' })}>\n Info\n </Button>\n </View>\n <Toaster id=\"demo-rich\" variant=\"rich\" />\n </Frame>\n );\n}\n"
152
+ "code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. In screen mode it is the screen. */\nfunction Frame({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : {\n height: 300,\n overflow: 'hidden',\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nexport default function RichColors({ screen }: DemoProps) {\n const { colors } = useKit();\n const to = { toaster: 'demo-rich' };\n return (\n <Frame screen={screen}>\n <View style={row}>\n <Button\n size=\"sm\"\n variant=\"soft\"\n color={colors.success}\n onPress={() => toast.success('Payment received', { ...to, description: '₹18,500 from Acme Inc.' })}\n >\n Success\n </Button>\n <Button\n size=\"sm\"\n variant=\"soft\"\n color={colors.danger}\n onPress={() => toast.error('Card declined', { ...to, description: 'Try another payment method.' })}\n >\n Error\n </Button>\n <Button size=\"sm\" variant=\"soft\" color={colors.warning} onPress={() => toast.warning('You are offline', { ...to, description: 'Changes will sync when you reconnect.' })}>\n Warning\n </Button>\n <Button size=\"sm\" variant=\"soft\" onPress={() => toast.info('New version available', { ...to, description: 'Restart to update to 4.2.' })}>\n Info\n </Button>\n </View>\n <Toaster id=\"demo-rich\" variant=\"rich\" />\n </Frame>\n );\n}\n"
129
153
  },
130
154
  {
131
155
  "name": "Island",
132
156
  "title": "Island",
133
- "code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Frame({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n height: 300,\n overflow: 'hidden',\n padding: 16,\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nconst wait = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\nexport default function Island() {\n const to = { toaster: 'demo-island' };\n return (\n <Frame style={{ justifyContent: 'flex-end' }}>\n <View style={row}>\n <Button\n size=\"sm\"\n variant=\"secondary\"\n onPress={() => toast.success('AirPods connected', { ...to, description: 'Battery 84% · Case 100%' })}\n >\n Success\n </Button>\n <Button\n size=\"sm\"\n variant=\"secondary\"\n onPress={() =>\n toast.error('Payment failed', {\n ...to,\n description: 'Your card ending 4242 was declined by the bank. Update your billing details to keep Pro.',\n action: { label: 'Update', onPress: () => toast.info('Opening billing…', to) },\n })\n }\n >\n Error\n </Button>\n <Button\n size=\"sm\"\n variant=\"secondary\"\n onPress={() =>\n toast.promise(wait(2200), { loading: 'Sending to Maya…', success: 'Photo sent', error: 'Could not send' }, to)\n }\n >\n Promise\n </Button>\n <Button\n size=\"sm\"\n variant=\"secondary\"\n onPress={() => toast('Maya Chen', { ...to, description: 'Running 5 min late, grab us a table?' })}\n >\n Message\n </Button>\n </View>\n <Toaster id=\"demo-island\" variant=\"island\" />\n </Frame>\n );\n}\n"
157
+ "code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. In screen mode it is the screen. */\nfunction Frame({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : {\n height: 300,\n overflow: 'hidden',\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nconst wait = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\nexport default function Island({ screen }: DemoProps) {\n const to = { toaster: 'demo-island' };\n return (\n <Frame screen={screen} style={{ justifyContent: 'flex-end' }}>\n <View style={row}>\n <Button\n size=\"sm\"\n variant=\"secondary\"\n onPress={() => toast.success('AirPods connected', { ...to, description: 'Battery 84% · Case 100%' })}\n >\n Success\n </Button>\n <Button\n size=\"sm\"\n variant=\"secondary\"\n onPress={() =>\n toast.error('Payment failed', {\n ...to,\n description: 'Your card ending 4242 was declined by the bank. Update your billing details to keep Pro.',\n action: { label: 'Update', onPress: () => toast.info('Opening billing…', to) },\n })\n }\n >\n Error\n </Button>\n <Button\n size=\"sm\"\n variant=\"secondary\"\n onPress={() =>\n toast.promise(wait(2200), { loading: 'Sending to Maya…', success: 'Photo sent', error: 'Could not send' }, to)\n }\n >\n Promise\n </Button>\n <Button\n size=\"sm\"\n variant=\"secondary\"\n onPress={() => toast('Maya Chen', { ...to, description: 'Running 5 min late, grab us a table?' })}\n >\n Message\n </Button>\n </View>\n <Toaster id=\"demo-island\" variant=\"island\" />\n </Frame>\n );\n}\n"
134
158
  },
135
159
  {
136
160
  "name": "PromiseMorph",
137
161
  "title": "Promise Morph",
138
- "code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Frame({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n height: 300,\n overflow: 'hidden',\n padding: 16,\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nconst wait = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\nconst fail = (ms: number) => new Promise<void>((_, reject) => setTimeout(() => reject(new Error('timeout')), ms));\n\nexport default function PromiseMorph() {\n const to = { toaster: 'demo-promise' };\n return (\n <Frame>\n <View style={row}>\n <Button\n size=\"sm\"\n variant=\"accent\"\n onPress={() =>\n toast.promise(\n wait(1800),\n { loading: 'Uploading report.pdf…', success: 'Report uploaded', error: 'Upload failed' },\n { ...to, description: 'Q3 board pack · 4.2 MB' },\n )\n }\n >\n Upload (succeeds)\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onPress={() =>\n toast\n .promise(\n fail(1800),\n { loading: 'Publishing release…', success: 'Release published', error: 'Publish failed: build timed out' },\n to,\n )\n .catch(() => {})\n }\n >\n Publish (fails)\n </Button>\n </View>\n <Toaster id=\"demo-promise\" />\n </Frame>\n );\n}\n"
162
+ "code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. In screen mode it is the screen. */\nfunction Frame({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : {\n height: 300,\n overflow: 'hidden',\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nconst wait = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\nconst fail = (ms: number) => new Promise<void>((_, reject) => setTimeout(() => reject(new Error('timeout')), ms));\n\nexport default function PromiseMorph({ screen }: DemoProps) {\n const to = { toaster: 'demo-promise' };\n return (\n <Frame screen={screen}>\n <View style={row}>\n <Button\n size=\"sm\"\n variant=\"accent\"\n onPress={() =>\n toast.promise(\n wait(1800),\n { loading: 'Uploading report.pdf…', success: 'Report uploaded', error: 'Upload failed' },\n { ...to, description: 'Q3 board pack · 4.2 MB' },\n )\n }\n >\n Upload (succeeds)\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onPress={() =>\n toast\n .promise(\n fail(1800),\n { loading: 'Publishing release…', success: 'Release published', error: 'Publish failed: build timed out' },\n to,\n )\n .catch(() => {})\n }\n >\n Publish (fails)\n </Button>\n </View>\n <Toaster id=\"demo-promise\" />\n </Frame>\n );\n}\n"
139
163
  },
140
164
  {
141
165
  "name": "Minimal",
142
166
  "title": "Minimal",
143
- "code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Frame({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n height: 300,\n overflow: 'hidden',\n padding: 16,\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nexport default function Minimal() {\n const to = { toaster: 'demo-minimal' };\n return (\n <Frame style={{ justifyContent: 'flex-end' }}>\n <View style={row}>\n <Button size=\"sm\" variant=\"secondary\" onPress={() => toast('Copied to clipboard', to)}>\n Copy link\n </Button>\n <Button size=\"sm\" variant=\"secondary\" onPress={() => toast.success('Added to favourites', to)}>\n Favourite\n </Button>\n <Button size=\"sm\" variant=\"secondary\" onPress={() => toast.loading('Syncing…', { ...to, id: 'sync', duration: 1500 })}>\n Sync\n </Button>\n </View>\n <Toaster id=\"demo-minimal\" variant=\"minimal\" position=\"top\" />\n </Frame>\n );\n}\n"
167
+ "code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. In screen mode it is the screen. */\nfunction Frame({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : {\n height: 300,\n overflow: 'hidden',\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nexport default function Minimal({ screen }: DemoProps) {\n const to = { toaster: 'demo-minimal' };\n return (\n <Frame screen={screen} style={{ justifyContent: 'flex-end' }}>\n <View style={row}>\n <Button size=\"sm\" variant=\"secondary\" onPress={() => toast('Copied to clipboard', to)}>\n Copy link\n </Button>\n <Button size=\"sm\" variant=\"secondary\" onPress={() => toast.success('Added to favourites', to)}>\n Favourite\n </Button>\n <Button size=\"sm\" variant=\"secondary\" onPress={() => toast.loading('Syncing…', { ...to, id: 'sync', duration: 1500 })}>\n Sync\n </Button>\n </View>\n <Toaster id=\"demo-minimal\" variant=\"minimal\" position=\"top\" />\n </Frame>\n );\n}\n"
144
168
  },
145
169
  {
146
170
  "name": "WithAction",
147
171
  "title": "With Action",
148
- "code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Frame({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n height: 300,\n overflow: 'hidden',\n padding: 16,\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function WithAction() {\n const to = { toaster: 'demo-action' };\n return (\n <Frame>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n style={{ alignSelf: 'flex-start' }}\n onPress={() =>\n toast('Conversation archived', {\n ...to,\n description: 'Moved 12 messages to Archive.',\n action: { label: 'Undo', onPress: () => toast.success('Conversation restored', to) },\n })\n }\n >\n Archive chat\n </Button>\n <Toaster id=\"demo-action\" closeButton duration={6000} />\n </Frame>\n );\n}\n"
172
+ "code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. In screen mode it is the screen. */\nfunction Frame({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : {\n height: 300,\n overflow: 'hidden',\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function WithAction({ screen }: DemoProps) {\n const to = { toaster: 'demo-action' };\n return (\n <Frame screen={screen}>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n style={{ alignSelf: 'flex-start' }}\n onPress={() =>\n toast('Conversation archived', {\n ...to,\n description: 'Moved 12 messages to Archive.',\n action: { label: 'Undo', onPress: () => toast.success('Conversation restored', to) },\n })\n }\n >\n Archive chat\n </Button>\n <Toaster id=\"demo-action\" closeButton duration={6000} />\n </Frame>\n );\n}\n"
149
173
  },
150
174
  {
151
175
  "name": "Expanded",
152
176
  "title": "Expanded",
153
- "code": "import React, { useRef } from 'react';\nimport { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Frame({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n height: 300,\n overflow: 'hidden',\n padding: 16,\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Expanded() {\n const to = { toaster: 'demo-expanded' };\n const messages = [\n () => toast.info('Maya joined #design', to),\n () => toast.success('Build #1824 passed', { ...to, description: 'main · 3m 12s' }),\n () => toast('Invoice INV-0042 sent', to),\n () => toast.warning('Disk usage at 85%', to),\n ];\n const n = useRef(0);\n return (\n <Frame style={{ justifyContent: 'flex-end' }}>\n <Button size=\"sm\" style={{ alignSelf: 'flex-start' }} onPress={() => messages[n.current++ % messages.length]()}>\n Send notification\n </Button>\n <Toaster id=\"demo-expanded\" position=\"top\" expand visibleToasts={3} gap={8} />\n </Frame>\n );\n}\n"
177
+ "code": "import React, { useRef } from 'react';\nimport { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. In screen mode it is the screen. */\nfunction Frame({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : {\n height: 300,\n overflow: 'hidden',\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Expanded({ screen }: DemoProps) {\n const to = { toaster: 'demo-expanded' };\n const messages = [\n () => toast.info('Maya joined #design', to),\n () => toast.success('Build #1824 passed', { ...to, description: 'main · 3m 12s' }),\n () => toast('Invoice INV-0042 sent', to),\n () => toast.warning('Disk usage at 85%', to),\n ];\n const n = useRef(0);\n return (\n <Frame screen={screen} style={{ justifyContent: 'flex-end' }}>\n <Button size=\"sm\" style={{ alignSelf: 'flex-start' }} onPress={() => messages[n.current++ % messages.length]()}>\n Send notification\n </Button>\n <Toaster id=\"demo-expanded\" position=\"top\" expand visibleToasts={3} gap={8} />\n </Frame>\n );\n}\n"
154
178
  },
155
179
  {
156
180
  "name": "WithProvider",
157
181
  "title": "With Provider",
158
- "code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Frame({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n height: 300,\n overflow: 'hidden',\n padding: 16,\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nfunction ProviderDemo() {\n const toast = useToast();\n return (\n <Frame>\n <View style={row}>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onPress={() => toast.success('Draft saved', { description: 'Only this Toaster sees it.' })}\n >\n Save draft\n </Button>\n <Button size=\"sm\" variant=\"outline\" onPress={() => toast('Reminder set for 6:00 PM')}>\n Remind me\n </Button>\n </View>\n <Toaster />\n </Frame>\n );\n}\n\nexport default function WithProvider() {\n return (\n <ToastProvider>\n <ProviderDemo />\n </ToastProvider>\n );\n}\n"
182
+ "code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. In screen mode it is the screen. */\nfunction Frame({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : {\n height: 300,\n overflow: 'hidden',\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nfunction ProviderDemo({ screen }: { screen?: boolean }) {\n const toast = useToast();\n return (\n <Frame screen={screen}>\n <View style={row}>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onPress={() => toast.success('Draft saved', { description: 'Only this Toaster sees it.' })}\n >\n Save draft\n </Button>\n <Button size=\"sm\" variant=\"outline\" onPress={() => toast('Reminder set for 6:00 PM')}>\n Remind me\n </Button>\n </View>\n <Toaster />\n </Frame>\n );\n}\n\nexport default function WithProvider({ screen }: DemoProps) {\n return (\n <ToastProvider>\n <ProviderDemo screen={screen} />\n </ToastProvider>\n );\n}\n"
159
183
  }
160
184
  ]
161
185
  }
@@ -6,6 +6,7 @@
6
6
  "files": [
7
7
  "src/components/charts/treemap.tsx"
8
8
  ],
9
+ "summary": null,
9
10
  "props": [
10
11
  [
11
12
  "data",
@@ -26,5 +27,7 @@
26
27
  "Tile colours."
27
28
  ]
28
29
  ],
30
+ "behavior": [],
31
+ "usage": null,
29
32
  "examples": []
30
33
  }
@@ -6,6 +6,7 @@
6
6
  "files": [
7
7
  "src/components/charts/waterfall.tsx"
8
8
  ],
9
+ "summary": null,
9
10
  "props": [
10
11
  [
11
12
  "data",
@@ -32,5 +33,7 @@
32
33
  "Change labels."
33
34
  ]
34
35
  ],
36
+ "behavior": [],
37
+ "usage": null,
35
38
  "examples": []
36
39
  }
@@ -6,6 +6,7 @@
6
6
  "files": [
7
7
  "src/components/ui/wheel-list.tsx"
8
8
  ],
9
+ "summary": "An iOS alarm-style picker whose rows curve away on a 3D cylinder.",
9
10
  "props": [
10
11
  [
11
12
  "data",
@@ -98,5 +99,24 @@
98
99
  "Style for the root (width)."
99
100
  ]
100
101
  ],
102
+ "behavior": [
103
+ [
104
+ "Touch",
105
+ "Flick to spin and it snaps to a row; tap a row to scroll it into the band."
106
+ ],
107
+ [
108
+ "Motion",
109
+ "Each row rotates, scales and fades by its distance from the band, interpolated from the scroll position on the native driver."
110
+ ],
111
+ [
112
+ "Screen readers",
113
+ "One adjustable element with increment / decrement actions; its value is the selected label, or \"3 / 12\" for custom rows."
114
+ ],
115
+ [
116
+ "Web",
117
+ "Browsers lack snap momentum, so it settles on the nearest row shortly after scrolling stops."
118
+ ]
119
+ ],
120
+ "usage": null,
101
121
  "examples": []
102
122
  }
@@ -119,7 +119,8 @@ export function AccordionItem({ value, title, subtitle, icon, children, disabled
119
119
  if (!ctx) throw new Error('AccordionItem must be used inside <Accordion>');
120
120
  const { open: openValues, toggle, variant, tint } = ctx;
121
121
  const { last } = useContext(PositionContext);
122
- const { colors, radius, dark } = useKit();
122
+ const { colors, radius, dark, font } = useKit();
123
+ const ff = font.family ? { fontFamily: font.family } : null;
123
124
  const open = openValues.includes(value);
124
125
 
125
126
  const [height, setHeight] = useState(0);
@@ -168,11 +169,11 @@ export function AccordionItem({ value, title, subtitle, icon, children, disabled
168
169
  {icon ? <View style={styles.icon}>{icon}</View> : null}
169
170
  <View style={styles.titles}>
170
171
  {isTextNode(title) ? (
171
- <Text style={[styles.title, { color: open && variant === 'default' ? tint : colors.foreground }]}>{title}</Text>
172
+ <Text style={[styles.title, ff, { color: open && variant === 'default' ? tint : colors.foreground }]}>{title}</Text>
172
173
  ) : (
173
174
  title
174
175
  )}
175
- {subtitle ? <Text style={[styles.subtitle, { color: colors.mutedForeground }]}>{subtitle}</Text> : null}
176
+ {subtitle ? <Text style={[styles.subtitle, ff, { color: colors.mutedForeground }]}>{subtitle}</Text> : null}
176
177
  </View>
177
178
  <Animated.View
178
179
  style={{ transform: [{ rotate: turn.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '180deg'] }) }] }}
@@ -209,7 +210,7 @@ export function AccordionItem({ value, title, subtitle, icon, children, disabled
209
210
  ]}
210
211
  >
211
212
  {isTextNode(children) ? (
212
- <Text style={[styles.text, { color: colors.mutedForeground }]}>{children}</Text>
213
+ <Text style={[styles.text, ff, { color: colors.mutedForeground }]}>{children}</Text>
213
214
  ) : (
214
215
  children
215
216
  )}
@@ -181,14 +181,17 @@ export function useKit(): Theme {
181
181
  return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
182
182
  }
183
183
 
184
- /** Shared motion presets so the whole kit moves the same way. */
184
+ /**
185
+ * Shared motion presets so the whole kit moves the same way. Every spring is critically damped
186
+ * (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
187
+ */
185
188
  export const motion = {
186
189
  /** Toggles, selection indicators. */
187
- snappy: { damping: 18, stiffness: 260, mass: 0.8 },
188
- /** Press feedback, pops. */
189
- bouncy: { damping: 11, stiffness: 220, mass: 0.7 },
190
+ snappy: { damping: 30, stiffness: 260, mass: 0.8 },
191
+ /** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
192
+ bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
190
193
  /** Sheets, layout changes. */
191
- gentle: { damping: 22, stiffness: 160, mass: 1 },
194
+ gentle: { damping: 27, stiffness: 160, mass: 1 },
192
195
  fast: 160,
193
196
  normal: 240,
194
197
  };
@@ -134,7 +134,8 @@ export function ActionSheet({
134
134
  haptics = true,
135
135
  style,
136
136
  }: ActionSheetProps) {
137
- const { colors, radius, dark } = useKit();
137
+ const { colors, radius, dark, font } = useKit();
138
+ const ff = font.family ? { fontFamily: font.family } : null;
138
139
  const { height: winH } = useWindowDimensions();
139
140
  const reduceMotion = useReduceMotion();
140
141
  const tint = accent ?? colors.accent;
@@ -247,13 +248,13 @@ export function ActionSheet({
247
248
  {title ? (
248
249
  <Text
249
250
  accessibilityRole="header"
250
- style={[list ? styles.titleLarge : styles.title, { color: list ? colors.foreground : colors.mutedForeground }]}
251
+ style={[list ? styles.titleLarge : styles.title, ff, { color: list ? colors.foreground : colors.mutedForeground }]}
251
252
  >
252
253
  {title}
253
254
  </Text>
254
255
  ) : null}
255
256
  {message ? (
256
- <Text style={[styles.message, { color: colors.mutedForeground, textAlign: list ? 'left' : 'center' }]}>
257
+ <Text style={[styles.message, ff, { color: colors.mutedForeground, textAlign: list ? 'left' : 'center' }]}>
257
258
  {message}
258
259
  </Text>
259
260
  ) : null}
@@ -265,7 +266,7 @@ export function ActionSheet({
265
266
  cancelLabel !== null ? (
266
267
  <Card corner={corner} bg={colors.popover} edge={edge} dark={dark}>
267
268
  <Row onPress={dismiss} highlight={highlight} label={cancelLabel} style={styles.row}>
268
- <Text style={[styles.rowLabel, styles.center, { color: variant === 'inset' ? tint : colors.foreground }]}>
269
+ <Text style={[styles.rowLabel, ff, styles.center, { color: variant === 'inset' ? tint : colors.foreground }]}>
269
270
  {cancelLabel}
270
271
  </Text>
271
272
  </Row>
@@ -295,7 +296,7 @@ export function ActionSheet({
295
296
  <View style={[styles.tileIcon, { backgroundColor: fill, borderRadius: radius.scale(16) }]}>
296
297
  {icon(a, fg, 24)}
297
298
  </View>
298
- <Text numberOfLines={2} style={[styles.tileLabel, { color: a.destructive ? colors.danger : colors.foreground }]}>
299
+ <Text numberOfLines={2} style={[styles.tileLabel, ff, { color: a.destructive ? colors.danger : colors.foreground }]}>
299
300
  {a.label}
300
301
  </Text>
301
302
  </Row>
@@ -337,7 +338,7 @@ export function ActionSheet({
337
338
  {a.icon ? (
338
339
  <View style={styles.listIcon}>{icon(a, a.destructive ? colors.danger : colors.mutedForeground, 22)}</View>
339
340
  ) : null}
340
- <Text style={[styles.listLabel, { color }]} numberOfLines={1}>
341
+ <Text style={[styles.listLabel, ff, { color }]} numberOfLines={1}>
341
342
  {a.label}
342
343
  </Text>
343
344
  </Row>
@@ -347,7 +348,7 @@ export function ActionSheet({
347
348
  <>
348
349
  <View style={[styles.divider, { backgroundColor: separator }]} />
349
350
  <Row onPress={dismiss} highlight={highlight} corner={radius.md} label={cancelLabel} style={styles.listRow}>
350
- <Text style={[styles.listLabel, styles.center, { color: colors.mutedForeground }]}>{cancelLabel}</Text>
351
+ <Text style={[styles.listLabel, ff, styles.center, { color: colors.mutedForeground }]}>{cancelLabel}</Text>
351
352
  </Row>
352
353
  </>
353
354
  ) : null}
@@ -379,7 +380,7 @@ export function ActionSheet({
379
380
  (i > 0 || header) && { borderTopWidth: StyleSheet.hairlineWidth, borderTopColor: separator },
380
381
  ]}
381
382
  >
382
- <Text style={[styles.rowLabel, withIcons ? styles.rowLabelMenu : styles.center, { color }]} numberOfLines={1}>
383
+ <Text style={[styles.rowLabel, ff, withIcons ? styles.rowLabelMenu : styles.center, { color }]} numberOfLines={1}>
383
384
  {a.label}
384
385
  </Text>
385
386
  {withIcons ? <View style={styles.rowIcon}>{a.icon ? icon(a, color, 22) : null}</View> : null}
@@ -181,14 +181,17 @@ export function useKit(): Theme {
181
181
  return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
182
182
  }
183
183
 
184
- /** Shared motion presets so the whole kit moves the same way. */
184
+ /**
185
+ * Shared motion presets so the whole kit moves the same way. Every spring is critically damped
186
+ * (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
187
+ */
185
188
  export const motion = {
186
189
  /** Toggles, selection indicators. */
187
- snappy: { damping: 18, stiffness: 260, mass: 0.8 },
188
- /** Press feedback, pops. */
189
- bouncy: { damping: 11, stiffness: 220, mass: 0.7 },
190
+ snappy: { damping: 30, stiffness: 260, mass: 0.8 },
191
+ /** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
192
+ bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
190
193
  /** Sheets, layout changes. */
191
- gentle: { damping: 22, stiffness: 160, mass: 1 },
194
+ gentle: { damping: 27, stiffness: 160, mass: 1 },
192
195
  fast: 160,
193
196
  normal: 240,
194
197
  };
@@ -89,7 +89,8 @@ export function Alert({
89
89
  color,
90
90
  style,
91
91
  }: AlertProps) {
92
- const { colors, radius, dark } = useKit();
92
+ const { colors, radius, dark, font } = useKit();
93
+ const ff = font.family ? { fontFamily: font.family } : null;
93
94
  const uid = useId().replace(/[^a-zA-Z0-9]/g, '');
94
95
  const reduceMotion = useReduceMotion();
95
96
  const tones: Record<AlertVariant, string> = {
@@ -322,9 +323,9 @@ export function Alert({
322
323
  <>
323
324
  {iconNode}
324
325
  <View style={[styles.body, { paddingTop: textTop }]}>
325
- {title ? <Text style={[styles.title, { color: fg }]}>{title}</Text> : null}
326
+ {title ? <Text style={[styles.title, ff, { color: fg }]}>{title}</Text> : null}
326
327
  {typeof description === 'string' ? (
327
- <Text style={[styles.desc, { color: title ? sub : fg }]}>{description}</Text>
328
+ <Text style={[styles.desc, ff, { color: title ? sub : fg }]}>{description}</Text>
328
329
  ) : (
329
330
  description
330
331
  )}
@@ -443,6 +444,8 @@ function Pill({
443
444
  bg: string;
444
445
  fg: string;
445
446
  }) {
447
+ const { font } = useKit();
448
+ const ff = font.family ? { fontFamily: font.family } : null;
446
449
  return (
447
450
  <Pressable
448
451
  accessibilityRole="button"
@@ -455,7 +458,7 @@ function Pill({
455
458
  pressed ? { opacity: 0.75, transform: [{ scale: 0.97 }] } : null,
456
459
  ]}
457
460
  >
458
- <Text style={[styles.pillText, { color: fg }]} numberOfLines={1}>
461
+ <Text style={[styles.pillText, ff, { color: fg }]} numberOfLines={1}>
459
462
  {action.label}
460
463
  </Text>
461
464
  </Pressable>
@@ -116,7 +116,8 @@ export function Button({
116
116
  onPressOut,
117
117
  ...props
118
118
  }: ButtonProps) {
119
- const { colors, radius, dark } = useKit();
119
+ const { colors, radius, dark, font } = useKit();
120
+ const ff = font.family ? { fontFamily: font.family } : null;
120
121
  const gid = 'btn' + useId().replace(/[^a-zA-Z0-9]/g, '');
121
122
  const scale = useRef(new Animated.Value(1)).current;
122
123
  const isDisabled = disabled || loading;
@@ -149,7 +150,7 @@ export function Button({
149
150
  const label = isTextNode(children) ? (
150
151
  <Text
151
152
  numberOfLines={1}
152
- style={[styles.label, { color: fg, fontSize: s.font }, variant === 'link' && styles.link, textStyle]}
153
+ style={[styles.label, ff, { color: fg, fontSize: s.font }, variant === 'link' && styles.link, textStyle]}
153
154
  >
154
155
  {children}
155
156
  </Text>
@@ -235,7 +236,7 @@ export function Button({
235
236
  <View style={styles.row}>
236
237
  <ActivityIndicator size="small" color={fg} />
237
238
  {size !== 'icon' && isTextNode(children) ? (
238
- <Text style={[styles.label, { color: fg, fontSize: s.font, opacity: 0.85 }]}>{children}</Text>
239
+ <Text style={[styles.label, ff, { color: fg, fontSize: s.font, opacity: 0.85 }]}>{children}</Text>
239
240
  ) : null}
240
241
  </View>
241
242
  ) : (
@@ -181,14 +181,17 @@ export function useKit(): Theme {
181
181
  return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
182
182
  }
183
183
 
184
- /** Shared motion presets so the whole kit moves the same way. */
184
+ /**
185
+ * Shared motion presets so the whole kit moves the same way. Every spring is critically damped
186
+ * (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
187
+ */
185
188
  export const motion = {
186
189
  /** Toggles, selection indicators. */
187
- snappy: { damping: 18, stiffness: 260, mass: 0.8 },
188
- /** Press feedback, pops. */
189
- bouncy: { damping: 11, stiffness: 220, mass: 0.7 },
190
+ snappy: { damping: 30, stiffness: 260, mass: 0.8 },
191
+ /** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
192
+ bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
190
193
  /** Sheets, layout changes. */
191
- gentle: { damping: 22, stiffness: 160, mass: 1 },
194
+ gentle: { damping: 27, stiffness: 160, mass: 1 },
192
195
  fast: 160,
193
196
  normal: 240,
194
197
  };
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "assistant-app",
3
+ "type": "template",
4
+ "title": "AI assistant app",
5
+ "description": "A complete Expo Router app for an AI chat assistant: onboarding once, Apple / Google / email-code sign in, a streaming chat with suggestions and stop, conversation history, profile and settings.",
6
+ "tier": "pro",
7
+ "dependencies": [
8
+ "react-native-svg",
9
+ "expo-haptics",
10
+ "expo-router",
11
+ "expo-splash-screen",
12
+ "expo-status-bar",
13
+ "react-native-safe-area-context",
14
+ "react-native-screens",
15
+ "@react-native-async-storage/async-storage"
16
+ ],
17
+ "registryDependencies": [
18
+ "kit",
19
+ "bottom-nav",
20
+ "onboarding",
21
+ "sign-in",
22
+ "chat",
23
+ "inbox",
24
+ "profile",
25
+ "settings"
26
+ ],
27
+ "sharedFiles": [
28
+ "lib/kit.tsx",
29
+ "components/ui/bottom-nav.tsx",
30
+ "components/blocks/onboarding.tsx",
31
+ "components/ui/button.tsx",
32
+ "components/ui/floating-input.tsx",
33
+ "components/ui/password-input.tsx",
34
+ "components/ui/otp-input.tsx",
35
+ "components/blocks/sign-in.tsx",
36
+ "components/blocks/chat.tsx",
37
+ "components/ui/avatar.tsx",
38
+ "components/ui/badge.tsx",
39
+ "components/ui/chip-group.tsx",
40
+ "components/ui/search-bar.tsx",
41
+ "components/ui/swipeable-row.tsx",
42
+ "components/blocks/inbox.tsx",
43
+ "components/blocks/profile.tsx",
44
+ "components/ui/segmented-control.tsx",
45
+ "components/ui/switch.tsx",
46
+ "components/blocks/settings.tsx"
47
+ ],
48
+ "filesDir": null
49
+ }
@@ -131,7 +131,8 @@ export function Avatar({
131
131
  haptics = true,
132
132
  style,
133
133
  }: AvatarProps) {
134
- const { colors, radius, dark } = useKit();
134
+ const { colors, radius, dark, font } = useKit();
135
+ const ff = font.family ? { fontFamily: font.family } : null;
135
136
  const reduceMotion = useReduceMotion();
136
137
  const uid = useId().replace(/[^a-zA-Z0-9]/g, '');
137
138
  const px = resolveSize(size);
@@ -280,6 +281,7 @@ export function Avatar({
280
281
  numberOfLines={1}
281
282
  style={[
282
283
  styles.initials,
284
+ ff,
283
285
  {
284
286
  color: ink,
285
287
  fontSize: Math.max(9, px * (initials.length > 1 ? 0.37 : 0.42)),
@@ -448,7 +450,7 @@ export function Avatar({
448
450
  {badgeText ? (
449
451
  <Text
450
452
  allowFontScaling={false}
451
- style={[styles.badgeText, { color: colors.dangerForeground, fontSize: Math.max(9, badgeH * 0.6) }]}
453
+ style={[styles.badgeText, ff, { color: colors.dangerForeground, fontSize: Math.max(9, badgeH * 0.6) }]}
452
454
  >
453
455
  {badgeText}
454
456
  </Text>
@@ -623,7 +625,8 @@ export function AvatarGroup({
623
625
  onPressMore,
624
626
  style,
625
627
  }: AvatarGroupProps) {
626
- const { colors, radius, dark } = useKit();
628
+ const { colors, radius, dark, font } = useKit();
629
+ const ff = font.family ? { fontFamily: font.family } : null;
627
630
  const reduceMotion = useReduceMotion();
628
631
  const px = resolveSize(size);
629
632
  const bg = surface ?? colors.background;
@@ -699,7 +702,7 @@ export function AvatarGroup({
699
702
  <Text
700
703
  allowFontScaling={false}
701
704
  numberOfLines={1}
702
- style={[styles.moreText, { color: colors.foreground, fontSize: Math.max(8, px * (extraText.length <= 2 ? 0.34 : extraText.length === 3 ? 0.29 : 0.24)) }]}
705
+ style={[styles.moreText, ff, { color: colors.foreground, fontSize: Math.max(8, px * (extraText.length <= 2 ? 0.34 : extraText.length === 3 ? 0.29 : 0.24)) }]}
703
706
  >
704
707
  +{extraText}
705
708
  </Text>
@@ -181,14 +181,17 @@ export function useKit(): Theme {
181
181
  return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
182
182
  }
183
183
 
184
- /** Shared motion presets so the whole kit moves the same way. */
184
+ /**
185
+ * Shared motion presets so the whole kit moves the same way. Every spring is critically damped
186
+ * (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
187
+ */
185
188
  export const motion = {
186
189
  /** Toggles, selection indicators. */
187
- snappy: { damping: 18, stiffness: 260, mass: 0.8 },
188
- /** Press feedback, pops. */
189
- bouncy: { damping: 11, stiffness: 220, mass: 0.7 },
190
+ snappy: { damping: 30, stiffness: 260, mass: 0.8 },
191
+ /** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
192
+ bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
190
193
  /** Sheets, layout changes. */
191
- gentle: { damping: 22, stiffness: 160, mass: 1 },
194
+ gentle: { damping: 27, stiffness: 160, mass: 1 },
192
195
  fast: 160,
193
196
  normal: 240,
194
197
  };