aix 0.3.1 → 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.
package/README.md CHANGED
@@ -1,32 +1,12 @@
1
- <img src="https://github.com/vercel/aix/blob/main/Aix.png?raw=true"
2
- alt="aix" width="1600" height="900" />
1
+ # aix
3
2
 
4
- # AIX
3
+ Primitives for building beautiful AI chat apps with React Native.
5
4
 
6
- UI Primitives for building AI apps in React Native.
5
+ > aix is currently in alpha preview. The API will change, and it is not yet feature complete.
7
6
 
8
- ## Features
7
+ We're rewriting the engine that powers the chat experience in the v0 mobile app with a focus on native feel.
9
8
 
10
- - Start a chat scrolled to end on the first frame
11
- - Animate scrolling to new messages when they send
12
- - Float messages to the top of the screen with automated "blank size" handling
13
- - Animate message content as it streams
14
- - Keyboard handling out-of-the-box with no external dependencies
15
- - Support for absolute-positioned composers
16
- - Detect "is scrolled near end" for ScrollToEnd buttons
17
-
18
- To learn about the motivation behind AIX, you can read our blog post on
19
- [How we built the v0 iOS app](https://vercel.com/blog/how-we-built-the-v0-ios-app).
20
- AIX is an opinionated, feature-complete, and extensible way to implement every
21
- single feature mentioned in that blog post.
22
-
23
- When building AIX, we started by copying the code from v0 into a separate
24
- repository. However, as we worked to make it flexible for use cases outside of
25
- our own app, we decided to rewrite it from scratch in native code. What you see
26
- here is a Nitro Module which handles all business logic in UIKit. We plan on
27
- adding support for Android as well and welcome contributions.
28
-
29
- > aix is currently in alpha preview. The API may change.
9
+ aix is a native module with UIKit with Nitro Modules.
30
10
 
31
11
  ## Installation
32
12
 
@@ -34,195 +14,133 @@ adding support for Android as well and welcome contributions.
34
14
  npm i aix react-native-nitro-modules
35
15
  ```
36
16
 
37
- Next, rebuild your native app. For Expo users, run `npx expo prebuild` and
38
- rebuild.
39
-
40
- - For a full example, see the [example app](./react-native-aix/example/App.tsx).
17
+ Next, rebuild your native app. For Expo users, run `npx expo prebuild` and rebuild.
41
18
 
42
19
  ## Usage
43
20
 
44
21
  Wrap your `ScrollView` with `Aix`, and wrap your messages with `AixCell`.
45
22
 
23
+ <details>
24
+ <summary>Click here to view a full example</summary>
25
+ </details>
26
+
46
27
  ```tsx
47
- import { Aix, AixCell } from 'aix'
48
- import { Message } from 'path/to/your/message'
49
- import { Composer } from 'path/to/your/composer'
28
+ import { Aix, AixCell } from 'aix';
29
+ import { Message } from 'path/to/your/message';
30
+ import { Composer } from 'path/to/your/composer';
50
31
 
51
32
  export function ChatScreen({ messages }) {
52
33
  return (
53
- <Aix style={{ flex: 1 }} shouldStartAtEnd>
34
+ <Aix style={{ flex: 1 }}>
54
35
  <ScrollView>
55
36
  {messages.map((message) => (
56
- <AixCell key={message.id} index={index} isLast={index === messages.length - 1}>
37
+ <AixCell
38
+ key={message.id}
39
+ index={index}
40
+ isLast={index === messages.length - 1}
41
+ >
57
42
  <Message message={message} />
58
43
  </AixCell>
59
44
  ))}
60
45
  </ScrollView>
61
46
  </Aix>
62
- )
47
+ );
63
48
  }
64
49
  ```
65
50
 
66
- ### Add a floating composer
67
-
68
- To add a floating composer which lets content scroll under it, you can use the
69
- `AixFooter`. Pair it with `Aix.scrollOnFooterSizeUpdate` to ensure content
70
- scrolls under the footer when it changes size.
51
+ To add a floating composer which lets content scroll under it, you can use the `AixFooter` and `KeyboardStickyView` from `react-native-keyboard-controller`:
71
52
 
72
53
  ```tsx
73
- import { Aix, AixCell, AixFooter } from 'aix'
54
+ import { Aix, AixCell, AixFooter } from 'aix';
55
+ import { KeyboardStickyView } from 'react-native-keyboard-controller';
74
56
 
75
57
  export function ChatScreen({ messages }) {
76
- const { bottom } = useSafeAreaInsets()
77
-
78
58
  return (
79
- <Aix
80
- style={{ flex: 1 }}
81
- shouldStartAtEnd
82
- scrollOnFooterSizeUpdate={{
83
- enabled: true,
84
- scrolledToEndThreshold: 100,
85
- animated: false,
86
- }}
87
- >
59
+ <Aix style={{ flex: 1 }}>
88
60
  <ScrollView>
89
61
  {messages.map((message) => (
90
- <AixCell key={message.id} index={index} isLast={index === messages.length - 1}>
62
+ <AixCell
63
+ key={message.id}
64
+ index={index}
65
+ isLast={index === messages.length - 1}
66
+ >
91
67
  <Message message={message} />
92
68
  </AixCell>
93
69
  ))}
94
70
  </ScrollView>
95
71
 
96
- <AixFooter
97
- style={{ position: 'absolute', inset: 0, top: 'auto' }}
98
- stickToKeyboard={{
99
- enabled: true,
100
- offset: {
101
- whenKeyboardOpen: 0,
102
- whenKeyboardClosed: -bottom,
103
- },
104
- }}
105
- >
106
- <Composer />
107
- </AixFooter>
72
+ <KeyboardStickyView offset={{ opened: 0, closed: -bottomInsetPadding }}>
73
+ <AixFooter style={{ position: 'absolute', inset: 0, top: 'auto'}}>
74
+ <Composer />
75
+ </AixFooter>
76
+ </KeyboardStickyView>
108
77
  </Aix>
109
- )
110
- }
111
- ```
112
-
113
- ### Send a message
114
-
115
- When sending a message, you will likely want to scroll to it after it gets added
116
- to the list.
117
-
118
- Simply call `aix.current?.scrollToIndexWhenBlankSizeReady(index)` in your submit
119
- handler.
120
-
121
- The `index` you pass should correspond to the newest message in the list. For AI
122
- chats, this is typically the next assistant message index.
123
-
124
- ```tsx
125
- import { Keyboard } from 'react-native'
126
- import { useAixRef } from 'aix'
127
-
128
- function Chat() {
129
- const aix = useAixRef()
130
-
131
- const onSubmit = () => {
132
- aix.current?.scrollToIndexWhenBlankSizeReady(messages.length + 1, true)
133
- requestAnimationFrame(Keyboard.dismiss)
134
- }
135
-
136
- return <Aix ref={aix}>{/* ... */}</Aix>
78
+ );
137
79
  }
138
80
  ```
139
81
 
140
- ### Add a scroll to end button
141
-
142
- You can use `onScrolledNearEndChange` to show a "scroll to end" button when the
143
- user scrolls away from the bottom:
144
-
145
- ```tsx
146
- import { Aix, useAixRef } from 'aix'
147
- import { useState } from 'react'
148
- import { Button } from 'react-native'
149
-
150
- function Chat() {
151
- const aix = useAixRef()
152
- const [isNearEnd, setIsNearEnd] = useState(false)
153
-
154
- return (
155
- <Aix ref={aix} scrollEndReachedThreshold={200} onScrolledNearEndChange={setIsNearEnd}>
156
- {/* ScrollView and messages... */}
82
+ ## TODOs
157
83
 
158
- {!isNearEnd && (
159
- <Button onPress={() => aix.current?.scrollToEnd(true)} title='Scroll to end' />
160
- )}
161
- </Aix>
162
- )
163
- }
164
- ```
84
+ - [ ] Android support
85
+ - [ ] LegendList support
86
+ - [ ] FlashList support
165
87
 
166
88
  ## API Reference
167
89
 
168
90
  ### `Aix`
169
91
 
170
- The main container component that provides keyboard-aware behavior and manages
171
- scrolling for chat interfaces.
92
+ The main container component that provides keyboard-aware behavior and manages scrolling for chat interfaces.
172
93
 
173
94
  #### Props
174
95
 
175
- | Prop | Type | Default | Description |
176
- | --------------------------------- | ------------------------------ | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
177
- | `shouldStartAtEnd` | `boolean` | - | Whether the scroll view should start scrolled to the end of the content. |
178
- | `scrollOnFooterSizeUpdate` | `object` | `{ enabled: true, scrolledToEndThreshold: 100, animated: false }` | Control the behavior of scrolling when the footer size changes. By default, changing the height of the footer will shift content up in the scroll view. |
179
- | `scrollEndReachedThreshold` | `number` | `max(blankSize, 200)` | The number of pixels from the bottom of the scroll view to the end of the content that is considered "near the end". Used by `onScrolledNearEndChange` and to determine if content should shift up when keyboard opens. |
180
- | `onScrolledNearEndChange` | `(isNearEnd: boolean) => void` | - | Callback fired when the scroll position transitions between "near end" and "not near end" states. Reactive to user scrolling, content size changes, parent size changes, and keyboard height changes. Uses `scrollEndReachedThreshold` to determine the threshold. |
181
- | `additionalContentInsets` | `object` | - | Additional content insets applied when keyboard is open or closed. Shape: `{ top?: { whenKeyboardOpen, whenKeyboardClosed }, bottom?: { whenKeyboardOpen, whenKeyboardClosed } }` |
182
- | `additionalScrollIndicatorInsets` | `object` | - | Additional insets for the scroll indicator, added to existing safe area insets. Applied to `verticalScrollIndicatorInsets` on iOS. |
183
- | `mainScrollViewID` | `string` | - | The `nativeID` of the scroll view to use. If provided, will search for a scroll view with this `accessibilityIdentifier`. |
184
- | `penultimateCellIndex` | `number` | - | The index of the second-to-last message (typically the last user message in AI chat apps). Used to determine which message will be scrolled into view. Useful when you have custom message types like timestamps in your list. |
96
+ | Prop | Type | Default | Description |
97
+ |------|------|---------|-------------|
98
+ | `shouldStartAtEnd` | `boolean` | - | Whether the scroll view should start scrolled to the end of the content. |
99
+ | `scrollOnFooterSizeUpdate` | `object` | `{ enabled: true, scrolledToEndThreshold: 100, animated: false }` | Control the behavior of scrolling when the footer size changes. By default, changing the height of the footer will shift content up in the scroll view. |
100
+ | `scrollEndReachedThreshold` | `number` | `max(blankSize, 200)` | The number of pixels from the bottom of the scroll view to the end of the content that is considered "near the end". Used by `onScrolledNearEndChange` and to determine if content should shift up when keyboard opens. |
101
+ | `onScrolledNearEndChange` | `(isNearEnd: boolean) => void` | - | Callback fired when the scroll position transitions between "near end" and "not near end" states. Reactive to user scrolling, content size changes, parent size changes, and keyboard height changes. Uses `scrollEndReachedThreshold` to determine the threshold. |
102
+ | `additionalContentInsets` | `object` | - | Additional content insets applied when keyboard is open or closed. Shape: `{ top?: { whenKeyboardOpen, whenKeyboardClosed }, bottom?: { whenKeyboardOpen, whenKeyboardClosed } }` |
103
+ | `additionalScrollIndicatorInsets` | `object` | - | Additional insets for the scroll indicator, added to existing safe area insets. Applied to `verticalScrollIndicatorInsets` on iOS. |
104
+ | `mainScrollViewID` | `string` | - | The `nativeID` of the scroll view to use. If provided, will search for a scroll view with this `accessibilityIdentifier`. |
105
+ | `penultimateCellIndex` | `number` | - | The index of the second-to-last message (typically the last user message in AI chat apps). Used to determine which message will be scrolled into view. Useful when you have custom message types like timestamps in your list. |
185
106
 
186
107
  #### Ref Methods
187
108
 
188
109
  Access these methods via `useAixRef()`:
189
110
 
190
111
  ```tsx
191
- const aix = useAixRef()
112
+ const aix = useAixRef();
192
113
 
193
114
  // Scroll to the end of the content
194
- aix.current?.scrollToEnd(animated)
115
+ aix.current?.scrollToEnd(animated);
195
116
 
196
117
  // Scroll to a specific index when the blank size is ready
197
- aix.current?.scrollToIndexWhenBlankSizeReady(index, animated, waitForKeyboardToEnd)
118
+ aix.current?.scrollToIndexWhenBlankSizeReady(index, animated, waitForKeyboardToEnd);
198
119
  ```
199
120
 
200
- | Method | Parameters | Description |
201
- | --------------------------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------- |
202
- | `scrollToEnd` | `animated?: boolean` | Scrolls to the end of the content. |
121
+ | Method | Parameters | Description |
122
+ |--------|------------|-------------|
123
+ | `scrollToEnd` | `animated?: boolean` | Scrolls to the end of the content. |
203
124
  | `scrollToIndexWhenBlankSizeReady` | `index: number, animated?: boolean, waitForKeyboardToEnd?: boolean` | Scrolls to a specific cell index once the blank size calculation is ready. |
204
125
 
205
126
  ---
206
127
 
207
128
  ### `AixCell`
208
129
 
209
- A wrapper component for each message in the list. It communicates cell position
210
- and dimensions to the parent `Aix` component.
130
+ A wrapper component for each message in the list. It communicates cell position and dimensions to the parent `Aix` component.
211
131
 
212
132
  #### Props
213
133
 
214
- | Prop | Type | Required | Description |
215
- | -------- | --------- | -------- | ------------------------------------------------------------------------------------------- |
216
- | `index` | `number` | Yes | The index of this cell in the message list. |
217
- | `isLast` | `boolean` | Yes | Whether this cell is the last item in the list. Used for scroll positioning and animations. |
134
+ | Prop | Type | Required | Description |
135
+ |------|------|----------|-------------|
136
+ | `index` | `number` | Yes | The index of this cell in the message list. |
137
+ | `isLast` | `boolean` | Yes | Whether this cell is the last item in the list. Used for scroll positioning and animations. |
218
138
 
219
139
  ---
220
140
 
221
141
  ### `AixFooter`
222
142
 
223
- A footer component for floating composers that allows content to scroll
224
- underneath it. The footer's height is automatically tracked for proper scroll
225
- offset calculations.
143
+ A footer component for floating composers that allows content to scroll underneath it. The footer's height is automatically tracked for proper scroll offset calculations.
226
144
 
227
145
  #### Props
228
146
 
@@ -230,8 +148,7 @@ Accepts all standard React Native `View` props.
230
148
 
231
149
  #### Important Notes
232
150
 
233
- - **Do not apply vertical padding** (`padding`, `paddingBottom`) directly to
234
- `AixFooter`. Apply padding to a child view instead.
151
+ - **Do not apply vertical padding** (`padding`, `paddingBottom`) directly to `AixFooter`. Apply padding to a child view instead.
235
152
  - Position the footer absolutely at the bottom of the `Aix` container:
236
153
 
237
154
  ```tsx
@@ -247,32 +164,57 @@ Accepts all standard React Native `View` props.
247
164
  A hook that returns a ref to access imperative methods on the `Aix` component.
248
165
 
249
166
  ```tsx
250
- import { useAixRef } from 'aix'
167
+ import { useAixRef } from 'aix';
251
168
 
252
169
  function Chat({ messages }) {
253
- const aix = useAixRef()
170
+ const aix = useAixRef();
254
171
  const send = useSendMessage()
255
-
172
+
256
173
  const handleSend = () => {
257
174
  // Scroll to end after sending a message
258
- send(message)
259
- aix.current?.scrollToIndexWhenBlankSizeReady(messages.length + 1, true)
260
- requestAnimationFrame(Keyboard.dismiss)
261
- }
262
-
263
- return <Aix ref={aix}>{/* ... */}</Aix>
175
+ send(message);
176
+ aix.current?.scrollToIndexWhenBlankSizeReady(messages.length + 1, true);
177
+ requestAnimationFrame(Keyboard.dismiss);
178
+ };
179
+
180
+ return <Aix ref={aix}>{/* ... */}</Aix>;
264
181
  }
265
182
  ```
266
183
 
267
184
  ---
268
185
 
186
+ ### Scroll to End Button
187
+
188
+ You can use `onScrolledNearEndChange` to show a "scroll to end" button when the user scrolls away from the bottom:
189
+
190
+ ```tsx
191
+ import { Aix, useAixRef } from 'aix';
192
+ import { useState } from 'react';
193
+
194
+ function Chat() {
195
+ const aix = useAixRef();
196
+ const [isNearEnd, setIsNearEnd] = useState(false);
197
+
198
+ return (
199
+ <Aix
200
+ ref={aix}
201
+ scrollEndReachedThreshold={200}
202
+ onScrolledNearEndChange={setIsNearEnd}
203
+ >
204
+ {/* ScrollView and messages... */}
205
+
206
+ {!isNearEnd && (
207
+ <Button onPress={() => aix.current?.scrollToEnd(true)} />
208
+ )}
209
+ </Aix>
210
+ );
211
+ }
212
+ ```
213
+
269
214
  ## TODOs
270
215
 
271
- - [ ] Android support
272
- - [ ] LegendList support
273
- - [ ] FlashList support
274
216
 
275
217
  ## Requirements
276
218
 
277
219
  - React Native v0.78.0 or higher
278
- - Node 18.0.0 or higher
220
+ - Node 18.0.0 or higher
@@ -5,7 +5,7 @@ import android.view.View
5
5
  import androidx.annotation.Keep
6
6
  import com.facebook.proguard.annotations.DoNotStrip
7
7
  import com.facebook.react.uimanager.ThemedReactContext
8
- import com.margelo.nitro.aix.HybridAixSpec
8
+ import com.margelo.nitro.aix.*
9
9
 
10
10
  @Keep
11
11
  @DoNotStrip
@@ -14,14 +14,24 @@ class HybridAix(val context: ThemedReactContext): HybridAixSpec() {
14
14
  override val view: View = View(context)
15
15
 
16
16
  // Props
17
- private var _isRed = false
18
- override var isRed: Boolean
19
- get() = _isRed
20
- set(value) {
21
- _isRed = value
22
- view.setBackgroundColor(
23
- if (value) Color.RED
24
- else Color.BLACK
25
- )
26
- }
17
+ override var shouldStartAtEnd: Boolean = true
18
+ override var scrollOnFooterSizeUpdate: AixScrollOnFooterSizeUpdate? = null
19
+ override var scrollEndReachedThreshold: Double? = null
20
+ override var additionalContentInsets: AixAdditionalContentInsetsProp? = null
21
+ override var additionalScrollIndicatorInsets: AixScrollIndicatorInsets? = null
22
+ override var mainScrollViewID: String? = null
23
+ override var penultimateCellIndex: Double? = null
24
+ override var shouldApplyContentInsets: Boolean? = null
25
+ override var applyContentInsetDelay: Double? = null
26
+ override var onWillApplyContentInsets: ((insets: AixContentInsets) -> Unit)? = null
27
+ override var onScrolledNearEndChange: ((isNearEnd: Boolean) -> Unit)? = null
28
+
29
+ // Methods
30
+ override fun scrollToEnd(animated: Boolean?) {
31
+ // TODO: Implement for Android
32
+ }
33
+
34
+ override fun scrollToIndexWhenBlankSizeReady(index: Double, animated: Boolean?, waitForKeyboardToEnd: Boolean?) {
35
+ // TODO: Implement for Android
36
+ }
27
37
  }
@@ -100,6 +100,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
100
100
  var penultimateCellIndex: Double?
101
101
 
102
102
  var shouldApplyContentInsets: Bool? = nil
103
+ var applyContentInsetDelay: Double? = nil
103
104
  var onWillApplyContentInsets: ((_ insets: AixContentInsets) -> Void)? = nil
104
105
  var onScrolledNearEndChange: ((_ isNearEnd: Bool) -> Void)? = nil
105
106
 
@@ -448,23 +449,32 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
448
449
  )
449
450
 
450
451
  print("[aix] applyContentInset \(targetBottom)")
452
+ onWillApplyContentInsets?(insets)
451
453
 
452
454
  // If shouldApplyContentInsets is explicitly false, call callback and return
453
455
  if shouldApplyContentInsets == false {
454
- onWillApplyContentInsets?(insets)
455
456
  return
456
457
  }
457
458
 
458
- // Default behavior: apply insets directly
459
- if scrollView.contentInset.top != targetTop {
460
- scrollView.contentInset.top = targetTop
461
- }
462
- if scrollView.contentInset.bottom != targetBottom {
463
- scrollView.contentInset.bottom = targetBottom
459
+ // Helper to actually apply the insets
460
+ let applyInsets = { [weak self] in
461
+ guard let self, let scrollView = self.scrollView else { return }
462
+ if scrollView.contentInset.top != targetTop {
463
+ scrollView.contentInset.top = targetTop
464
+ }
465
+ if scrollView.contentInset.bottom != targetBottom {
466
+ scrollView.contentInset.bottom = targetBottom
467
+ }
468
+ // Update scrolled near end state after insets change
469
+ self.updateScrolledNearEndState()
464
470
  }
465
471
 
466
- // Update scrolled near end state after insets change
467
- updateScrolledNearEndState()
472
+ // Apply with optional delay
473
+ if let delay = applyContentInsetDelay, delay > 0 {
474
+ DispatchQueue.main.asyncAfter(deadline: .now() + delay / 1000.0, execute: applyInsets)
475
+ } else {
476
+ applyInsets()
477
+ }
468
478
  }
469
479
 
470
480
  /// Centralized function to check and fire onScrolledNearEndChange callback
@@ -10,6 +10,7 @@ export declare const Aix: import("react").ForwardRefExoticComponent<Omit<{
10
10
  mainScrollViewID?: string | import("react-native-reanimated").SharedValue<string | undefined> | undefined;
11
11
  penultimateCellIndex?: number | import("react-native-reanimated").SharedValue<number | undefined> | undefined;
12
12
  shouldApplyContentInsets?: boolean | import("react-native-reanimated").SharedValue<boolean | undefined> | undefined;
13
+ applyContentInsetDelay?: number | import("react-native-reanimated").SharedValue<number | undefined> | undefined;
13
14
  onWillApplyContentInsets?: import("react-native-nitro-modules").NitroViewWrappedCallback<((insets: AixContentInsets) => void) | undefined> | import("react-native-reanimated").SharedValue<import("react-native-nitro-modules").NitroViewWrappedCallback<((insets: AixContentInsets) => void) | undefined> | undefined> | undefined;
14
15
  onScrolledNearEndChange?: import("react-native-nitro-modules").NitroViewWrappedCallback<((isNearEnd: boolean) => void) | undefined> | import("react-native-reanimated").SharedValue<import("react-native-nitro-modules").NitroViewWrappedCallback<((isNearEnd: boolean) => void) | undefined> | undefined> | undefined;
15
16
  hybridRef?: import("react-native-nitro-modules").NitroViewWrappedCallback<((ref: import("react-native-nitro-modules").HybridView<AixProps, AixMethods>) => void) | undefined> | import("react-native-reanimated").SharedValue<import("react-native-nitro-modules").NitroViewWrappedCallback<((ref: import("react-native-nitro-modules").HybridView<AixProps, AixMethods>) => void) | undefined> | undefined> | undefined;
@@ -127,6 +128,7 @@ export declare const Aix: import("react").ForwardRefExoticComponent<Omit<{
127
128
  mainScrollViewID?: string | import("react-native-reanimated").SharedValue<string | undefined> | undefined;
128
129
  penultimateCellIndex?: number | import("react-native-reanimated").SharedValue<number | undefined> | undefined;
129
130
  shouldApplyContentInsets?: boolean | import("react-native-reanimated").SharedValue<boolean | undefined> | undefined;
131
+ applyContentInsetDelay?: number | import("react-native-reanimated").SharedValue<number | undefined> | undefined;
130
132
  onWillApplyContentInsets?: import("react-native-nitro-modules").NitroViewWrappedCallback<((insets: AixContentInsets) => void) | undefined> | import("react-native-reanimated").SharedValue<import("react-native-nitro-modules").NitroViewWrappedCallback<((insets: AixContentInsets) => void) | undefined> | undefined> | undefined;
131
133
  onScrolledNearEndChange?: import("react-native-nitro-modules").NitroViewWrappedCallback<((isNearEnd: boolean) => void) | undefined> | import("react-native-reanimated").SharedValue<import("react-native-nitro-modules").NitroViewWrappedCallback<((isNearEnd: boolean) => void) | undefined> | undefined> | undefined;
132
134
  hybridRef?: import("react-native-nitro-modules").NitroViewWrappedCallback<((ref: import("react-native-nitro-modules").HybridView<AixProps, AixMethods>) => void) | undefined> | import("react-native-reanimated").SharedValue<import("react-native-nitro-modules").NitroViewWrappedCallback<((ref: import("react-native-nitro-modules").HybridView<AixProps, AixMethods>) => void) | undefined> | undefined> | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"aix.d.ts","sourceRoot":"","sources":["../../../src/aix.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,SAAS,EACf,MAAM,4BAA4B,CAAA;AAEnC,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAI/E,MAAM,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;AAgBpD,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAJa,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI;8BACnC,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI;0CAyCvD,CAAA"}
1
+ {"version":3,"file":"aix.d.ts","sourceRoot":"","sources":["../../../src/aix.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,SAAS,EACf,MAAM,4BAA4B,CAAA;AAEnC,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAI/E,MAAM,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;AAgBpD,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAJa,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI;8BACnC,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI;0CAyCvD,CAAA"}
@@ -104,6 +104,13 @@ export interface AixProps extends HybridViewProps {
104
104
  */
105
105
  penultimateCellIndex?: number;
106
106
  shouldApplyContentInsets?: boolean;
107
+ /**
108
+ * Optional delay in milliseconds before applying content insets to the scroll view.
109
+ *
110
+ * This can help work around race conditions where insets are applied before
111
+ * React commits layout changes.
112
+ */
113
+ applyContentInsetDelay?: number;
107
114
  onWillApplyContentInsets?: (insets: AixContentInsets) => void;
108
115
  /**
109
116
  * Called when the scroll position changes between "near end" and "not near end" states.
@@ -1 +1 @@
1
- {"version":3,"file":"aix.nitro.d.ts","sourceRoot":"","sources":["../../../../src/views/aix.nitro.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"aix.nitro.d.ts","sourceRoot":"","sources":["../../../../src/views/aix.nitro.ts"],"names":[],"mappings":"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"}
@@ -140,6 +140,15 @@ namespace margelo::nitro::aix {
140
140
  static const auto method = javaClassStatic()->getMethod<void(jni::alias_ref<jni::JBoolean> /* shouldApplyContentInsets */)>("setShouldApplyContentInsets");
141
141
  method(_javaPart, shouldApplyContentInsets.has_value() ? jni::JBoolean::valueOf(shouldApplyContentInsets.value()) : nullptr);
142
142
  }
143
+ std::optional<double> JHybridAixSpec::getApplyContentInsetDelay() {
144
+ static const auto method = javaClassStatic()->getMethod<jni::local_ref<jni::JDouble>()>("getApplyContentInsetDelay");
145
+ auto __result = method(_javaPart);
146
+ return __result != nullptr ? std::make_optional(__result->value()) : std::nullopt;
147
+ }
148
+ void JHybridAixSpec::setApplyContentInsetDelay(std::optional<double> applyContentInsetDelay) {
149
+ static const auto method = javaClassStatic()->getMethod<void(jni::alias_ref<jni::JDouble> /* applyContentInsetDelay */)>("setApplyContentInsetDelay");
150
+ method(_javaPart, applyContentInsetDelay.has_value() ? jni::JDouble::valueOf(applyContentInsetDelay.value()) : nullptr);
151
+ }
143
152
  std::optional<std::function<void(const AixContentInsets& /* insets */)>> JHybridAixSpec::getOnWillApplyContentInsets() {
144
153
  static const auto method = javaClassStatic()->getMethod<jni::local_ref<JFunc_void_AixContentInsets::javaobject>()>("getOnWillApplyContentInsets_cxx");
145
154
  auto __result = method(_javaPart);
@@ -66,6 +66,8 @@ namespace margelo::nitro::aix {
66
66
  void setPenultimateCellIndex(std::optional<double> penultimateCellIndex) override;
67
67
  std::optional<bool> getShouldApplyContentInsets() override;
68
68
  void setShouldApplyContentInsets(std::optional<bool> shouldApplyContentInsets) override;
69
+ std::optional<double> getApplyContentInsetDelay() override;
70
+ void setApplyContentInsetDelay(std::optional<double> applyContentInsetDelay) override;
69
71
  std::optional<std::function<void(const AixContentInsets& /* insets */)>> getOnWillApplyContentInsets() override;
70
72
  void setOnWillApplyContentInsets(const std::optional<std::function<void(const AixContentInsets& /* insets */)>>& onWillApplyContentInsets) override;
71
73
  std::optional<std::function<void(bool /* isNearEnd */)>> getOnScrolledNearEndChange() override;
@@ -68,6 +68,10 @@ void JHybridAixStateUpdater::updateViewProps(jni::alias_ref<jni::JClass> /* clas
68
68
  view->setShouldApplyContentInsets(props.shouldApplyContentInsets.value);
69
69
  // TODO: Set isDirty = false
70
70
  }
71
+ if (props.applyContentInsetDelay.isDirty) {
72
+ view->setApplyContentInsetDelay(props.applyContentInsetDelay.value);
73
+ // TODO: Set isDirty = false
74
+ }
71
75
  if (props.onWillApplyContentInsets.isDirty) {
72
76
  view->setOnWillApplyContentInsets(props.onWillApplyContentInsets.value);
73
77
  // TODO: Set isDirty = false
@@ -90,6 +90,12 @@ abstract class HybridAixSpec: HybridView() {
90
90
  @set:Keep
91
91
  abstract var shouldApplyContentInsets: Boolean?
92
92
 
93
+ @get:DoNotStrip
94
+ @get:Keep
95
+ @set:DoNotStrip
96
+ @set:Keep
97
+ abstract var applyContentInsetDelay: Double?
98
+
93
99
  abstract var onWillApplyContentInsets: ((insets: AixContentInsets) -> Unit)?
94
100
 
95
101
  private var onWillApplyContentInsets_cxx: Func_void_AixContentInsets?
@@ -130,6 +130,13 @@ namespace margelo::nitro::aix {
130
130
  inline void setShouldApplyContentInsets(std::optional<bool> shouldApplyContentInsets) noexcept override {
131
131
  _swiftPart.setShouldApplyContentInsets(shouldApplyContentInsets);
132
132
  }
133
+ inline std::optional<double> getApplyContentInsetDelay() noexcept override {
134
+ auto __result = _swiftPart.getApplyContentInsetDelay();
135
+ return __result;
136
+ }
137
+ inline void setApplyContentInsetDelay(std::optional<double> applyContentInsetDelay) noexcept override {
138
+ _swiftPart.setApplyContentInsetDelay(applyContentInsetDelay);
139
+ }
133
140
  inline std::optional<std::function<void(const AixContentInsets& /* insets */)>> getOnWillApplyContentInsets() noexcept override {
134
141
  auto __result = _swiftPart.getOnWillApplyContentInsets();
135
142
  return __result;
@@ -111,6 +111,11 @@ using namespace margelo::nitro::aix::views;
111
111
  swiftPart.setShouldApplyContentInsets(newViewProps.shouldApplyContentInsets.value);
112
112
  newViewProps.shouldApplyContentInsets.isDirty = false;
113
113
  }
114
+ // applyContentInsetDelay: optional
115
+ if (newViewProps.applyContentInsetDelay.isDirty) {
116
+ swiftPart.setApplyContentInsetDelay(newViewProps.applyContentInsetDelay.value);
117
+ newViewProps.applyContentInsetDelay.isDirty = false;
118
+ }
114
119
  // onWillApplyContentInsets: optional
115
120
  if (newViewProps.onWillApplyContentInsets.isDirty) {
116
121
  swiftPart.setOnWillApplyContentInsets(newViewProps.onWillApplyContentInsets.value);
@@ -19,6 +19,7 @@ public protocol HybridAixSpec_protocol: HybridObject, HybridView {
19
19
  var mainScrollViewID: String? { get set }
20
20
  var penultimateCellIndex: Double? { get set }
21
21
  var shouldApplyContentInsets: Bool? { get set }
22
+ var applyContentInsetDelay: Double? { get set }
22
23
  var onWillApplyContentInsets: ((_ insets: AixContentInsets) -> Void)? { get set }
23
24
  var onScrolledNearEndChange: ((_ isNearEnd: Bool) -> Void)? { get set }
24
25
 
@@ -258,6 +258,23 @@ open class HybridAixSpec_cxx {
258
258
  }
259
259
  }
260
260
 
261
+ public final var applyContentInsetDelay: bridge.std__optional_double_ {
262
+ @inline(__always)
263
+ get {
264
+ return { () -> bridge.std__optional_double_ in
265
+ if let __unwrappedValue = self.__implementation.applyContentInsetDelay {
266
+ return bridge.create_std__optional_double_(__unwrappedValue)
267
+ } else {
268
+ return .init()
269
+ }
270
+ }()
271
+ }
272
+ @inline(__always)
273
+ set {
274
+ self.__implementation.applyContentInsetDelay = newValue.value
275
+ }
276
+ }
277
+
261
278
  public final var onWillApplyContentInsets: bridge.std__optional_std__function_void_const_AixContentInsets_____insets______ {
262
279
  @inline(__always)
263
280
  get {
@@ -30,6 +30,8 @@ namespace margelo::nitro::aix {
30
30
  prototype.registerHybridSetter("penultimateCellIndex", &HybridAixSpec::setPenultimateCellIndex);
31
31
  prototype.registerHybridGetter("shouldApplyContentInsets", &HybridAixSpec::getShouldApplyContentInsets);
32
32
  prototype.registerHybridSetter("shouldApplyContentInsets", &HybridAixSpec::setShouldApplyContentInsets);
33
+ prototype.registerHybridGetter("applyContentInsetDelay", &HybridAixSpec::getApplyContentInsetDelay);
34
+ prototype.registerHybridSetter("applyContentInsetDelay", &HybridAixSpec::setApplyContentInsetDelay);
33
35
  prototype.registerHybridGetter("onWillApplyContentInsets", &HybridAixSpec::getOnWillApplyContentInsets);
34
36
  prototype.registerHybridSetter("onWillApplyContentInsets", &HybridAixSpec::setOnWillApplyContentInsets);
35
37
  prototype.registerHybridGetter("onScrolledNearEndChange", &HybridAixSpec::getOnScrolledNearEndChange);
@@ -73,6 +73,8 @@ namespace margelo::nitro::aix {
73
73
  virtual void setPenultimateCellIndex(std::optional<double> penultimateCellIndex) = 0;
74
74
  virtual std::optional<bool> getShouldApplyContentInsets() = 0;
75
75
  virtual void setShouldApplyContentInsets(std::optional<bool> shouldApplyContentInsets) = 0;
76
+ virtual std::optional<double> getApplyContentInsetDelay() = 0;
77
+ virtual void setApplyContentInsetDelay(std::optional<double> applyContentInsetDelay) = 0;
76
78
  virtual std::optional<std::function<void(const AixContentInsets& /* insets */)>> getOnWillApplyContentInsets() = 0;
77
79
  virtual void setOnWillApplyContentInsets(const std::optional<std::function<void(const AixContentInsets& /* insets */)>>& onWillApplyContentInsets) = 0;
78
80
  virtual std::optional<std::function<void(bool /* isNearEnd */)>> getOnScrolledNearEndChange() = 0;
@@ -105,6 +105,16 @@ namespace margelo::nitro::aix::views {
105
105
  throw std::runtime_error(std::string("Aix.shouldApplyContentInsets: ") + exc.what());
106
106
  }
107
107
  }()),
108
+ applyContentInsetDelay([&]() -> CachedProp<std::optional<double>> {
109
+ try {
110
+ const react::RawValue* rawValue = rawProps.at("applyContentInsetDelay", nullptr, nullptr);
111
+ if (rawValue == nullptr) return sourceProps.applyContentInsetDelay;
112
+ const auto& [runtime, value] = (std::pair<jsi::Runtime*, jsi::Value>)*rawValue;
113
+ return CachedProp<std::optional<double>>::fromRawValue(*runtime, value, sourceProps.applyContentInsetDelay);
114
+ } catch (const std::exception& exc) {
115
+ throw std::runtime_error(std::string("Aix.applyContentInsetDelay: ") + exc.what());
116
+ }
117
+ }()),
108
118
  onWillApplyContentInsets([&]() -> CachedProp<std::optional<std::function<void(const AixContentInsets& /* insets */)>>> {
109
119
  try {
110
120
  const react::RawValue* rawValue = rawProps.at("onWillApplyContentInsets", nullptr, nullptr);
@@ -146,6 +156,7 @@ namespace margelo::nitro::aix::views {
146
156
  mainScrollViewID(other.mainScrollViewID),
147
157
  penultimateCellIndex(other.penultimateCellIndex),
148
158
  shouldApplyContentInsets(other.shouldApplyContentInsets),
159
+ applyContentInsetDelay(other.applyContentInsetDelay),
149
160
  onWillApplyContentInsets(other.onWillApplyContentInsets),
150
161
  onScrolledNearEndChange(other.onScrolledNearEndChange),
151
162
  hybridRef(other.hybridRef) { }
@@ -160,6 +171,7 @@ namespace margelo::nitro::aix::views {
160
171
  case hashString("mainScrollViewID"): return true;
161
172
  case hashString("penultimateCellIndex"): return true;
162
173
  case hashString("shouldApplyContentInsets"): return true;
174
+ case hashString("applyContentInsetDelay"): return true;
163
175
  case hashString("onWillApplyContentInsets"): return true;
164
176
  case hashString("onScrolledNearEndChange"): return true;
165
177
  case hashString("hybridRef"): return true;
@@ -55,6 +55,7 @@ namespace margelo::nitro::aix::views {
55
55
  CachedProp<std::optional<std::string>> mainScrollViewID;
56
56
  CachedProp<std::optional<double>> penultimateCellIndex;
57
57
  CachedProp<std::optional<bool>> shouldApplyContentInsets;
58
+ CachedProp<std::optional<double>> applyContentInsetDelay;
58
59
  CachedProp<std::optional<std::function<void(const AixContentInsets& /* insets */)>>> onWillApplyContentInsets;
59
60
  CachedProp<std::optional<std::function<void(bool /* isNearEnd */)>>> onScrolledNearEndChange;
60
61
  CachedProp<std::optional<std::function<void(const std::shared_ptr<HybridAixSpec>& /* ref */)>>> hybridRef;
@@ -12,6 +12,7 @@
12
12
  "mainScrollViewID": true,
13
13
  "penultimateCellIndex": true,
14
14
  "shouldApplyContentInsets": true,
15
+ "applyContentInsetDelay": true,
15
16
  "onWillApplyContentInsets": true,
16
17
  "onScrolledNearEndChange": true,
17
18
  "hybridRef": true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aix",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "author": "Fernando Rojo",
5
5
  "repository": {
6
6
  "type": "git",
@@ -120,6 +120,13 @@ export interface AixProps extends HybridViewProps {
120
120
  penultimateCellIndex?: number
121
121
 
122
122
  shouldApplyContentInsets?: boolean
123
+ /**
124
+ * Optional delay in milliseconds before applying content insets to the scroll view.
125
+ *
126
+ * This can help work around race conditions where insets are applied before
127
+ * React commits layout changes.
128
+ */
129
+ applyContentInsetDelay?: number
123
130
  onWillApplyContentInsets?: (insets: AixContentInsets) => void
124
131
  /**
125
132
  * Called when the scroll position changes between "near end" and "not near end" states.