@lorikeetai/react-native-sdk 2.0.0-alpha.3 → 2.1.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
@@ -2,8 +2,7 @@
2
2
 
3
3
  SDK containing embeddable React Native components that renders the LorikeetCX web widget inside a react-native-webview.
4
4
 
5
- > **Alpha.** The surface can still change between releases. Resume, storage and
6
- > token refresh are host responsibilities today; see [Resuming a
5
+ > Resume, storage and token refresh are host responsibilities; see [Resuming a
7
6
  > conversation](#resuming-a-conversation) and [Identity](#identity).
8
7
 
9
8
  ## Installation
@@ -17,12 +16,11 @@ successfully, and the failure surfaces later, when Metro cannot resolve the
17
16
  module.
18
17
  Metro reports one missing module at a time, so install all three at once.
19
18
 
20
- Name the SDK's version too. Alphas go to the `alpha` dist tag, not `latest`, so a
21
- bare `pnpm add @lorikeetai/react-native-sdk` installs the 1.x line, and no
22
- semver range reaches a prerelease either.
19
+ 2.x is on `latest`, so a bare install resolves it. A host already on 1.x stays
20
+ there until it asks for the new major, because no 1.x range reaches it.
23
21
 
24
22
  ```bash
25
- pnpm add @lorikeetai/react-native-sdk@2.0.0-alpha.3 \
23
+ pnpm add @lorikeetai/react-native-sdk \
26
24
  react-native-webview@^13.15.0 \
27
25
  react-native-safe-area-context@^5.6.0 \
28
26
  react-native-svg@^15.13.0
@@ -42,9 +40,6 @@ major. Widening them is a change that needs testing, not a version bump.
42
40
  - **`webviewProps` now reach the WebView.** The drop-in previously discarded
43
41
  them. If you were passing props that silently did nothing, they take effect
44
42
  now.
45
- - **Lazy ticket creation.** A ticket is no longer created when the widget opens,
46
- which changes when conversations appear in your Lorikeet queue and in anything
47
- counting them.
48
43
 
49
44
  ## Basic Usage
50
45
 
@@ -54,7 +49,7 @@ import { LorikeetWidgetView } from '@lorikeetai/react-native-sdk'
54
49
  export default function Screen() {
55
50
  return (
56
51
  <LorikeetWidgetView
57
- publicKey="wk_xxx"
52
+ publicKey="your-public-key"
58
53
  authToken="your-auth-token"
59
54
  onWebViewMessage={(event) => console.log(event.nativeEvent.data)}
60
55
  />
@@ -64,7 +59,9 @@ export default function Screen() {
64
59
 
65
60
  **Props:**
66
61
 
67
- - `publicKey` (required): The public key for your Lorikeet account
62
+ - `publicKey` (required): The public key issued for your Lorikeet account. Use the value you were
63
+ given rather than matching a shape: the format has changed over time, and keys issued under
64
+ earlier ones keep working
68
65
  - `authToken` (required): Authentication token for the user
69
66
  - `baseUrl` (optional): Base URL for the Lorikeet API (defaults to `https://chat.lorikeetcx.ai`)
70
67
  - `customisations` (optional): Widget customisation options. Exported as
@@ -81,6 +78,48 @@ export default function Screen() {
81
78
  defaults for media playback, scaling and the loading state can all be
82
79
  overridden; `onMessage` is additive, so the SDK reads the bridge first and
83
80
  then calls yours
81
+ - `avoidKeyboard` (optional): Pads the widget clear of the keyboard. Defaults to
82
+ `true`. See [The keyboard](#the-keyboard)
83
+
84
+ ## The keyboard
85
+
86
+ Android apps drawn edge to edge, the default for current Expo and React Native
87
+ apps, no longer resize their window for the keyboard. `adjustResize` in the
88
+ manifest does not change that. Nothing shrinks the WebView, so the keyboard
89
+ covers the composer and the page is never told the keyboard is there.
90
+
91
+ `LorikeetWidgetView` handles this itself: it wraps its content in a
92
+ `KeyboardAvoidingView` with `behavior="padding"` on both platforms, and measures
93
+ its own position so it works below a navigation header or tab bar. On iOS the
94
+ same padding keeps the page's header on screen. If your screen already makes
95
+ room for the keyboard, pass `avoidKeyboard={false}`, or the widget is padded
96
+ twice.
97
+
98
+ If you compose the parts yourself, the keyboard is yours. Wrap them in a
99
+ `KeyboardAvoidingView` with `behavior="padding"` on both platforms, and set
100
+ `keyboardVerticalOffset` to their distance from the top of the screen.
101
+ `apps/rn-widget-demo/src/components/WidgetPane.tsx` measures it with
102
+ `measureInWindow`. Check it on an Android device: an iOS simulator will not
103
+ show the problem.
104
+
105
+ ## Which header you get
106
+
107
+ `hideHeader` decides whose header the customer sees, not whether there is one.
108
+ `LorikeetWidgetView` defaults it to `true`.
109
+
110
+ | Value | The chat page's header | The SDK's own header |
111
+ | ------------------------------ | ---------------------- | -------------------------------------------------- |
112
+ | `false` | Shown | Not rendered |
113
+ | `true` (the drop-in's default) | Hidden | Rendered, with a new-chat control and nothing else |
114
+
115
+ The page's header is the one carrying the controls for leaving a conversation.
116
+ The SDK's carries a new-chat control and nothing else, so under the drop-in's
117
+ default a host that neither sets `hideHeader: false` nor builds its own header
118
+ leaves the customer no way out of the chat.
119
+
120
+ Which controls the page's header offers, and what they are called, depends on
121
+ the account. Build against "the page's own controls" rather than a fixed set,
122
+ and ask us what your account serves before reproducing them.
84
123
 
85
124
  ## Resuming a conversation
86
125
 
@@ -103,7 +142,7 @@ useEffect(() => {
103
142
 
104
143
  return (
105
144
  <LorikeetWidgetView
106
- publicKey="wk_xxx"
145
+ publicKey="your-public-key"
107
146
  authToken={token}
108
147
  ticketId={resume.ticketId}
109
148
  session={resume.session}
@@ -156,6 +195,43 @@ not learn two vocabularies.
156
195
  `preload_complete` is in the union but cannot reach React Native: the preload
157
196
  page posts it to a web parent only. Do not wait on it.
158
197
 
198
+ ## Minimise, end and new chat
199
+
200
+ `close` means the page hid itself. It may carry a `reason`: a minimise keeps the
201
+ conversation and offers a control that reopens it, an end offers one that starts a
202
+ replacement. Treat an absent reason as an end, because that is what the widget
203
+ does.
204
+
205
+ Do not unmount on `close`. The end and any CSAT rating are still in flight, and
206
+ tearing the page down cancels them. The widget hides the chat itself.
207
+
208
+ While `ticketId` is set, the new-chat control reloads that conversation rather
209
+ than replacing it. To start a new conversation, clear your stored `ticketId` and
210
+ `session` and stop passing them, so the next load falls back to `/widget`.
211
+
212
+ ## The widget handle
213
+
214
+ Pass a `ref` to `LorikeetWidgetView` to reach these.
215
+
216
+ ```tsx
217
+ const widget = useRef<LorikeetWidgetHandle>(null)
218
+
219
+ widget.current?.getConversation() // { minimised, ticketId?, session? } | null
220
+ widget.current?.logout()
221
+ widget.current?.reopen()
222
+ ```
223
+
224
+ - `getConversation()` reports the conversation the widget is on, which is not
225
+ always the one the host passed in: a refused resume is replaced, and a new chat
226
+ replaces the conversation before it. It returns `null` until the page has
227
+ reported a ticket, and it can report a ticket with no session, so it supplements
228
+ the persistence events rather than replacing them.
229
+ - `logout()` clears this customer's chat from the device. See
230
+ [Logging out](#logging-out).
231
+ - `reopen()` shows a minimised conversation again, exactly as the customer left
232
+ it. Nothing reloads and nothing is reported. A no-op unless the widget is
233
+ minimised, so a host with its own launcher can call it unconditionally.
234
+
159
235
  ## Logging out
160
236
 
161
237
  Clearing your own stored `ticketId` and `session` is not enough. The chat keeps
@@ -196,11 +272,26 @@ previous customer's credentials still unlock. Call `logout()` again to retry.
196
272
  That obligation outlives the component. The SDK stores nothing, so a widget
197
273
  unmounted before the clear confirmed, by a screen change or an app restart,
198
274
  comes back with no memory of it and opens a chat normally, on a device whose
199
- cookies were never cleared. Record the failure alongside the conversation you
200
- already store, and call `logout()` again before mounting a chat for anyone
201
- else. We would rather own this than ask you to, and
202
- [ADP-3160](https://linear.app/lorikeet/issue/ADP-3160) is where that storage
203
- layer belongs.
275
+ cookies were never cleared.
276
+
277
+ So persist the logout before you start it, rather than recording that it failed.
278
+ Write down the `baseUrl`, `publicKey` and cookie-store `webviewProps` you are
279
+ clearing, read it at launch, and delete it only once `logged_out` has arrived.
280
+ Waiting for `logout_failed` is not enough on its own: unmounting cancels its
281
+ timer, so an app that dies inside the ten seconds reports nothing at all.
282
+
283
+ Recovery then has to clear that recorded target rather than whatever the next
284
+ launch happens to configure. The page drops only that public key's entry, and
285
+ settings such as `sharedCookiesEnabled` decide which cookie store it reaches, so
286
+ clearing another account or another store reports `logged_out` just the same.
287
+
288
+ It does not need the customer who left, though. The page needs no `authToken`, so
289
+ a host that no longer holds the departed customer's, which after a restart is the
290
+ normal case, can still clear the device by navigating a WebView to
291
+ `getLogoutUrl()` and waiting for its confirmation, as below. Do not reach for the
292
+ drop-in's `logout()` here: it clears for whichever credentials are in props and
293
+ then will not open a chat for them until they change, so calling it with the next
294
+ customer's props locks that customer out instead of the last one.
204
295
 
205
296
  If you compose the parts yourself, build the URL and navigate to it:
206
297
 
@@ -217,8 +308,7 @@ The anonymous customer's long-lived token is deliberately not among these
217
308
  events. It identifies the same anonymous customer for a year, this SDK offers
218
309
  no way to hand one back, and the web SDK keeps the same message inside the
219
310
  frame. A host that genuinely needs it can read the raw bridge, but storing it
220
- is storing a credential, and [ADP-3160](https://linear.app/lorikeet/issue/ADP-3160)
221
- is the work that should remove the need.
311
+ is storing a credential.
222
312
 
223
313
  ## Identity
224
314
 
@@ -309,8 +399,14 @@ WebView component that handles the widget content.
309
399
 
310
400
  ## Development
311
401
 
312
- - Build: `pnpm -w turbo build --filter=@lorikeetai/react-native-sdk`
313
- - Lint: `pnpm --filter=@lorikeetai/react-native-sdk lint`
402
+ This package is outside the root pnpm workspace, on its own lockfile, so run these from `packages/react-native-widget` after `pnpm install` there.
403
+
404
+ - Build: `pnpm build`
405
+ - Test: `pnpm test`
406
+ - Typecheck: `pnpm typecheck`
407
+ - Lint: `pnpm lint`. It uses the root's oxlint and oxfmt, so run `pnpm install` at the repository root first.
408
+
409
+ To release, bump `version` in `package.json` and add its `CHANGELOG.md` section in the same pull request. See `docs/engineering/sdk-releases.md`.
314
410
 
315
411
  ## License
316
412
 
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- //#region \0rolldown/runtime.js
2
+ // \0rolldown/runtime.js
3
3
  var __create = Object.create;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -25,7 +25,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
  enumerable: true
26
26
  }) : target, mod));
27
27
 
28
- //#endregion
29
28
  let react = require("react");
30
29
  let react_native = require("react-native");
31
30
  let react_native_safe_area_context = require("react-native-safe-area-context");
@@ -34,7 +33,7 @@ let react_native_svg = require("react-native-svg");
34
33
  react_native_svg = __toESM(react_native_svg, 1);
35
34
  let react_jsx_runtime = require("react/jsx-runtime");
36
35
 
37
- //#region src/bridge.ts
36
+ // src/bridge.ts
38
37
  /**
39
38
  * The public key, base URL and token are one unit, and a conversation belongs to
40
39
  * the credentials that created it. Stamping an observation with them is what
@@ -54,8 +53,7 @@ const resumableConversation = (observed, identity, intendedTicketId) => {
54
53
  };
55
54
  };
56
55
 
57
- //#endregion
58
- //#region src/encode.ts
56
+ // src/encode.ts
59
57
  const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
60
58
  /**
61
59
  * Base64 of a string's UTF-8 bytes.
@@ -82,8 +80,7 @@ function base64EncodeUtf8(value) {
82
80
  return encoded;
83
81
  }
84
82
 
85
- //#endregion
86
- //#region \0@oxc-project+runtime@0.147.0/helpers/esm/typeof.js
83
+ // \0@oxc-project+runtime@0.156.0/helpers/esm/typeof.js
87
84
  function _typeof(o) {
88
85
  "@babel/helpers - typeof";
89
86
  return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
@@ -93,8 +90,7 @@ function _typeof(o) {
93
90
  }, _typeof(o);
94
91
  }
95
92
 
96
- //#endregion
97
- //#region \0@oxc-project+runtime@0.147.0/helpers/esm/toPrimitive.js
93
+ // \0@oxc-project+runtime@0.156.0/helpers/esm/toPrimitive.js
98
94
  function toPrimitive(t, r) {
99
95
  if ("object" != _typeof(t) || !t) return t;
100
96
  var e = t[Symbol.toPrimitive];
@@ -106,15 +102,13 @@ function toPrimitive(t, r) {
106
102
  return ("string" === r ? String : Number)(t);
107
103
  }
108
104
 
109
- //#endregion
110
- //#region \0@oxc-project+runtime@0.147.0/helpers/esm/toPropertyKey.js
105
+ // \0@oxc-project+runtime@0.156.0/helpers/esm/toPropertyKey.js
111
106
  function toPropertyKey(t) {
112
107
  var i = toPrimitive(t, "string");
113
108
  return "symbol" == _typeof(i) ? i : i + "";
114
109
  }
115
110
 
116
- //#endregion
117
- //#region \0@oxc-project+runtime@0.147.0/helpers/esm/defineProperty.js
111
+ // \0@oxc-project+runtime@0.156.0/helpers/esm/defineProperty.js
118
112
  function _defineProperty(e, r, t) {
119
113
  return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
120
114
  value: t,
@@ -124,8 +118,7 @@ function _defineProperty(e, r, t) {
124
118
  }) : e[r] = t, e;
125
119
  }
126
120
 
127
- //#endregion
128
- //#region src/client.ts
121
+ // src/client.ts
129
122
  var LorikeetClient = class {
130
123
  constructor({ customisations, api_base, metadata, public_key }) {
131
124
  _defineProperty(this, "baseUrl", "https://chat.lorikeetcx.ai");
@@ -171,12 +164,16 @@ var LorikeetClient = class {
171
164
  }
172
165
  };
173
166
 
174
- //#endregion
175
- //#region src/errors.ts
167
+ // src/errors.ts
176
168
  /**
177
- * Codes the chat page posts to the host as `{ type: 'error', error: CODE }`.
178
- * Mirrored here because the page publishes them nowhere else, so a host would
179
- * otherwise be matching on strings it discovered by observation.
169
+ * The chat page's error vocabulary, as it reaches a host in
170
+ * `{ type: 'error', error: CODE }`. Mirrored here because the page publishes it
171
+ * nowhere else, so a host would otherwise be matching on strings it discovered
172
+ * by observation.
173
+ *
174
+ * The page's own list rather than a list of codes seen in the wild:
175
+ * `MISSING_PUBLIC_KEY` has no producer today, and is kept because narrowing this
176
+ * would be a breaking change.
180
177
  */
181
178
  const LORIKEET_ERROR_CODES = [
182
179
  "MISSING_TOKEN",
@@ -214,8 +211,7 @@ function isUnusableConversation(message) {
214
211
  return type === "error" && typeof error === "string" ? isUnusableConversationCode(error) : false;
215
212
  }
216
213
 
217
- //#endregion
218
- //#region src/events.ts
214
+ // src/events.ts
219
215
  const asRecord = (message) => typeof message === "object" && message !== null && !Array.isArray(message) ? message : null;
220
216
  const asNonEmptyString = (value) => typeof value === "string" && value !== "" ? value : null;
221
217
  /**
@@ -293,8 +289,7 @@ const resumedTicketIdFromUri = (uri) => {
293
289
  return (_segments$ = segments[2]) !== null && _segments$ !== void 0 ? _segments$ : null;
294
290
  };
295
291
 
296
- //#endregion
297
- //#region src/lifecycle.ts
292
+ // src/lifecycle.ts
298
293
  /**
299
294
  * The page gives its message stream ten attempts with exponential backoff
300
295
  * capped at 30s (`message-stream-utils.tsx`), so the budget runs out roughly
@@ -331,8 +326,7 @@ const shouldReloadAfterBackground = (backgroundedAt, now, thresholdMs = BACKGROU
331
326
  return elapsed >= thresholdMs;
332
327
  };
333
328
 
334
- //#endregion
335
- //#region src/links.ts
329
+ // src/links.ts
336
330
  /**
337
331
  * Schemes the WebView resolves for itself. None of them is a page the customer
338
332
  * asked to leave the app for, and the OS cannot open them anyway.
@@ -377,8 +371,7 @@ const isInternalNavigation = (url, baseUrl) => {
377
371
  return target.origin === base.origin;
378
372
  };
379
373
 
380
- //#endregion
381
- //#region src/message-circle-plus-icon.tsx
374
+ // src/message-circle-plus-icon.tsx
382
375
  const MessageCirclePlusIcon = ({ size = 24, color = "currentColor", strokeWidth = 2 }) => {
383
376
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native_svg.default, {
384
377
  fill: "none",
@@ -415,13 +408,13 @@ const MessageCirclePlusIcon = ({ size = 24, color = "currentColor", strokeWidth
415
408
  });
416
409
  };
417
410
 
418
- //#endregion
419
- //#region src/widget.tsx
411
+ // src/widget.tsx
420
412
  const stylesheet = react_native.StyleSheet.create({
421
413
  root: {
422
414
  flex: 1,
423
415
  backgroundColor: "#fff"
424
416
  },
417
+ keyboardAvoiding: { flex: 1 },
425
418
  container: {
426
419
  flex: 1,
427
420
  borderRadius: 8,
@@ -525,7 +518,7 @@ const LorikeetWidgetHeader = ({ title, style, icon = /* @__PURE__ */ (0, react_j
525
518
  ]
526
519
  });
527
520
  };
528
- const LorikeetWidgetView = (0, react.forwardRef)(function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, styles, onEvent, onWebViewMessage, ticketId, session, metadata, webviewProps }, ref) {
521
+ const LorikeetWidgetView = (0, react.forwardRef)(function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, styles, onEvent, onWebViewMessage, ticketId, session, metadata, webviewProps, avoidKeyboard = true }, ref) {
529
522
  var _customisations$hideH, _customisations$title;
530
523
  const [lorikeetClient, setLorikeetClient] = (0, react.useState)(null);
531
524
  const [loaded, setLoaded] = (0, react.useState)(null);
@@ -536,6 +529,15 @@ const LorikeetWidgetView = (0, react.forwardRef)(function LorikeetWidgetView({ p
536
529
  setGeneration((previous) => previous + 1);
537
530
  }, []);
538
531
  const webViewRef = (0, react.useRef)(null);
532
+ const containerRef = (0, react.useRef)(null);
533
+ const [keyboardOffset, setKeyboardOffset] = (0, react.useState)(0);
534
+ const measureKeyboardOffset = (0, react.useCallback)((event) => {
535
+ var _containerRef$current;
536
+ const { y } = event.nativeEvent.layout;
537
+ (_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 || _containerRef$current.measureInWindow((_x, windowY) => {
538
+ setKeyboardOffset(windowY - y);
539
+ });
540
+ }, []);
539
541
  const [refusedTicketId, setRefusedTicketId] = (0, react.useState)(null);
540
542
  const observedRef = (0, react.useRef)(null);
541
543
  const backgroundedAtRef = (0, react.useRef)(null);
@@ -823,68 +825,75 @@ const LorikeetWidgetView = (0, react.forwardRef)(function LorikeetWidgetView({ p
823
825
  "right"
824
826
  ],
825
827
  style: stylesheet.root,
826
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
827
- style: stylesheet.container,
828
- children: [
829
- hideHeader && closed !== "minimised" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LorikeetWidgetHeader, {
830
- onIconPress: startNewChat,
831
- style: styles === null || styles === void 0 ? void 0 : styles.header,
832
- title: (_customisations$title = customisations === null || customisations === void 0 ? void 0 : customisations.title) !== null && _customisations$title !== void 0 ? _customisations$title : "Lorikeet AI Agent"
833
- }) : null,
834
- closed === "minimised" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableOpacity, {
835
- onPress: reopen,
836
- style: stylesheet.closedControl,
837
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
838
- style: stylesheet.headerText,
839
- children: "Reopen chat"
840
- })
841
- }) : null,
842
- !hideHeader && closed === "ended" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableOpacity, {
843
- onPress: startNewChat,
844
- style: stylesheet.closedControl,
845
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
846
- style: stylesheet.headerText,
847
- children: "Start new chat"
848
- })
849
- }) : null,
850
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
851
- accessibilityElementsHidden: isClosed,
852
- importantForAccessibility: isClosed ? "no-hide-descendants" : "auto",
853
- pointerEvents: isClosed ? "none" : "auto",
854
- style: isClosed ? stylesheet.closed : stylesheet.webView,
855
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LorikeetWidgetWebView, {
856
- baseUrl: lorikeetClient.baseUrl,
857
- onEvent: handleEvent,
858
- ref: webViewRef,
859
- uri: loaded.uri,
860
- webviewProps: {
861
- allowsInlineMediaPlayback: true,
862
- mediaPlaybackRequiresUserAction: false,
863
- scalesPageToFit: true,
864
- startInLoadingState: true,
865
- style: stylesheet.webView,
866
- ...webviewProps,
867
- onMessage: handleMessage,
868
- onRenderProcessGone: (event) => {
869
- var _webviewProps$onRende;
870
- if (generation === generationRef.current) reload();
871
- webviewProps === null || webviewProps === void 0 || (_webviewProps$onRende = webviewProps.onRenderProcessGone) === null || _webviewProps$onRende === void 0 || _webviewProps$onRende.call(webviewProps, event);
872
- },
873
- onContentProcessDidTerminate: (event) => {
874
- var _webviewProps$onConte;
875
- if (generation === generationRef.current) reload();
876
- webviewProps === null || webviewProps === void 0 || (_webviewProps$onConte = webviewProps.onContentProcessDidTerminate) === null || _webviewProps$onConte === void 0 || _webviewProps$onConte.call(webviewProps, event);
828
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.KeyboardAvoidingView, {
829
+ behavior: "padding",
830
+ enabled: avoidKeyboard,
831
+ keyboardVerticalOffset: keyboardOffset,
832
+ onLayout: measureKeyboardOffset,
833
+ style: stylesheet.keyboardAvoiding,
834
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
835
+ ref: containerRef,
836
+ style: stylesheet.container,
837
+ children: [
838
+ hideHeader && closed !== "minimised" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LorikeetWidgetHeader, {
839
+ onIconPress: startNewChat,
840
+ style: styles === null || styles === void 0 ? void 0 : styles.header,
841
+ title: (_customisations$title = customisations === null || customisations === void 0 ? void 0 : customisations.title) !== null && _customisations$title !== void 0 ? _customisations$title : "Lorikeet AI Agent"
842
+ }) : null,
843
+ closed === "minimised" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableOpacity, {
844
+ onPress: reopen,
845
+ style: stylesheet.closedControl,
846
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
847
+ style: stylesheet.headerText,
848
+ children: "Reopen chat"
849
+ })
850
+ }) : null,
851
+ !hideHeader && closed === "ended" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableOpacity, {
852
+ onPress: startNewChat,
853
+ style: stylesheet.closedControl,
854
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
855
+ style: stylesheet.headerText,
856
+ children: "Start new chat"
857
+ })
858
+ }) : null,
859
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
860
+ accessibilityElementsHidden: isClosed,
861
+ importantForAccessibility: isClosed ? "no-hide-descendants" : "auto",
862
+ pointerEvents: isClosed ? "none" : "auto",
863
+ style: isClosed ? stylesheet.closed : stylesheet.webView,
864
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LorikeetWidgetWebView, {
865
+ baseUrl: lorikeetClient.baseUrl,
866
+ onEvent: handleEvent,
867
+ ref: webViewRef,
868
+ uri: loaded.uri,
869
+ webviewProps: {
870
+ allowsInlineMediaPlayback: true,
871
+ mediaPlaybackRequiresUserAction: false,
872
+ scalesPageToFit: true,
873
+ startInLoadingState: true,
874
+ style: stylesheet.webView,
875
+ ...webviewProps,
876
+ onMessage: handleMessage,
877
+ onRenderProcessGone: (event) => {
878
+ var _webviewProps$onRende;
879
+ if (generation === generationRef.current) reload();
880
+ webviewProps === null || webviewProps === void 0 || (_webviewProps$onRende = webviewProps.onRenderProcessGone) === null || _webviewProps$onRende === void 0 || _webviewProps$onRende.call(webviewProps, event);
881
+ },
882
+ onContentProcessDidTerminate: (event) => {
883
+ var _webviewProps$onConte;
884
+ if (generation === generationRef.current) reload();
885
+ webviewProps === null || webviewProps === void 0 || (_webviewProps$onConte = webviewProps.onContentProcessDidTerminate) === null || _webviewProps$onConte === void 0 || _webviewProps$onConte.call(webviewProps, event);
886
+ }
877
887
  }
878
- }
879
- }, `${identity}|${resumingTicketId !== null && resumingTicketId !== void 0 ? resumingTicketId : ""}|${generation}`)
880
- })
881
- ]
888
+ }, `${identity}|${resumingTicketId !== null && resumingTicketId !== void 0 ? resumingTicketId : ""}|${generation}`)
889
+ })
890
+ ]
891
+ })
882
892
  })
883
893
  });
884
894
  });
885
895
  LorikeetWidgetView.displayName = "LorikeetWidgetView";
886
896
 
887
- //#endregion
888
897
  exports.LOGOUT_CONFIRMATION_TIMEOUT_MS = LOGOUT_CONFIRMATION_TIMEOUT_MS;
889
898
  exports.LORIKEET_ERROR_CODES = LORIKEET_ERROR_CODES;
890
899
  exports.LorikeetClient = LorikeetClient;