@ringg/react-native 0.2.0 → 0.3.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 +290 -0
- package/dist/index.d.mts +392 -1
- package/dist/index.d.ts +392 -1
- package/dist/index.js +4605 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4579 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +16 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { FC, ReactNode } from 'react';
|
|
2
|
+
import { Room, RoomOptions } from 'livekit-client';
|
|
3
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
4
|
+
|
|
1
5
|
/**
|
|
2
6
|
* Theme types for the widget.
|
|
3
7
|
* Covers both widget-level theming and per-component theming.
|
|
@@ -177,6 +181,16 @@ interface RinggWidgetConfig {
|
|
|
177
181
|
xApiKey?: string;
|
|
178
182
|
/** JWT for authentication (Authorization header) — wins over xApiKey when both are set */
|
|
179
183
|
authorization?: string;
|
|
184
|
+
/**
|
|
185
|
+
* `Origin` to present to the backend, which allow-lists an agent's callers
|
|
186
|
+
* by that value. Browsers send it themselves, so web leaves this unset and
|
|
187
|
+
* MUST NOT set it — `Origin` is a forbidden header there. Native callers
|
|
188
|
+
* send nothing on their own and have to supply their own identity, by
|
|
189
|
+
* convention `<platform>://<bundleId>` (see @ringg/react-native's
|
|
190
|
+
* `appOrigin`). Whatever string is given is sent verbatim; only the backend
|
|
191
|
+
* decides whether it is allowed.
|
|
192
|
+
*/
|
|
193
|
+
clientOrigin?: string;
|
|
180
194
|
/** Custom variables passed to the agent (e.g., user name, role) */
|
|
181
195
|
variables?: WidgetVariables;
|
|
182
196
|
/** Environment mode — selects which UrlResolver entry to use */
|
|
@@ -961,4 +975,381 @@ declare const useRinggShell: (controller: RinggWidgetController) => ShellSnapsho
|
|
|
961
975
|
declare const useRinggComponents: (controller: RinggWidgetController) => ComponentSnapshot;
|
|
962
976
|
declare const useRinggSlashCommands: (controller: RinggWidgetController) => SlashCommandSnapshot;
|
|
963
977
|
|
|
964
|
-
|
|
978
|
+
/**
|
|
979
|
+
* RinggWidget — the assembled React Native widget.
|
|
980
|
+
*
|
|
981
|
+
* The native counterpart of the web assembly: the same tree, the same order,
|
|
982
|
+
* the same branches. It holds NO conversation state — every snapshot comes
|
|
983
|
+
* from `@ringg/core` via the shared hooks and every intent is forwarded to the
|
|
984
|
+
* controller. If a behaviour looks like it belongs here, it belongs in core.
|
|
985
|
+
*
|
|
986
|
+
* Three things genuinely differ from web, all forced by the platform:
|
|
987
|
+
*
|
|
988
|
+
* 1. There is no `position: fixed`. The widget is an absolutely-filled
|
|
989
|
+
* overlay with `pointerEvents="box-none"`, so taps pass through everywhere
|
|
990
|
+
* except the trigger and the panel. Mount it as the LAST child of the app
|
|
991
|
+
* root; `widgetPosition` (a web-only config) is ignored and the panel
|
|
992
|
+
* sizes itself to the device.
|
|
993
|
+
* 2. The keyboard covers the bottom of the screen. The panel lives inside a
|
|
994
|
+
* `KeyboardAvoidingView` so the composer stays visible while typing —
|
|
995
|
+
* without it the input is simply unreachable in text mode.
|
|
996
|
+
* 3. The transcript cannot measure a child's offset the way web does to
|
|
997
|
+
* anchor the newest turn's first line. It pins to the bottom instead, and
|
|
998
|
+
* the "new message" / "typing toggled" distinction is preserved so a long
|
|
999
|
+
* reply arriving does not yank the view while the user is reading.
|
|
1000
|
+
*/
|
|
1001
|
+
|
|
1002
|
+
interface RinggWidgetProps {
|
|
1003
|
+
controller: RinggWidgetController;
|
|
1004
|
+
/**
|
|
1005
|
+
* The LiveKit room from `createLiveKitTransport()`. Optional: it only powers
|
|
1006
|
+
* the in-call audio visualizer and the mute button. Without it the widget is
|
|
1007
|
+
* fully functional and the visualizer shows its ambient idle animation.
|
|
1008
|
+
*/
|
|
1009
|
+
room?: Room;
|
|
1010
|
+
}
|
|
1011
|
+
declare const RinggWidget: FC<RinggWidgetProps>;
|
|
1012
|
+
|
|
1013
|
+
/**
|
|
1014
|
+
* LiveKit React Native transport — the real `TransportAdapter` over
|
|
1015
|
+
* `@livekit/react-native`, behavior-matched to the web adapter (which is
|
|
1016
|
+
* itself matched to production, @desivocal/agents-cdn).
|
|
1017
|
+
*
|
|
1018
|
+
* The wire behavior is deliberately identical to `web/src/transport/
|
|
1019
|
+
* livekit-transport.ts` — same Room options, same double-send chat, same
|
|
1020
|
+
* agent-classification and session-ended rules — because both talk to the same
|
|
1021
|
+
* backend and core drives them through the same port. Read that file's parity
|
|
1022
|
+
* notes; they apply here verbatim.
|
|
1023
|
+
*
|
|
1024
|
+
* Only what the platform forces differs:
|
|
1025
|
+
* - remote audio needs no elements. Mobile plays subscribed audio through the
|
|
1026
|
+
* OS, so `<RoomAudioRenderer />`'s job becomes owning an audio SESSION
|
|
1027
|
+
* (see platform/audio-session.ts) rather than attaching media elements;
|
|
1028
|
+
* - `registerGlobals()` must run before a Room is constructed — the adapter
|
|
1029
|
+
* does it so a missing app-entry call is not a mysterious runtime failure;
|
|
1030
|
+
* - autoplay policy has no mobile equivalent, so the web adapter's
|
|
1031
|
+
* `startAudio()` recovery has no counterpart.
|
|
1032
|
+
*/
|
|
1033
|
+
|
|
1034
|
+
interface LiveKitTransportOptions {
|
|
1035
|
+
/**
|
|
1036
|
+
* Extra LiveKit `RoomOptions` merged over the parity defaults
|
|
1037
|
+
* (`dynacast: true, adaptiveStream: true`). Rarely needed.
|
|
1038
|
+
*/
|
|
1039
|
+
roomOptions?: RoomOptions;
|
|
1040
|
+
/**
|
|
1041
|
+
* Own the native audio session for the duration of a call — the mobile
|
|
1042
|
+
* equivalent of production's `<RoomAudioRenderer />`. Default true; disable
|
|
1043
|
+
* when the host app already manages an audio session (an in-app player, a
|
|
1044
|
+
* CallKit/ConnectionService integration).
|
|
1045
|
+
*/
|
|
1046
|
+
manageAudioSession?: boolean;
|
|
1047
|
+
/**
|
|
1048
|
+
* Call `registerGlobals()` before constructing the Room. Default true.
|
|
1049
|
+
* Set false when the app already calls it at its entry point — it is
|
|
1050
|
+
* idempotent, so this is a formality rather than a correctness switch.
|
|
1051
|
+
*/
|
|
1052
|
+
registerGlobals?: boolean;
|
|
1053
|
+
}
|
|
1054
|
+
interface LiveKitTransport {
|
|
1055
|
+
transport: TransportAdapter;
|
|
1056
|
+
/**
|
|
1057
|
+
* The underlying Room, for presentational concerns the port cannot express
|
|
1058
|
+
* (visualizer track handles, `isMicrophoneEnabled` readback — spec §11).
|
|
1059
|
+
* State-changing calls must keep going through the transport/core.
|
|
1060
|
+
*/
|
|
1061
|
+
room: Room;
|
|
1062
|
+
/** Tears down listeners, handlers and the audio session; disconnects the room. */
|
|
1063
|
+
dispose(): void;
|
|
1064
|
+
}
|
|
1065
|
+
declare const createLiveKitTransport: (options?: LiveKitTransportOptions) => LiveKitTransport;
|
|
1066
|
+
|
|
1067
|
+
/**
|
|
1068
|
+
* Native audio session — React Native's replacement for the web adapter's
|
|
1069
|
+
* hidden `<audio>` elements.
|
|
1070
|
+
*
|
|
1071
|
+
* On web, remote audio needs a DOM element to play into. On mobile it needs
|
|
1072
|
+
* the opposite: nothing to attach, but an OS-level audio session that has to
|
|
1073
|
+
* be configured and activated before the call and released after it, or the
|
|
1074
|
+
* mic indicator stays lit and the app keeps ducking other audio.
|
|
1075
|
+
*
|
|
1076
|
+
* The configuration encodes the mobile audio invariants (AGENTS.md rule 10):
|
|
1077
|
+
* communication mode so the call survives backgrounding and routes through the
|
|
1078
|
+
* earpiece/bluetooth stack rather than the media stream, and a preferred
|
|
1079
|
+
* output list that follows a headset or bluetooth device when one appears.
|
|
1080
|
+
*
|
|
1081
|
+
* Every call is best-effort. A device that refuses to hand over the audio
|
|
1082
|
+
* session must not take the call down with it — the user still gets a
|
|
1083
|
+
* (possibly routed-oddly) conversation, which beats a hard failure.
|
|
1084
|
+
*/
|
|
1085
|
+
interface AudioSessionPort {
|
|
1086
|
+
/** Configure + activate. Safe to call repeatedly; only the first wins. */
|
|
1087
|
+
start(): Promise<void>;
|
|
1088
|
+
/** Release the session. Safe to call when never started. */
|
|
1089
|
+
stop(): Promise<void>;
|
|
1090
|
+
}
|
|
1091
|
+
declare const createAudioSession: () => AudioSessionPort;
|
|
1092
|
+
|
|
1093
|
+
/**
|
|
1094
|
+
* React Native `MicPermissionPort`.
|
|
1095
|
+
*
|
|
1096
|
+
* Core's session store drives this on audio start: `isGranted()` first, then
|
|
1097
|
+
* `request()`, aborting the call on denial — identical to web. What differs is
|
|
1098
|
+
* how the answer is obtained, and the two platforms genuinely differ:
|
|
1099
|
+
*
|
|
1100
|
+
* - Android exposes a real permission API, so the state can be READ without
|
|
1101
|
+
* prompting. That matters: `isGranted()` must never show a dialog, or the
|
|
1102
|
+
* user gets prompted before they have pressed anything.
|
|
1103
|
+
* - iOS has no readable pre-check from JS. The only probe is `getUserMedia`,
|
|
1104
|
+
* which prompts on first use — the same fallback the web port uses for
|
|
1105
|
+
* browsers without the Permissions API. Once answered, iOS resolves the
|
|
1106
|
+
* probe from its own record without prompting again.
|
|
1107
|
+
*
|
|
1108
|
+
* The probe must never keep the microphone: every track it opens is stopped
|
|
1109
|
+
* before returning, or the OS recording indicator stays lit before the call
|
|
1110
|
+
* has even started.
|
|
1111
|
+
*/
|
|
1112
|
+
|
|
1113
|
+
declare const createNativeMicPermission: () => MicPermissionPort;
|
|
1114
|
+
|
|
1115
|
+
/**
|
|
1116
|
+
* App identity for the backend's caller allow-list.
|
|
1117
|
+
*
|
|
1118
|
+
* The Ringg backend allow-lists an agent's callers by `Origin`. A browser
|
|
1119
|
+
* attaches it automatically; a native app sends nothing, so it has to present
|
|
1120
|
+
* its own identity — by convention `<platform>://<bundleId>`, the scheme the
|
|
1121
|
+
* backend accepts for app clients. Without it the webcall request is rejected
|
|
1122
|
+
* before authentication even matters (`400 Origin header is required`).
|
|
1123
|
+
*
|
|
1124
|
+
* The bundle id is the INTEGRATOR's, not ours, so the value allow-listed in
|
|
1125
|
+
* the dashboard is their app. React Native cannot read it without a native
|
|
1126
|
+
* module, and this package refuses to grow one for a single string — so the id
|
|
1127
|
+
* is a parameter. Expo apps have it in `expo-application`'s `applicationId`;
|
|
1128
|
+
* bare apps already know their own.
|
|
1129
|
+
*
|
|
1130
|
+
* Mirrors `flutter/lib/src/platform/app_origin.dart`, which derives the same
|
|
1131
|
+
* string from `package_info_plus`.
|
|
1132
|
+
*/
|
|
1133
|
+
/**
|
|
1134
|
+
* `<platform>://<bundleId>` for the running app — e.g. `android://com.acme.app`.
|
|
1135
|
+
*
|
|
1136
|
+
* Returns undefined on a platform the backend has no scheme for, which
|
|
1137
|
+
* includes react-native-web: there the browser sends a real `Origin` and
|
|
1138
|
+
* setting one from JavaScript is forbidden anyway.
|
|
1139
|
+
*/
|
|
1140
|
+
declare const appOrigin: (bundleId: string) => string | undefined;
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* React Native `NotificationPlayer`.
|
|
1144
|
+
*
|
|
1145
|
+
* Core decides WHEN to play (an agent reply or component landing while the
|
|
1146
|
+
* widget is closed); the port only owns the how. On web that is one line —
|
|
1147
|
+
* `new Audio(url)`. RN has no audio playback in the platform at all: every
|
|
1148
|
+
* option (expo-audio, react-native-sound, react-native-video) is a native
|
|
1149
|
+
* module, and forcing one on integrators would mean an extra pod/gradle
|
|
1150
|
+
* dependency and an Expo config plugin for a notification chime.
|
|
1151
|
+
*
|
|
1152
|
+
* So the decision is inverted: the widget ships silent by default and the
|
|
1153
|
+
* integrator opts in with three lines against whichever player their app
|
|
1154
|
+
* already has. `notificationTuneUrl` from the config is handed to that player,
|
|
1155
|
+
* so the URL stays a config concern on every platform.
|
|
1156
|
+
*/
|
|
1157
|
+
|
|
1158
|
+
/** What an integrator's audio library has to be able to do. */
|
|
1159
|
+
type PlayTune = (tuneUrl: string) => void;
|
|
1160
|
+
/**
|
|
1161
|
+
* The default. Silent, and deliberately so — a widget that cannot find an
|
|
1162
|
+
* audio library should be quiet, not noisy about it on every reply.
|
|
1163
|
+
*/
|
|
1164
|
+
declare const createSilentNotificationPlayer: () => NotificationPlayer;
|
|
1165
|
+
/**
|
|
1166
|
+
* Bridges the config's tune URL to a host-provided player.
|
|
1167
|
+
*
|
|
1168
|
+
* ```ts
|
|
1169
|
+
* import { createAudioPlayer } from "expo-audio";
|
|
1170
|
+
* const notifications = createNotificationPlayer(DEFAULT_CONFIG.notificationTuneUrl, (url) => createAudioPlayer(url).play());
|
|
1171
|
+
* ```
|
|
1172
|
+
*
|
|
1173
|
+
* Playback failures are swallowed: a chime is best-effort by contract, and a
|
|
1174
|
+
* rejected promise here must never surface mid-conversation.
|
|
1175
|
+
*/
|
|
1176
|
+
declare const createNotificationPlayer: (tuneUrl: string, playTune: PlayTune) => NotificationPlayer;
|
|
1177
|
+
|
|
1178
|
+
/**
|
|
1179
|
+
* Host action dispatcher — the React Native counterpart of the web widget's
|
|
1180
|
+
* `executeDomAction`.
|
|
1181
|
+
*
|
|
1182
|
+
* Core normalises the `execute_dom_action` RPC into a `DomAction` and hands it
|
|
1183
|
+
* to the `onDomAction` port. On web that ends in `window.dispatchEvent`, which
|
|
1184
|
+
* works because the host page is already an event bus. RN has no such ambient
|
|
1185
|
+
* bus, so the integrator supplies the handler and gets the event name plus the
|
|
1186
|
+
* payload the agent sent — the same `{...default_payload, action_id}` detail
|
|
1187
|
+
* the web CustomEvent carries, so a shared backend config drives both.
|
|
1188
|
+
*
|
|
1189
|
+
* Validation, logging and the "unknown kind" path stay identical to web: those
|
|
1190
|
+
* are contract behaviour, not platform detail.
|
|
1191
|
+
*/
|
|
1192
|
+
|
|
1193
|
+
/** What the host receives — the web CustomEvent, minus the DOM. */
|
|
1194
|
+
interface HostAction {
|
|
1195
|
+
/** The agent-configured event name (`CustomEvent.type` on web). */
|
|
1196
|
+
name: string;
|
|
1197
|
+
/** `default_payload` with the action id folded in (`CustomEvent.detail`). */
|
|
1198
|
+
payload: Record<string, unknown>;
|
|
1199
|
+
}
|
|
1200
|
+
type HostActionHandler = (action: HostAction) => void;
|
|
1201
|
+
/**
|
|
1202
|
+
* Builds the `onDomAction` port. `onAction` runs in the caller's context and
|
|
1203
|
+
* is allowed to throw — a broken host handler is reported, never fatal.
|
|
1204
|
+
*/
|
|
1205
|
+
declare const createHostActionDispatcher: (onAction: HostActionHandler) => (action: DomAction | undefined | null, onLog?: DomActionLogger) => void;
|
|
1206
|
+
|
|
1207
|
+
/**
|
|
1208
|
+
* Widget theme for React Native.
|
|
1209
|
+
*
|
|
1210
|
+
* Deliberately thin: the theme VALUES (defaults, presets) and the merge/
|
|
1211
|
+
* contrast LOGIC both come from `@ringg/core` — `mergeWidgetTheme` here is
|
|
1212
|
+
* core's function, not a copy. The web package still carries its own copy of
|
|
1213
|
+
* that table for byte-fidelity with agents-cdn; RN has no such legacy to match
|
|
1214
|
+
* and takes the shared one, so a token change reaches this platform through
|
|
1215
|
+
* codegen (AGENTS.md rule 2).
|
|
1216
|
+
*
|
|
1217
|
+
* What IS local is unit translation. The theme surface is CSS-shaped
|
|
1218
|
+
* (`"16px"`, `"9999px"`, `fontFamily: "inherit"`) because that's the contract
|
|
1219
|
+
* integrators already write against on web; RN needs numbers and a real family
|
|
1220
|
+
* name, so the `resolve*` helpers below are the single conversion point.
|
|
1221
|
+
*/
|
|
1222
|
+
|
|
1223
|
+
interface WidgetThemeProviderProps {
|
|
1224
|
+
theme?: WidgetTheme;
|
|
1225
|
+
children: ReactNode;
|
|
1226
|
+
}
|
|
1227
|
+
/** `"16px"` → `16`. Unparseable values fall back rather than laying out as NaN. */
|
|
1228
|
+
declare const resolveRadius: (value: string | undefined, fallback?: number) => number;
|
|
1229
|
+
/** Button corner radius as an RN number, from core's shared style → CSS mapping. */
|
|
1230
|
+
declare const resolveButtonRadius: (style: ButtonStyle | undefined) => number;
|
|
1231
|
+
/**
|
|
1232
|
+
* `"inherit"` has no meaning in RN — there is no cascade. Returning undefined
|
|
1233
|
+
* lets `<Text>` fall through to the platform system font, which is what
|
|
1234
|
+
* "inherit" resolves to on web for an unstyled host page.
|
|
1235
|
+
*/
|
|
1236
|
+
declare const resolveFontFamily: (fontFamily: string | undefined) => string | undefined;
|
|
1237
|
+
declare const WidgetThemeProvider: FC<WidgetThemeProviderProps>;
|
|
1238
|
+
declare const useWidgetTheme: () => ResolvedWidgetTheme;
|
|
1239
|
+
|
|
1240
|
+
/**
|
|
1241
|
+
* Gradient fills for React Native.
|
|
1242
|
+
*
|
|
1243
|
+
* The theme surface is CSS-shaped — `primaryColor` may be a solid hex OR a
|
|
1244
|
+
* `linear-gradient(...)` string (see core `types/theme.ts`, and the gradient
|
|
1245
|
+
* presets in `widget-theme`). The DOM renders those for free; RN has no
|
|
1246
|
+
* gradient primitive at all, so every themed surface goes through here.
|
|
1247
|
+
*
|
|
1248
|
+
* Colour PARSING is not duplicated — `isGradient` / `extractColorsFromGradient`
|
|
1249
|
+
* / `getDominantColor` come from `@ringg/core`, the same functions the web
|
|
1250
|
+
* widget uses (AGENTS.md rule 2: shared values come from core, never a local
|
|
1251
|
+
* re-implementation). Only the two things core cannot know about — the angle in
|
|
1252
|
+
* SVG coordinates, and how to paint it — live in this file.
|
|
1253
|
+
*
|
|
1254
|
+
* Non-linear gradients (radial/conic) degrade to their dominant colour rather
|
|
1255
|
+
* than approximating badly; the theme presets are all linear.
|
|
1256
|
+
*/
|
|
1257
|
+
|
|
1258
|
+
interface GradientFillProps {
|
|
1259
|
+
/** A theme colour: solid (`#0a0a0b`, `rgb(...)`) or a CSS gradient string. */
|
|
1260
|
+
color: string;
|
|
1261
|
+
/** Applied to the wrapper — size, padding, radius, alignment. */
|
|
1262
|
+
style?: StyleProp<ViewStyle>;
|
|
1263
|
+
children?: ReactNode;
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Paints `color` behind `children`. Solid colours take the cheap path
|
|
1267
|
+
* (`backgroundColor`); gradients get an absolutely-filled SVG layer that
|
|
1268
|
+
* inherits the wrapper's rounding through `overflow: hidden`.
|
|
1269
|
+
*/
|
|
1270
|
+
declare const GradientFill: FC<GradientFillProps>;
|
|
1271
|
+
/**
|
|
1272
|
+
* A guaranteed-solid colour, for the properties RN can only paint flat —
|
|
1273
|
+
* borders, shadows, icon tints, status dots.
|
|
1274
|
+
*/
|
|
1275
|
+
declare const solidColor: (color: string) => string;
|
|
1276
|
+
|
|
1277
|
+
/**
|
|
1278
|
+
* `PortableStyles` → React Native styles.
|
|
1279
|
+
*
|
|
1280
|
+
* `PortableStyles` (core `types/config.ts`) is the deliberately small subset of
|
|
1281
|
+
* CSS the config surface accepts precisely so it can cross platforms. Web hands
|
|
1282
|
+
* the values straight to the DOM; RN cannot — it has no CSS parser, so a
|
|
1283
|
+
* `"16px"` string silently breaks layout instead of throwing.
|
|
1284
|
+
*
|
|
1285
|
+
* This is the one place that converts. Rules, matching the Dart port
|
|
1286
|
+
* (`flutter/lib/src/ui/widgets/portable_styles.dart`):
|
|
1287
|
+
* - numbers pass through as density-independent pixels;
|
|
1288
|
+
* - `"16px"` / `"16"` → `16`;
|
|
1289
|
+
* - `"50%"` survives only where RN accepts percentages (width/height);
|
|
1290
|
+
* - anything else (`calc()`, `em`, `auto`, gradients in `backgroundColor`) is
|
|
1291
|
+
* dropped rather than guessed at — a dropped style degrades, a wrong one
|
|
1292
|
+
* corrupts the layout.
|
|
1293
|
+
*/
|
|
1294
|
+
|
|
1295
|
+
/** Convert a config `PortableStyles` bag into an RN `ViewStyle`. */
|
|
1296
|
+
declare const toViewStyle: (styles: PortableStyles | undefined) => ViewStyle;
|
|
1297
|
+
/** Icon sizing from `ButtonIconConfig.size` (same parsing, single value). */
|
|
1298
|
+
declare const toSize: (value: string | number | undefined, fallback: number) => number;
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
* Markdown for chat bubbles.
|
|
1302
|
+
*
|
|
1303
|
+
* The web widget hands agent replies to `react-markdown` + `remark-gfm` and
|
|
1304
|
+
* lets Tailwind's prose plugin style the result. Neither of those crosses to
|
|
1305
|
+
* RN, and this package takes no dependency for it, so what lives here is a
|
|
1306
|
+
* renderer for the subset that actually appears in agent replies. Everything
|
|
1307
|
+
* else falls through as the literal text the model wrote — a reply must never
|
|
1308
|
+
* show half-parsed markup.
|
|
1309
|
+
*
|
|
1310
|
+
* Supported: paragraphs, `**bold**`, `*italic*`, inline code, `[links](url)`
|
|
1311
|
+
* (handed to `Linking`), bullet and ordered lists, `#` through `######`
|
|
1312
|
+
* headings, fenced code blocks, blockquotes (which may contain further
|
|
1313
|
+
* blocks), horizontal rules, and backslash escapes.
|
|
1314
|
+
*
|
|
1315
|
+
* Not supported, deliberately: TABLES — GFM's headline feature, but a
|
|
1316
|
+
* phone-width bubble has nowhere to put one, so a table degrades to a
|
|
1317
|
+
* paragraph of its pipe-separated source. Also absent: images (rendered as
|
|
1318
|
+
* their alt text), strikethrough, task lists, footnotes, reference links, raw
|
|
1319
|
+
* HTML, and nested list indentation (nested items flatten into their parent
|
|
1320
|
+
* list).
|
|
1321
|
+
*
|
|
1322
|
+
* Soft line breaks inside a paragraph collapse into spaces, which is what the
|
|
1323
|
+
* browser does for the web widget.
|
|
1324
|
+
*/
|
|
1325
|
+
|
|
1326
|
+
interface MarkdownProps {
|
|
1327
|
+
content: string;
|
|
1328
|
+
/** Body text. */
|
|
1329
|
+
color: string;
|
|
1330
|
+
/** Chrome that is not text: the rule, the quote bar, the code outline. */
|
|
1331
|
+
mutedColor: string;
|
|
1332
|
+
linkColor: string;
|
|
1333
|
+
fontSize?: number;
|
|
1334
|
+
fontFamily?: string;
|
|
1335
|
+
}
|
|
1336
|
+
declare const Markdown: FC<MarkdownProps>;
|
|
1337
|
+
|
|
1338
|
+
/**
|
|
1339
|
+
* Node identifiers for integrators and end-to-end tests.
|
|
1340
|
+
*
|
|
1341
|
+
* The web widget tags every meaningful node with `data-ringg="..."` and treats
|
|
1342
|
+
* those names as a contract — integrators select on them, so renaming one is a
|
|
1343
|
+
* breaking change. RN has no attribute namespace, but it has `testID`, which
|
|
1344
|
+
* surfaces to the same audiences (Detox, Maestro, Appium, the native view
|
|
1345
|
+
* hierarchy).
|
|
1346
|
+
*
|
|
1347
|
+
* So the mapping is mechanical and total: `data-ringg="header-title"` becomes
|
|
1348
|
+
* `testID="ringg-header-title"`. The prefix keeps widget nodes from colliding
|
|
1349
|
+
* with the host app's own testIDs, and the suffix stays byte-identical to web
|
|
1350
|
+
* so one selector list documents both platforms.
|
|
1351
|
+
*/
|
|
1352
|
+
/** `"header-title"` → `"ringg-header-title"`. */
|
|
1353
|
+
declare const ringgId: (name: string) => string;
|
|
1354
|
+
|
|
1355
|
+
export { type AudioSessionPort, type ControllerPorts, GradientFill, type HostAction, type HostActionHandler, type LiveKitTransport, type LiveKitTransportOptions, Markdown, type PlayTune, RinggWidget, type RinggWidgetConfig, type RinggWidgetController, type RinggWidgetProps, type TransportAdapter, WidgetThemeProvider, appOrigin, createAudioSession, createCallbackEventBus, createHostActionDispatcher, createLiveKitTransport, createNativeMicPermission, createNotificationPlayer, createRinggWidgetController, createSilentNotificationPlayer, resolveButtonRadius, resolveFontFamily, resolveRadius, ringgId, solidColor, toSize, toViewStyle, useRinggComponents, useRinggMessages, useRinggSession, useRinggShell, useRinggSlashCommands, useRinggTyping, useStoreSnapshot, useWidgetTheme };
|