@vvedantb/native-ui 0.13.0 → 0.17.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 (108) hide show
  1. package/README.md +40 -19
  2. package/package.json +36 -3
  3. package/src/BottomSheetContainer.tsx +2 -2
  4. package/src/BulkActionBar.tsx +240 -105
  5. package/src/Button.tsx +11 -7
  6. package/src/ConfirmDialog.tsx +25 -35
  7. package/src/Dialog.tsx +47 -35
  8. package/src/DividedList.tsx +11 -7
  9. package/src/DropdownMenu.tsx +22 -8
  10. package/src/FloatingSurface.tsx +5 -2
  11. package/src/FloatingTabBar.tsx +13 -22
  12. package/src/HeaderIconButton.tsx +4 -3
  13. package/src/Input.tsx +2 -2
  14. package/src/LoadingState.tsx +132 -0
  15. package/src/PageTitle.tsx +9 -2
  16. package/src/Pager.tsx +62 -0
  17. package/src/PasswordInput.tsx +1 -1
  18. package/src/PillTabs.tsx +144 -34
  19. package/src/Progress.tsx +9 -7
  20. package/src/Select.tsx +4 -4
  21. package/src/Sheet.tsx +29 -22
  22. package/src/SheetRow.tsx +27 -9
  23. package/src/Skeleton.tsx +91 -9
  24. package/src/SurfaceRow.tsx +111 -0
  25. package/src/SwipeableRow.tsx +43 -0
  26. package/src/Switch.tsx +6 -4
  27. package/src/Tabs.tsx +68 -13
  28. package/src/Toast.tsx +107 -65
  29. package/src/ai/activity-step-detail.tsx +1 -3
  30. package/src/ai/activity-step-label.ts +981 -0
  31. package/src/ai/activity-step-types.ts +107 -0
  32. package/src/ai/activity-tasks-utils.ts +199 -0
  33. package/src/ai/activity-timeline.ts +45 -0
  34. package/src/ai/attachments.tsx +3 -1
  35. package/src/ai/chat-text.ts +13 -7
  36. package/src/ai/chat-thread.tsx +34 -27
  37. package/src/ai/choice-question.tsx +154 -35
  38. package/src/ai/code-block.tsx +189 -49
  39. package/src/ai/collapsible.tsx +19 -13
  40. package/src/ai/conversation.tsx +390 -57
  41. package/src/ai/icon-button.tsx +41 -6
  42. package/src/ai/index.ts +38 -1
  43. package/src/ai/message-action-sheet.tsx +4 -1
  44. package/src/ai/message.tsx +60 -31
  45. package/src/ai/prompt-input-menu.tsx +197 -0
  46. package/src/ai/prompt-input.tsx +301 -75
  47. package/src/ai/shimmer.tsx +4 -6
  48. package/src/ai/trace.tsx +54 -17
  49. package/src/ai/unified-diff.tsx +182 -0
  50. package/src/auth/GoogleSignInButton.tsx +6 -3
  51. package/src/auth/SignInScreen.tsx +11 -2
  52. package/src/brand/ProviderIcon.tsx +136 -0
  53. package/src/brand/index.ts +2 -0
  54. package/src/configureTheme.ts +39 -11
  55. package/src/controlStyles.ts +46 -30
  56. package/src/entries/gesture-handler.ts +4 -0
  57. package/src/entries/keyboard-controller.ts +4 -0
  58. package/src/entries/markdown.ts +1 -1
  59. package/src/entries/primitives.ts +24 -3
  60. package/src/entries/theme.ts +2 -1
  61. package/src/entries/video.ts +4 -0
  62. package/src/entries/webview.ts +6 -0
  63. package/src/floating.ts +25 -4
  64. package/src/haptics.ts +30 -4
  65. package/src/highlight.ts +302 -1
  66. package/src/index.ts +27 -5
  67. package/src/loadingGrid.ts +75 -0
  68. package/src/markdown/Markdown.tsx +234 -32
  69. package/src/markdown/MarkdownTable.tsx +188 -0
  70. package/src/markdown/tableText.ts +58 -0
  71. package/src/marketing/UpgradeToProCard.tsx +4 -3
  72. package/src/media/ImageGallery.tsx +224 -0
  73. package/src/media/MediaCard.tsx +100 -0
  74. package/src/media/imageGrid.ts +51 -0
  75. package/src/motion.ts +33 -0
  76. package/src/pagerSwipe.ts +102 -0
  77. package/src/palette.ts +8 -1
  78. package/src/primitives/button.tsx +4 -1
  79. package/src/primitives/keyboard-controller.tsx +106 -0
  80. package/src/primitives/keyboard-parts.tsx +104 -0
  81. package/src/primitives/overlay.tsx +133 -45
  82. package/src/primitives/pager-gesture.tsx +131 -0
  83. package/src/primitives/sheet-drag.tsx +305 -0
  84. package/src/primitives/sheet-gesture.tsx +181 -0
  85. package/src/primitives/swipe-row-gesture.tsx +290 -0
  86. package/src/primitives/toast-drag-gesture.tsx +92 -0
  87. package/src/primitives/web-click-guard.ts +50 -0
  88. package/src/relativeTime.ts +94 -0
  89. package/src/router/RouterStackPageHeader.tsx +2 -2
  90. package/src/settings/SettingsEmptyState.tsx +20 -10
  91. package/src/settings/SettingsField.tsx +11 -5
  92. package/src/settings/SettingsSection.tsx +8 -5
  93. package/src/settings/SettingsToggleRow.tsx +55 -22
  94. package/src/settings/relaxedSizeStyle.ts +11 -0
  95. package/src/sheetDrag.ts +67 -0
  96. package/src/swipeRow.ts +169 -0
  97. package/src/tabBarRoutes.ts +18 -0
  98. package/src/tabIndicator.ts +71 -0
  99. package/src/tailwind-preset.cjs +13 -6
  100. package/src/tailwind-preset.d.cts +5 -0
  101. package/src/theme.tsx +6 -2
  102. package/src/toastDrag.ts +58 -0
  103. package/src/typography.ts +20 -3
  104. package/src/useScheme.ts +10 -1
  105. package/src/video/InlineVideo.tsx +91 -0
  106. package/src/webview/HtmlView.tsx +91 -0
  107. package/src/webview/HtmlView.web.tsx +21 -0
  108. package/src/webview/htmlView.ts +45 -0
package/README.md CHANGED
@@ -25,6 +25,8 @@ Optional, only if you import those components:
25
25
  - `@gorhom/bottom-sheet` — `BottomSheetContainer`
26
26
  - `@react-native-community/datetimepicker` and `dayjs` — `DateTimeField`
27
27
  - `@clerk/clerk-expo`, `convex`, `expo-auth-session`, `expo-web-browser` and `expo-router` — `@vvedantb/native-ui/auth` (0.7.0). `expo-router` also for `@vvedantb/native-ui/expo-web-root`.
28
+ - `react-native-gesture-handler` (`>=2.0.0`) — `@vvedantb/native-ui/gesture-handler` (Unreleased). Wrap the app in `GestureHandlerRootView`, and put `GestureHandlerProvider` from that entry inside it: the sheet drags (`SheetContent`, the compact `DialogContent`) then run on Gesture Handler, on the UI thread, and each overlay's `Modal` gets its own root view (Android needs one in every `Modal`). It also turns on the `SwipeableRow` swipe. Without the provider, the sheets drag with `PanResponder`, with the same rules, and a `SwipeableRow` does not swipe. Only that entry imports the peer, so an app without it bundles as before. In a Metro monorepo, resolve the peer from the app (one copy), as for `react`.
29
+ - `react-native-keyboard-controller` (`>=1.21.0`) — `@vvedantb/native-ui/keyboard-controller` (Unreleased). Put `KeyboardControllerProvider` from that entry inside the app's `KeyboardProvider`: the chat transcript (`ConversationList`), the dock (`ConversationDock`) and its scroll button then follow the keyboard frame by frame, a drag down on the transcript moves the keyboard with the finger (iOS, Android 11+; Android 10 and below lower it at once), and `PromptInput`, `ChatThread`, the overlays and `KeyboardAvoidingView` pad frame by frame. The dock moves by the keyboard less the bottom inset: its content pads that inset, as `PromptInput` does. Without the provider, every part keeps React Native's `KeyboardAvoidingView`. Only that entry imports the peer. In a Metro monorepo, resolve the peer from the app (one copy): the parts read its provider's context.
28
30
  - `react-native-svg` — the app marks (`APP_ICONS`, `Icon*`, `AppMark`, `AppWordmark`), `GoogleMark` and `PricingModal`. Apps with the icon peers above already have it: `lucide-react-native` and `@tabler/icons-react-native` both require it.
29
31
 
30
32
  Optional and loaded only when installed, so the package imports and runs without them:
@@ -55,7 +57,7 @@ To hide a tab, give its screen Expo Router's `href: null`. From 0.3.9 `FloatingT
55
57
  </Tabs>
56
58
  ```
57
59
 
58
- From 0.3.16 the focused tab sits on a rounded pill with the `TabsTrigger` pills outline, with the foreground label and icon; the other tabs are muted. From 0.3.17 the pill fill is `floatingTabPillColors(scheme)`: the muted `palette.default` (`--default`, `#ebebeb`) in light, so it shows on the near-white glass bar, and `palette.segment` (`--segment`, `#474747`) in dark. On the web the `--segment` pill sits on a `--default` track; here the bar is the raised surface, so the light pill takes the track fill. A route's `tabBarActiveTintColor` and `tabBarInactiveTintColor` still override the label and icon colours. The pill is one tab wide, inset 4 from the bar on every side, and slides to a newly focused tab over the web `--tabs-dur` (200 ms) and `--tabs-ease`. It counts only the tabs drawn, and there is none while a hidden route is focused.
60
+ From 0.3.16 the focused tab sits on a rounded pill with the `TabsTrigger` pills outline, with the foreground label and icon; the other tabs are muted. From 0.3.17 the pill fill is `floatingTabPillColors(scheme)`: the muted `palette.default` (`--default`, `#ebebeb`) in light, so it shows on the near-white glass bar, and `palette.tabBarPill` (`#474747`, the kit `--segment`) in dark. `palette.tabBarPill` is its own key, so an app that themes `segment` (the segmented `Tabs` thumb) keeps a pill that shows on the dark glass bar. On the web the `--segment` pill sits on a `--default` track; here the bar is the raised surface, so the light pill takes the track fill. A route's `tabBarActiveTintColor` and `tabBarInactiveTintColor` still override the label and icon colours. The pill is one tab wide, inset 4 from the bar on every side, and slides to a newly focused tab over the web `--tabs-dur` (200 ms) and `--tabs-ease`. It counts only the tabs drawn, and there is none while a hidden route is focused.
59
61
 
60
62
  ## Entry points
61
63
 
@@ -66,12 +68,12 @@ The root entry exports everything. It still imports `BottomSheetContainer` and `
66
68
  | `@vvedantb/native-ui/tab-bar` | `FloatingTabBar`, `FloatingTabBarProps`, `FLOATING_INSET`, `FLOATING_TAB_BAR_HEIGHT`, `FLOATING_TAB_BAR_HEIGHT_LARGE`, `tabBarReserve` | `expo-blur`, `expo-haptics` (loaders) |
67
69
  | `@vvedantb/native-ui/brand` | `AppMark`, `AppWordmark`, `GoogleMark` (from 0.3.15), `APP_ICONS`, `IconVibot` and the other `Icon*`, the SVG helpers (`APP_MARKS`, `appMarkSvg`, `appTileSvg` and the rest), `FAMILY_APPS`, `familyApp`, `V_PRO_APP_BENEFITS` | none |
68
70
  | `@vvedantb/native-ui/button` | `Button`, `ButtonProps`, `ButtonShape` (0.6.0), `ButtonSize`, `ButtonVariant` | none |
69
- | `@vvedantb/native-ui/primitives` | `Button`, `Input`, `SearchInput`, `PasswordInput` (0.11.0), `Textarea`, `Label`, `Select`, `Switch`, `LabeledSwitchRow`, `Checkbox`, `Card` and its parts, `Badge`, `StatusDot`, `Avatar`, `Separator`, `Progress`, `Spinner`, `Skeleton`, `EmptyState`, `ListRow`, `Chip`, `HeaderIconButton`, `ThemeToggle` (from 0.3.16), `Tabs`, `PillTabs`, `Dialog`, `AlertDialog`, `ConfirmDialog`, `Sheet`, `toast`, `Toaster`. From 0.5.0: `Text` and `TextClassContext` (only here, not on the root), `ColorSwatches`, `StatGrid`, `SectionLabel`, `SheetRow`, `PageIndicator`, `IconButton`, `CaptureButton`, and the `ListRowProps` and `CardProps` types. From 0.6.0: the `ButtonShape` type. Unreleased: `DropdownMenu` and its parts, `BulkActionBar` | `expo-blur`, `expo-haptics` (loaders) |
71
+ | `@vvedantb/native-ui/primitives` | `Button`, `Input`, `SearchInput`, `PasswordInput` (0.11.0), `Textarea`, `Label`, `Select`, `Switch`, `LabeledSwitchRow`, `Checkbox`, `Card` and its parts, `Badge`, `StatusDot`, `Avatar`, `Separator`, `Progress`, `Spinner`, `Skeleton`, `EmptyState`, `ListRow`, `Chip`, `HeaderIconButton`, `ThemeToggle` (from 0.3.16), `Tabs`, `PillTabs`, `Dialog`, `AlertDialog`, `ConfirmDialog`, `Sheet`, `toast`, `Toaster`. From 0.5.0: `Text` and `TextClassContext` (only here, not on the root), `ColorSwatches`, `StatGrid`, `SectionLabel`, `SheetRow`, `PageIndicator`, `IconButton`, `CaptureButton`, and the `ListRowProps` and `CardProps` types. From 0.6.0: the `ButtonShape` type. Unreleased: `DropdownMenu` and its parts, `BulkActionBar`, `SwipeableRow` (with `closeSwipeRows`, `swipeActionsAccessibility` and the `SwipeAction` and `SwipeableRowProps` types). From 0.14.0: `formatCompactRelativeTime` (with the `CompactRelativeTimeOptions` type), and `TabsTrigger` `minHeight` | `expo-blur`, `expo-haptics` (loaders) |
70
72
  | `@vvedantb/native-ui/search-input` | `SearchInput`, `SearchInputProps` | none |
71
73
  | `@vvedantb/native-ui/password-input` | `PasswordInput`, `PasswordInputProps` (from 0.11.0) | none |
72
74
  | `@vvedantb/native-ui/page-title` | `PageTitle`, `PageHeader`, `PageTitleProps`, `PageHeaderProps` (from 0.3.13), `StackPageHeader`, `StackPageHeaderProps` (from 0.5.0; it brings the Tabler package for its chevron) | none |
73
75
  | `@vvedantb/native-ui/toast` | `toast`, `Toaster`, `ToastOptions`, `ToastVariant` | none (from 0.3.12) |
74
- | `@vvedantb/native-ui/theme` | `NativeUiProvider`, `useNativeUi`, `useLargerText`, `useAccentColor`, `DEFAULT_ACCENT`, `tokens`, `palette`, `withAlpha`, `configureTheme` (with the `ThemeConfig`, `ThemeColors` and `ThemePalette` types), `fontClass`, `cn`, `Colors` and the colour constants | none |
76
+ | `@vvedantb/native-ui/theme` | `NativeUiProvider`, `useNativeUi`, `useLargerText`, `useAccentColor`, `DEFAULT_ACCENT`, `tokens`, `palette`, `withAlpha`, `configureTheme` (with the `ThemeConfig`, `ThemeColors` and `ThemePalette` types), `fontClass`, `fontFamily` (with the `ThemeFonts` type), `cn`, `Colors` and the colour constants | none |
75
77
  | `@vvedantb/native-ui/floating` | `FloatingSurface`, `GlassSheetBackground`, `LiquidGlass`, `GlassBlurProvider`, `GlassBlurTarget`, `FloatingFAB`, `useReduceTransparency`, the floating metrics and styles | `expo-blur` (loader) |
76
78
  | `@vvedantb/native-ui/layout` | `ScreenContainer`, `SafeAreaContainer`, `PageTitle`, `PageHeader`, `StackPageHeader` (0.5.0) and their props types, `SectionLabel` (0.5.0), `SettingsSection`, `SettingsField`, `SettingsToggleRow`, `SettingsStack`, `MoreFromProjectV` and `FromProjectV` (0.8.0), `ScreenContainerProps`, `SettingsEmptyState`, `PinnedBotTile`, `BotPinGrid`, `ArtifactList` (0.12.0). `ScreenContainer` takes `refreshing`, `onRefresh` and `refreshControl` (0.12.0). `StackPageHeader` takes `centre` (0.12.0) | `expo-blur` (loader) |
77
79
  | `@vvedantb/native-ui/marketing` | `UpgradeToProCard`, `PricingModal` and the pricing types | none |
@@ -79,6 +81,8 @@ The root entry exports everything. It still imports `BottomSheetContainer` and `
79
81
  | `@vvedantb/native-ui/blur`, `/haptics` | the loaders | `expo-blur`, `expo-haptics` (loaders) |
80
82
  | `@vvedantb/native-ui/push` | `useExpoPushRegistration`, `getExpoPushToken`, `showPushesInForeground`, the three loaders and the types (0.9.0). Pass `register` from your backend (for `@vvedantb/server`: `useMutation(api.push.register)`) and route taps in `onNotificationTap` | `expo-notifications`, `expo-constants`, `expo-device` (loaders) |
81
83
  | `@vvedantb/native-ui/bottom-sheet` | `BottomSheetContainer` | `@gorhom/bottom-sheet` (import), `expo-blur` (loader) |
84
+ | `@vvedantb/native-ui/gesture-handler` | `GestureHandlerProvider` (Unreleased): the sheet drags on Gesture Handler, and the `SwipeableRow` swipe | `react-native-gesture-handler` (import) |
85
+ | `@vvedantb/native-ui/keyboard-controller` | `KeyboardControllerProvider` (Unreleased): the composer, transcript and forms follow the keyboard | `react-native-keyboard-controller` (import) |
82
86
  | `@vvedantb/native-ui/date-time` | `DateTimeField`, `DateTimeFieldProps` (0.5.0) | `@react-native-community/datetimepicker`, `dayjs` (import) |
83
87
  | `@vvedantb/native-ui/ai` | The AI chat parts (from 0.4.0): `Conversation`, `Message`, `Reasoning`, `AgentTrace`, `ChainOfThought`, `ActivityStep`, `PromptInput`, `Suggestions`, `Sources`, `FileChip`, `Attachments`, `CodeBlock`, `ResponseText`, `Shimmer`, `Collapsible`, `useStreamingDisclosure` and their parts. See [AI chat](#ai-chat-040). Not on the root entry.. From 0.12.0: `ChatThread`, `UIMessage`, `ChatJumpRail` and the `Attachment` props `onPress` and `accessibilityLabel` | none |
84
88
  | `@vvedantb/native-ui/ai-gallery` | `AiChatGallery`, `AiChatSamples` (from 0.4.0) | none |
@@ -126,6 +130,7 @@ module.exports = {
126
130
  | `color` | `cssVar`, or `rgbAlpha` with `injectCssVariables` | Maps a variable name to a colour value. `cssVar` gives `var(--name)`, `hslVar` gives `hsl(var(--name))` and `rgbAlpha` gives `rgb(var(--name) / <alpha-value>)`. For another prefix, pass your own, such as ``(name) => `rgb(var(--color-${name}) / <alpha-value>)` ``. |
127
131
  | `injectCssVariables` | `false` | Writes every colour variable from `tokens.ts` and `palette.ts` as `r g b` channels, and `--radius` (16px), for light (`:root`) and dark (`.dark`, or the media query with `darkMode: "media"`). The app's `global.css` then needs no token block. |
128
132
  | `radiusVariable` | `injectCssVariables` | `rounded-lg`, `-md` and `-sm` from `var(--radius)`. |
133
+ | `font` | Instrument Sans | `{ regular, medium, semibold, bold }` family names. Sets the family of `sans` and the four `font-instrument-sans*` classes. The class names stay the same. Pass the same names to `configureTheme({ font })` (see [App fonts](#app-fonts)). |
129
134
 
130
135
  The colour keys are `background`, `foreground`, `border`, `input`, `ring`, `separator`, `surface` (`DEFAULT`, `foreground`, `secondary`, `tertiary`, `card`), `field` (`DEFAULT`, `placeholder`) and, each with `DEFAULT` and `foreground`, `primary`, `secondary`, `destructive`, `muted`, `accent`, `popover`, `card`, `danger`, `success`, `warning`, `default` and `overlay`. Each reads the variable of the same name (`surface.secondary` reads `--surface-secondary`; `muted.foreground` reads `--muted-foreground`). `borderWidth.hairline` is NativeWind's `hairlineWidth()`.
131
136
 
@@ -307,6 +312,19 @@ The keys map to the shadcn-style variables as follows. `tokens.color`: `backgrou
307
312
 
308
313
  Limits: a call after the first render does not redraw mounted components. Only the JS colours change. NativeWind classes keep the app's Tailwind theme, and `nativeUiPreset({ injectCssVariables: true })` still writes the package values. Constants copied out at import (`NEUTRAL_500`) keep the package value; `floatingFill`, `floatingHairline` and `floatingSolidFill` follow. The `tokens` and `palette` types still name the package hex values.
309
314
 
315
+ ### App fonts
316
+
317
+ The default font is Instrument Sans. To use another, such as Inter, give the same family names in two places, one for each weight: `nativeUiPreset({ font })` in `tailwind.config.js` for the classes, and `configureTheme({ font })` at start for the JS styles (such as `Markdown`). Keys you leave out keep Instrument Sans. The app loads the font files; the package does not. `fontFamily` holds the current names (type `ThemeFonts`), on the root entry and `/theme`.
318
+
319
+ ```ts
320
+ // tailwind.config.js
321
+ const font = { regular: "Inter_400Regular", medium: "Inter_500Medium", semibold: "Inter_600SemiBold", bold: "Inter_700Bold" };
322
+ presets: [nativeUiPreset({ font })],
323
+
324
+ // app/_layout.tsx, outside the component
325
+ configureTheme({ font: { regular: "Inter_400Regular", medium: "Inter_500Medium", semibold: "Inter_600SemiBold", bold: "Inter_700Bold" } });
326
+ ```
327
+
310
328
  `useLargerText()` steps type and tap targets up. Without it, `Button`, `Input`, `Textarea` and the `Select` trigger have the web sizes (`Button` 32 / 40 / 44 high, fields 40); with it they keep the larger 0.3.2 sizes. `accentColor` tints FABs, card icons and progress bars (checked checkboxes use the palette primary, as on the web). Surfaces use the tokens in `src/tokens.ts` for fill, radius (card 24, field 10, pill 999; rows 20) and spacing (16). `Button` is a pill (999) by default, and 16 with `shape="rounded"`.
311
329
 
312
330
  ## Components (0.3.0)
@@ -315,13 +333,15 @@ Limits: a call after the first render does not redraw mounted components. Only t
315
333
  | --- | --- |
316
334
  | Actions and fields | `Button` (`default`/`primary`, `secondary`, `outline`, `destructive`, `ghost`, `link`, `tinted`; `sm`/`small`, `default`/`medium`, `lg`/`large`, `icon`; `shape` `pill`/`rounded` from 0.6.0), `IconButton` (0.5.0), `CaptureButton` (0.5.0), `Input`, `SearchInput` (0.3.9), `PasswordInput` (0.11.0), `Textarea`, `Label`, `Select`, `Switch`, `Checkbox`, `LabeledSwitchRow`, `ColorSwatches` (0.5.0), `DateTimeField` (date-only, empty and clearable from 0.5.0) |
317
335
  | Display | `Card` (+ `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`; `onPress` from 0.5.0), `Badge`, `StatusDot` (`info` from 0.5.0), `Avatar` (+ `AvatarImage`, `AvatarFallback`, `initials()`), `Separator`, `Progress`, `Spinner`, `CenteredSpinner`, `Skeleton`, `EmptyState` (+ `EmptyState.Root` and parts), `ListRow` (`leading`, `titleEnd`, `control`, `onLongPress` from 0.5.0), `StatGrid` (0.5.0), `SectionLabel` (0.5.0), `Chip`, `Text` (0.5.0, `/primitives` only) |
318
- | Navigation | `Tabs` (+ `TabsList`, `TabsTrigger`, `TabsContent`), `PillTabs`, `PageIndicator` (0.5.0), `FloatingTabBar`, `FloatingFAB`, `HeaderIconButton`, `ThemeToggle` (0.3.16), `StackPageHeader` (0.5.0) |
319
- | Overlays | `Dialog`, `AlertDialog`, `ConfirmDialog`, `Sheet` (RN `Modal`, each with the web part names), `SheetRow` (0.5.0), `BottomSheetContainer` (`@gorhom/bottom-sheet`), `DropdownMenu` (Unreleased; see below) |
336
+ | Navigation | `Tabs` (+ `TabsList`, `TabsTrigger` (`minHeight` from 0.14.0), `TabsContent`), `PillTabs`, `PageIndicator` (0.5.0), `FloatingTabBar`, `FloatingFAB`, `HeaderIconButton`, `ThemeToggle` (0.3.16), `StackPageHeader` (0.5.0) |
337
+ | Overlays | `Dialog`, `AlertDialog`, `ConfirmDialog`, `Sheet` (RN `Modal`, each with the web part names; a `SheetContent` or compact `DialogContent` drags down to close), `SheetRow` (0.5.0), `BottomSheetContainer` (`@gorhom/bottom-sheet`), `DropdownMenu` (Unreleased; see below) |
320
338
  | Selection | `BulkActionBar` (Unreleased; see below) |
321
339
  | Feedback | `toast()` and `<Toaster />` |
322
340
  | Brand and upgrade | `APP_ICONS`, `IconVibot` and the other `Icon*`, `AppMark`, `AppWordmark`, `GoogleMark` (0.3.15), `FAMILY_APPS`, `V_PRO_APP_BENEFITS`, `UpgradeToProCard`, `PricingModal` |
323
341
  | Settings and layout | `PageTitle`, `PageHeader` (0.3.13), `SettingsSection`, `SettingsField`, `SettingsToggleRow`, `SettingsStack`, `ScreenContainer`, `SafeAreaContainer`, `FloatingSurface`, `AmbientBackground` |
324
342
 
343
+ **Settings at web sizes (0.14.0).** `SettingsSection`, `SettingsToggleRow`, `SettingsField` and `SettingsEmptyState` take the web type: titles and labels `text-sm` (14 / 20; the section title semibold, the others medium), descriptions and help `text-xs leading-relaxed` (12 / 19.5; the section description `text-sm leading-relaxed`, 14 / 22.75). `SettingsToggleRow` steps up one size with larger text and has no minimum height; its control keeps the 44 target.
344
+
325
345
  **`Button` `shape` (0.6.0).** `Button` is a pill by default, as the web `Button` from `@vvedantb/ui` 0.31.0: `shape="pill"` is fully round (`tokens.radius.pill`, web `rounded-full`) for every variant and size, so `size="icon"` is a circle. `shape="rounded"` keeps the radius from before 0.6.0: 16 at every size (web `rounded-lg`). The web `rounded` is 14 (`rounded-md`) on `xs` and `icon-xs`; native has neither size. The radius is a style, so a `style` radius such as `style={{ borderRadius: 8 }}` wins over either shape; a `className` radius does not.
326
346
 
327
347
  ```tsx
@@ -404,6 +424,7 @@ Components and props that the Verve, vibot and Velth apps need. They follow the
404
424
  | `StatGrid` | Label and value tiles on the flat card, in rows of 2 or 3. |
405
425
  | `SectionLabel` | The muted `text-xs` medium label above a group of rows; `uppercase` for capitals. Add `className="px-4"` to line it up with rows. |
406
426
  | `DividedList` | Flat rows with a hairline between each pair, as the web `divide-y divide-border` lists. `inset` starts the dividers under the row text. |
427
+ | `SwipeableRow` (Unreleased) | A list row a horizontal swipe moves aside to reveal `leadingActions` / `trailingActions` (icon buttons on the `default`, `warning` or `danger` fill). The row follows the finger; on release it opens, closes or, past half the row (a light haptic as it arms), runs the first action, sliding out for an action that `exits`. A vertical drag scrolls first, one row is open at a time, a tap on a row or `closeSwipeRows` (a list's `onScrollBeginDrag`) closes it. Needs `GestureHandlerProvider`, else no swipe. Spread `swipeActionsAccessibility(actions)` on the row's pressable for screen readers. |
407
428
  | `SheetRow` | An action row for a `Sheet`: icon, title, detail; `active` adds a check, `danger` uses the danger colour, `disabled` dims it. |
408
429
  | `PageIndicator` | The web carousel dots. With `onChange` each dot is a 44 button named "Page 2 of 5", so the dots sit 44 apart. |
409
430
  | `StatusDot` `info` | A blue dot (`#3b82f6`, `#60a5fa` in dark), native only. |
@@ -432,7 +453,7 @@ Components and props that the Verve, vibot and Velth apps need. They follow the
432
453
 
433
454
  ```tsx
434
455
  // Typed routes: `afterAuthHref`, `signUpHref` and `signInHref` take expo-router's `Href`,
435
- // and `replaceOptions` takes `Parameters<Router["replace"]>[1]`.
456
+ // and `replaceOptions` takes the options of `useRouter().replace` (expo-router 57 exports no `Router` type).
436
457
  <SignInScreen scheme="app" afterAuthHref="/(app)/bots" signUpHref="/sign-up" replaceOptions={{ withAnchor: true }} />
437
458
 
438
459
  // Router-aware back: router.canGoBack() ? router.back() : router.replace(fallbackHref).
@@ -461,7 +482,7 @@ import { RouterStackPageHeader } from "@vvedantb/native-ui/expo-router";
461
482
  <ScreenContainer refreshing={refreshing} onRefresh={reload}>{children}</ScreenContainer>
462
483
  ```
463
484
 
464
- `StackPageHeader` `centre` replaces the centre `PageTitle` (for example a 20 px avatar beside a name). `title` stays the accessible name and the fallback; `description` still shows under it. The two sides keep the same width and the centre shrinks and truncates. `RouterStackPageHeader` takes the `StackPageHeader` props without `onBack`, plus `fallbackHref` (default `"/"`). `ChatThread` shows `thinking` right after the last user message while `working` and marks the last assistant message as streaming. A `PromptInput` footer keeps its own keyboard lift; the outer `KeyboardAvoidingView` moves it clear first, so the lift is not doubled. `SettingsEmptyState` is a compact empty note for the inside of a `SettingsSection`; `ArtifactList` lists artifacts as pressable rows; `ChatJumpRail` is a horizontal rail of chips that jump between messages.
485
+ `StackPageHeader` `centre` replaces the centre `PageTitle` (for example a 20 px avatar beside a name). `title` stays the accessible name and the fallback; `description` still shows under it. The two sides keep the same width and the centre shrinks and truncates. `RouterStackPageHeader` takes the `StackPageHeader` props without `onBack`, plus `fallbackHref` (default `"/"`). `ChatThread` shows `thinking` right after the last user message while `working` and marks the last assistant message as streaming. A `PromptInput` footer keeps its own keyboard lift; the outer `KeyboardAvoidingView` moves it clear first, so the lift is not doubled. `SettingsEmptyState` is the empty note for the inside of a `SettingsSection` (from 0.14.0 at the web size: a 28 icon at half opacity, a 14 / 20 medium title and a 12 / 19.5 description in `px-4 py-16`); `ArtifactList` lists artifacts as pressable rows; `ChatJumpRail` is a horizontal rail of chips that jump between messages.
465
486
 
466
487
  `Text` from `@vvedantb/native-ui/primitives` is the variant text (`h1`–`h4`, `p`, `lead`, `large`, `small`, `muted`, `code`, `blockquote`). It is not on the root entry. Unlike the components, it takes its colour from NativeWind classes (`text-foreground`, `text-muted-foreground`); where the app defines `foreground` with a nested `dark` key, give it `style={{ color: tokens.color.foreground[scheme] }}` in dark mode.
467
488
 
@@ -513,32 +534,32 @@ import {
513
534
 
514
535
  | Part | Notes |
515
536
  | --- | --- |
516
- | `Conversation`, `ConversationContent`, `ConversationEmptyState`, `ConversationScrollButton`, `useConversation` | A `ScrollView` that sticks to the bottom: while the reader is within 70 of the end, new and growing messages stay in view. Once they drag up it stops following, and the scroll button shows: a round outline arrow (40, slop to 48), 16 above the bottom, with an opaque fill and no shadow. Pressing it scrolls down (a jump with Reduce Motion). For very long threads, drive a `FlatList` with `useConversation`. |
517
- | `Message`, `MessageContent`, `MessageActionsBar`, `MessageAction` | (0.13.0: `Message` `onLongPress`, `actions`, `highlighted`; `MessageContent` `quote`; the `reply` kind.) A user message is the web primary bubble (radius 16, bottom-right 14, `px-4 py-3`) at the end of the row; an assistant message has no fill. `MessageAction` is a ghost icon button at the web `icon-sm` size (32, slop to 44). `kind` gives the icon and label: "Copy", "Retry", "Good response", "Bad response". `selected` marks chosen feedback. |
537
+ | `Conversation`, `ConversationContent`, `ConversationEmptyState`, `ConversationScrollButton`, `ConversationDock`, `useConversation` | A `ScrollView` that sticks to the bottom: while the reader is within 70 of the end, new and growing messages stay in view. Once they drag up it stops following, and the scroll button shows: a round outline arrow (40, slop to 48), 16 above the bottom, with an opaque fill and no shadow. Put the composer (or a docked question card) in a `ConversationDock` inside the `Conversation`, after the list: the button then sits 16 above the dock, so it moves with the dock and the keyboard. `insets` (for example `{ right: 10 }`) centres it on an off-centre thread. With `showLabel` (0.14.0) it is a 28-high pill with the icon and `label`, as the web button with a label (Eva: `label="Scroll to end"`, a chevron). Pressing it scrolls down (a jump with Reduce Motion). For very long threads, drive a `FlatList` with `useConversation`. |
538
+ | `Message`, `MessageContent`, `MessageActionsBar`, `MessageAction` | (0.13.0: `Message` `onLongPress`, `actions`, `highlighted`; `MessageContent` `quote`; the `reply` kind.) A user message is the web primary bubble (radius 16, bottom-right 14, `px-4 py-3`) at the end of the row; an assistant message has no fill. Message text is the web `text-sm` (14 / 20) from 0.14.0. `MessageAction` is a ghost icon button at the web `icon-sm` size (32, slop to 44), or the web `icon-xs` (28, 14 icon) with `size="xs"` (0.14.0). `kind` gives the icon and label: "Copy", "Retry", "Good response", "Bad response". `selected` marks chosen feedback. |
518
539
  | `Reasoning`, `ReasoningTrigger`, `ReasoningContent`, `useReasoning` | Opens when streaming starts, times the stream and closes once, a second after it stops. The trigger shows a brain, the shimmering "Thinking…", then "Thought for 12s", and a chevron. The content is muted monospace text on the web `bg-surface-secondary/40` panel. Unlike the web, a reader can close it while it streams. |
519
540
  | `AgentTrace`, `AgentTraceLine`, `AgentTraceIcon`, `AgentTraceTitle`, `AgentTraceDescription`, `AgentTraceCollapsible*` | One step per line, with an optional fold behind a rail. `AgentTraceTitle shimmer` pulses a running step. Spread `useStreamingDisclosure(isStreaming)` on `AgentTraceCollapsible` to open it while a step streams. |
520
541
  | `ActivityStep` | One tool step as a trace line: icon, label, a spinner while `running`, a check when `complete`, a warning on `error`, and a description such as `` `Worked for ${formatElapsed(12)}` ``. |
521
542
  | `ChainOfThought` and its parts | Simplified: header, steps with a rail, search-result chips. |
522
- | `PromptInput`, `PromptInputButton`, `PromptInputSubmit` | The web field surface with a `header` slot (attachments), a field, a `toolbar` slot and the send button. The field grows from one line to 160 high (about 6 lines), then scrolls (auto-grow in both layouts from 0.12.1; send resets the height). Send is primary with text and disabled without. While `status` is `submitted` or `streaming` it is a "Stop" square that calls `onStop`. It sits in a `KeyboardAvoidingView` (padding, on iOS and Android) and pads the safe-area insets: put it last in a full-height column. Under a navigation header, pass the header height as `keyboardVerticalOffset`; above a tab bar, pass `safeBottom={false}`. |
543
+ | `PromptInput`, `PromptInputButton`, `PromptInputSubmit` | The web field surface with a `header` slot (attachments), a field, a `toolbar` slot and the send button. The field grows from one line to 160 high (about 6 lines), then scrolls (auto-grow in both layouts from 0.12.1; send resets the height, and from 0.14.0 so does clearing the field). On web a multiline field is one row to start (0.14.0). Send is primary, and from 0.14.0 it stays primary and fades when there is nothing to send, as the web send button (before, it turned `secondary`). While `status` is `submitted` or `streaming` it is a "Stop" square that calls `onStop`. It sits in a `KeyboardAvoidingView` (padding, on iOS and Android) and pads the safe-area insets: put it last in a full-height column. Under a navigation header, pass the header height as `keyboardVerticalOffset`; above a tab bar, pass `safeBottom={false}`. |
523
544
  | `MessageActionSheet`, `ReplyQuoteChip`, `MessageQuote` (0.13.0) | Quoted replies. Long-press a `Message` with `actions` to open the sheet; Reply first, then Copy. Pass `<ReplyQuoteChip author text onDismiss />` as `PromptInput` `replyTo`; send the reply with a `MessageQuote` in `MessageContent` `quote`. See [Quoted replies](#quoted-replies-0130). |
524
545
  | `ToolApprovalCard`, `ToolDraftCard` (0.13.0) | A pending tool call: name, cut args summary, Deny and Approve (`ToolDraftCard` adds Edit). `onDecide("approve" \| "deny" \| "edit")`; `pending` or `disabled` turns the buttons off. |
525
546
  | `SubagentRunCard`, `SubagentResultCard` (0.13.0) | A subagent run (title, status pill, elapsed, Cancel while `running`) and its result (summary, detail in a fold). |
526
547
  | `MessageBotLabel`, `Message` `bot` (0.13.0) | Which bot sent an assistant message, for group chats: `bot={{ name, avatar }}`, or render `MessageBotLabel` above `MessageContent`. |
527
- | `PromptInput layout="inline"` | One row, as the web `Composer layout="inline"` (Eva's chat pill): `leading` (such as a + button), the field (one line to start, then it grows from 0.12.1), `toolbar`, then send, in a 20-radius field with `p-1.5`. 44 high on one line. |
548
+ | `PromptInput layout="inline"` | One row, as the web `Composer layout="inline"`: `leading` (such as a + button), the field (one line to start, then it grows from 0.12.1), `toolbar`, then send, in a 20-radius field with `p-1.5`. The field is the web `text-sm leading-6` (14 / 24) from 0.14.0. `shape="pill"` (0.14.0) is Eva's web chat composer: 48 high on one line, radius 24, a 14 / 20 field 36 high and a 36 send button; give the + `variant="secondary"`. |
528
549
  | `Suggestions`, `Suggestion` | A horizontal row of round chips (or `orientation="vertical"`). `onPress` gets the suggestion text. |
529
550
  | `Sources`, `SourcesTrigger`, `SourcesContent`, `Source` | A fold of link rows. A row opens its address with `Linking.openURL`, or hands it to `onPress`. |
530
551
  | `FileChip`, `Attachments`, `Attachment` | A monospace file chip (a button with `onOpenFile`), and attachment chips with a media icon or image thumbnail and a labelled remove button. No hover cards and no grid previews. |
531
- | `ChoiceQuestion`, `choiceAnswers` | An agent's blocking multiple-choice question, as Eva's web question dock: question with a header badge and step count, lettered option rows with descriptions (a check on the primary fill when picked), "Other..." with a text field, then Back and Next or Submit. `onSubmit` gets question text to label (multi-select joined with ", "), the AskUserQuestion answer shape. `framed` (default) puts it in the dock card. |
532
- | `AgentTraceFold` | A finished turn folded behind "Worked for 1m 5s" over a hairline, as the web `ActivityTasks` folded mode; the steps show in an `AgentTrace` when open. |
533
- | `MessageMeta` | The muted clock after a message's actions ("10:21 PM · 1m"), caption size, tabular figures. |
552
+ | `ChoiceQuestion`, `choiceAnswers` | An agent's blocking multiple-choice question, as Eva's web question dock: question with a header badge and step count, lettered option rows with descriptions (a check on the primary fill when picked), "Other..." with a text field, then Back and Next or Submit. `onSubmit` gets question text to label (multi-select joined with ", "), the AskUserQuestion answer shape. `framed` (default) puts it in the dock card. From 0.14.0 the type follows the web card (12 / 19.5 descriptions, a 10 / 14 semibold topic badge), option rows have no border width and show a 1-wide ring outside when picked, and `buttonWeight="semibold"` draws semibold Back, Next and Submit labels, as Eva. |
553
+ | `AgentTraceFold` | A finished turn folded behind "Worked for 1m 5s" over a hairline, as the web `ActivityTasks` folded mode; the steps show in an `AgentTrace` when open. Pass `seconds`, or `duration` (0.14.0), a string from the app's own formatter, as the web `duration`. |
554
+ | `MessageMeta` | The muted clock after a message's actions ("10:21 PM · 1m"), caption size, tabular figures. `size="2xs"` (0.14.0) is the web `text-2xs` (11 / 16), as Eva; `META_TEXT_STYLE` is the same token as a style. |
534
555
  | `ActivityStepDetail` | A tool step's body, as the web: exit code and "Truncated" badges, the shell transcript in a `CodeBlock`, red/green edit blocks, a write preview, and "Loading output..." while trimmed detail loads. |
535
- | `CodeBlock`, `CodeBlockCopyButton` | Monospace code on the flat panel, scrolling sideways, with the language and a copy button in a header. With the optional peer `prism-react-renderer` and a `language`, tokens take the GitHub light or dark colours of the web shiki block; `highlight={false}` turns that off. Without the peer, plain text. |
556
+ | `CodeBlock`, `CodeBlockCopyButton` | Monospace code on the flat panel, scrolling sideways, with the language and a copy button in a header: the web `CodeBlock`. `variant="markdown"` (0.14.0) is the web `MarkdownCodeBlock` that `Markdown` draws: 13 / 20.15 code, a 32 header, `icon-xs` actions and a "Wrap long lines" toggle. With the optional peer `prism-react-renderer` and a `language`, tokens take the GitHub light or dark colours of the web shiki block; `highlight={false}` turns that off. Without the peer, plain text. |
536
557
  | `ResponseText` | Plain, selectable reply text with newlines kept. It takes the colour of the `MessageContent` it is in. |
537
558
  | `Shimmer` | "Live" text: the foreground fades to 0.45 and back on the UI thread. With Reduce Motion it holds still, muted. |
538
559
 
539
- **Markdown.** `ResponseText` draws text as it is. For markdown, install the optional peer `react-native-marked` and use `Markdown` from `@vvedantb/native-ui/markdown` in place of `ResponseText`: `<Markdown onCopyCode={copy}>{reply}</Markdown>`. It follows the web `markdown.css`: compact headings (1.43, 1.29, 1.14 em), primary links underlined at 30%, inline code on a 6% foreground wash, and fenced code as `CodeBlock`. Text takes the `MessageContent` colour. It renders through `useMarkdown`, so it nests in `ConversationContent` without a `FlatList`. Fences with a language are highlighted when `prism-react-renderer` is installed. Other markdown packages (`react-native-markdown-display`, `@ronradtke/react-native-markdown-display`) still work in place of `ResponseText`.
560
+ **Markdown.** `ResponseText` draws text as it is. For markdown, install the optional peer `react-native-marked` and use `Markdown` from `@vvedantb/native-ui/markdown` in place of `ResponseText`: `<Markdown onCopyCode={copy}>{reply}</Markdown>`. It follows the web `markdown.css`: compact headings (1.43, 1.29, 1.14 em), primary links underlined at 30%, inline code on a 6% foreground wash, and fenced code as `CodeBlock`. From 0.14.0 it also takes the 1.625 body line height (14 / 22.75), 16 between blocks as the Streamdown root the web `Markdown` renders, no rule under headings, medium links, muted quotes, tables on the code panel (0.93 em, 1.5 line height, semibold header) and the `MarkdownCodeBlock` look for fences. `leading="normal"` keeps the text size's own line height (14 / 20), as a web `Markdown` with `text-sm` on it. Text takes the `MessageContent` colour. It renders through `useMarkdown`, so it nests in `ConversationContent` without a `FlatList`. Fences with a language are highlighted when `prism-react-renderer` is installed. Other markdown packages (`react-native-markdown-display`, `@ronradtke/react-native-markdown-display`) still work in place of `ResponseText`.
540
561
 
541
- **Eva's tinted bubble.** `MessageContent bubble="tint"` draws a user message as Eva's web chat does (`rounded-surface bg-primary/10 px-3 py-2 text-foreground`): a 10% wash of the primary colour with foreground text, radius 16 on every corner. The default, `"primary"`, stays the web `MessageContent` bubble.
562
+ **Eva's tinted bubble.** `MessageContent bubble="tint"` draws a user message as Eva's web chat does (`rounded-surface bg-primary/10 text-foreground`): a 10% wash of the primary colour with foreground text, radius 16 on every corner. From 0.14.0 it is `px-4 py-3`, as every web user bubble (the web `group-[.is-user]` padding wins over a `px-3 py-2` class). The default, `"primary"`, stays the web `MessageContent` bubble.
542
563
 
543
564
  **Clipboard.** React Native's own `Clipboard` is deprecated (moved out of core), and the package adds no clipboard package. `MessageAction kind="copy"` calls your `onPress`, and `CodeBlock` shows its copy button when given `onCopy(code)`. Pass your own clipboard call, for example `expo-clipboard`'s `setStringAsync`.
544
565
 
@@ -641,7 +662,7 @@ The bar is the glass `FloatingSurface` at radius 16 with `px-2.5 py-2`. It holds
641
662
 
642
663
  ## Phones and accessibility
643
664
 
644
- The components keep the web sizes on screen and reach a 44 pt touch target with `hitSlop` (`touchSlop` in `src/controlStyles.ts`): `Checkbox` (16), `Switch` (36 × 20), tab triggers (30 high), the `Select` trigger (40 high), the `SearchInput` clear button (24), the `PasswordInput` show/hide button (24), the `UpgradeToProCard` pill (32 high, slop 8) and `Button` (the slop brings each size to at least 48). From 0.5.0 so do `ColorSwatches` swatches and `IconButton` (32 and 40); the `DateTimeField` clear button and the pressable `PageIndicator` dots are 44 squares. Controls set their role and state for VoiceOver and TalkBack: `Button` (`disabled`, `busy` while loading), `Checkbox` (`checked`, `mixed`), `Switch` (`checked`), `Tabs` and `PillTabs` (`tablist`, `tab`, `selected`) and the `Select` trigger (`combobox`, with an `accessibilityLabel` prop). Give a `Button` with no text child an `accessibilityLabel`.
665
+ The components keep the web sizes on screen and reach a 44 pt touch target with `hitSlop` (`touchSlop` in `src/controlStyles.ts`): `Checkbox` (16), `Switch` (36 × 20), tab triggers (36 high, slop 4; unreleased, was 30), the `Select` trigger (40 high), the `SearchInput` clear button (24), the `PasswordInput` show/hide button (24), the `UpgradeToProCard` pill (32 high, slop 8) and `Button` (the slop brings each size to at least 44; unreleased: `small` slops only above and below, so buttons side by side keep separate targets). From 0.5.0 so do `ColorSwatches` swatches and `IconButton` (32 and 40); the `DateTimeField` clear button and the pressable `PageIndicator` dots are 44 squares. Controls set their role and state for VoiceOver and TalkBack: `Button` (`disabled`, `busy` while loading), `Checkbox` (`checked`, `mixed`), `Switch` (`checked`), `Tabs` and `PillTabs` (`tablist`, `tab`, `selected`) and the `Select` trigger (`combobox`, with an `accessibilityLabel` prop). Give a `Button` with no text child an `accessibilityLabel`.
645
666
 
646
667
  Text scales with the system font size. Labelled controls have minimum heights, so a large Dynamic Type size grows them instead of clipping the label (the `SearchInput` lock grows with the font scale); only the `FloatingTabBar` labels are capped (1.5×), to fit the fixed bar. With Reduce Motion on, dialogs, sheets, toasts, the `Select` popover, `PricingModal`, `Progress`, `Skeleton` and the `FloatingTabBar` pill do not animate. Dialogs, sheets, toasts, `FloatingTabBar` and `FloatingFAB` stay inside the safe-area insets, and dialog and sheet fields stay above the keyboard on iOS and Android.
647
668
 
@@ -688,7 +709,7 @@ The JS tokens (colours, radii, spacing) live in `src/tokens.ts`. It is a copy of
688
709
 
689
710
  The brand files `src/brand/svg.ts`, `familyApps.ts` and `vProAppBenefits.ts` are byte-identical copies of `packages/ui/src/brand/`. A test fails when they differ.
690
711
 
691
- `fontClass` lists the Instrument Sans classes. `AppWordmark` uses `font-instrument-sans-medium` (weight 500) with -0.02em letter spacing, as on the web.
712
+ `fontClass` lists the font classes (Instrument Sans unless the app sets [App fonts](#app-fonts)). `AppWordmark` uses `font-instrument-sans-medium` (weight 500) with -0.02em letter spacing, as on the web.
692
713
 
693
714
  ## Type checking
694
715
 
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "@vvedantb/native-ui",
3
- "version": "0.13.0",
3
+ "version": "0.17.0",
4
4
  "license": "MIT",
5
5
  "description": "Shared React Native / Expo UI primitives. Floating surfaces, forms, overlays, toasts, settings chrome, tab bar, AI chat parts, the family app marks, a NativeWind preset, the Expo web root and Clerk + Convex auth screens.",
6
6
  "type": "module",
7
7
  "publishConfig": {
8
- "access": "public"
8
+ "access": "public",
9
+ "provenance": true
9
10
  },
10
11
  "repository": {
11
12
  "type": "git",
12
- "url": "git+https://github.com/vvedantb/ui.git",
13
+ "url": "git+https://github.com/vvedantb/packages.git",
13
14
  "directory": "packages/native-ui"
14
15
  },
15
16
  "exports": {
@@ -73,6 +74,22 @@
73
74
  "types": "./src/entries/bottom-sheet.ts",
74
75
  "default": "./src/entries/bottom-sheet.ts"
75
76
  },
77
+ "./gesture-handler": {
78
+ "types": "./src/entries/gesture-handler.ts",
79
+ "default": "./src/entries/gesture-handler.ts"
80
+ },
81
+ "./keyboard-controller": {
82
+ "types": "./src/entries/keyboard-controller.ts",
83
+ "default": "./src/entries/keyboard-controller.ts"
84
+ },
85
+ "./video": {
86
+ "types": "./src/entries/video.ts",
87
+ "default": "./src/entries/video.ts"
88
+ },
89
+ "./webview": {
90
+ "types": "./src/entries/webview.ts",
91
+ "default": "./src/entries/webview.ts"
92
+ },
76
93
  "./date-time": {
77
94
  "types": "./src/entries/date-time.ts",
78
95
  "default": "./src/entries/date-time.ts"
@@ -151,17 +168,21 @@
151
168
  "expo-linear-gradient": "*",
152
169
  "expo-notifications": "*",
153
170
  "expo-router": "*",
171
+ "expo-video": "*",
154
172
  "expo-web-browser": "*",
155
173
  "lucide-react-native": "*",
156
174
  "nativewind": ">=4.0.0",
157
175
  "prism-react-renderer": ">=2.0.0",
158
176
  "react": ">=19.0.0",
159
177
  "react-native": ">=0.76.0",
178
+ "react-native-gesture-handler": ">=2.0.0",
179
+ "react-native-keyboard-controller": ">=1.21.0",
160
180
  "react-native-marked": ">=8.0.0",
161
181
  "react-native-reanimated": "*",
162
182
  "react-native-safe-area-context": "*",
163
183
  "react-native-screens": "*",
164
184
  "react-native-svg": "*",
185
+ "react-native-webview": "*",
165
186
  "tailwind-merge": ">=2.0.0"
166
187
  },
167
188
  "peerDependenciesMeta": {
@@ -204,17 +225,29 @@
204
225
  "expo-router": {
205
226
  "optional": true
206
227
  },
228
+ "expo-video": {
229
+ "optional": true
230
+ },
207
231
  "expo-web-browser": {
208
232
  "optional": true
209
233
  },
210
234
  "prism-react-renderer": {
211
235
  "optional": true
212
236
  },
237
+ "react-native-gesture-handler": {
238
+ "optional": true
239
+ },
240
+ "react-native-keyboard-controller": {
241
+ "optional": true
242
+ },
213
243
  "react-native-marked": {
214
244
  "optional": true
215
245
  },
216
246
  "react-native-svg": {
217
247
  "optional": true
248
+ },
249
+ "react-native-webview": {
250
+ "optional": true
218
251
  }
219
252
  },
220
253
  "devDependencies": {
@@ -5,13 +5,13 @@ import {
5
5
  } from "@gorhom/bottom-sheet";
6
6
  import { View, Text, TouchableOpacity } from "react-native";
7
7
  import { useRef, useEffect, useCallback, JSX } from "react";
8
- import { useColorScheme } from "nativewind";
9
8
  import { Colors } from "./colors";
10
9
  import { textStyle, typeStyle } from "./controlStyles";
11
10
  import { IconX } from "@tabler/icons-react-native";
12
11
  import { BottomSheetDefaultBackdropProps } from "@gorhom/bottom-sheet/lib/typescript/components/bottomSheetBackdrop/types";
13
12
  import { GlassSheetBackground } from "./LiquidGlass";
14
13
  import { useLargerText } from "./theme";
14
+ import { useScheme } from "./useScheme";
15
15
 
16
16
  export function BottomSheetContainer({
17
17
  visible,
@@ -28,7 +28,7 @@ export function BottomSheetContainer({
28
28
  }) {
29
29
  const bottomSheetModalRef = useRef<BottomSheetModal>(null);
30
30
  const largerText = useLargerText();
31
- const { colorScheme } = useColorScheme();
31
+ const colorScheme = useScheme();
32
32
 
33
33
  useEffect(() => {
34
34
  if (visible) {