@opengeni/react-native 1.4.5-canary.37615713321001

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 (132) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +74 -0
  3. package/dist/adapters.d.ts +53 -0
  4. package/dist/adapters.js +3 -0
  5. package/dist/adapters.js.map +1 -0
  6. package/dist/attachments.d.ts +75 -0
  7. package/dist/chunk-3VSZAQ7G.js +391 -0
  8. package/dist/chunk-3VSZAQ7G.js.map +1 -0
  9. package/dist/chunk-5ZVK7WKA.js +88 -0
  10. package/dist/chunk-5ZVK7WKA.js.map +1 -0
  11. package/dist/chunk-EEDIMY3L.js +240 -0
  12. package/dist/chunk-EEDIMY3L.js.map +1 -0
  13. package/dist/chunk-M23SDI4R.js +83 -0
  14. package/dist/chunk-M23SDI4R.js.map +1 -0
  15. package/dist/chunk-PHJBSAR3.js +2006 -0
  16. package/dist/chunk-PHJBSAR3.js.map +1 -0
  17. package/dist/environment.d.ts +19 -0
  18. package/dist/expo.d.ts +25 -0
  19. package/dist/expo.js +267 -0
  20. package/dist/expo.js.map +1 -0
  21. package/dist/index.d.ts +13 -0
  22. package/dist/index.js +3345 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/presentation.d.ts +262 -0
  25. package/dist/read-acknowledgement.d.ts +39 -0
  26. package/dist/realtime/call-events.d.ts +11 -0
  27. package/dist/realtime/call-view.d.ts +26 -0
  28. package/dist/realtime/expo-call.d.ts +11 -0
  29. package/dist/realtime/native-call.d.ts +104 -0
  30. package/dist/realtime/native-realtime.d.ts +35 -0
  31. package/dist/realtime/use-native-session-realtime.d.ts +25 -0
  32. package/dist/session-view.d.ts +87 -0
  33. package/dist/timeline/activity.d.ts +34 -0
  34. package/dist/timeline/composer.d.ts +86 -0
  35. package/dist/timeline/controls.d.ts +27 -0
  36. package/dist/timeline/decisions.d.ts +29 -0
  37. package/dist/timeline/feedback.d.ts +26 -0
  38. package/dist/timeline/icon-nodes.generated.d.ts +738 -0
  39. package/dist/timeline/icon.d.ts +11 -0
  40. package/dist/timeline/index.d.ts +32 -0
  41. package/dist/timeline/index.js +5419 -0
  42. package/dist/timeline/index.js.map +1 -0
  43. package/dist/timeline/markdown.d.ts +11 -0
  44. package/dist/timeline/markdown.js +415 -0
  45. package/dist/timeline/markdown.js.map +1 -0
  46. package/dist/timeline/message-attachments.d.ts +13 -0
  47. package/dist/timeline/message-timeline.d.ts +36 -0
  48. package/dist/timeline/model-picker.d.ts +17 -0
  49. package/dist/timeline/primitives.d.ts +85 -0
  50. package/dist/timeline/queue-dock.d.ts +19 -0
  51. package/dist/timeline/session-actions.d.ts +34 -0
  52. package/dist/timeline/session-list.d.ts +50 -0
  53. package/dist/timeline/session-screen.d.ts +35 -0
  54. package/dist/timeline/session-signals.d.ts +37 -0
  55. package/dist/timeline/sheet.d.ts +14 -0
  56. package/dist/timeline/theme.d.ts +58 -0
  57. package/dist/timeline/thinking-orb.d.ts +13 -0
  58. package/dist/timeline/turn.d.ts +80 -0
  59. package/dist/ui/attention.d.ts +14 -0
  60. package/dist/ui/cards.d.ts +21 -0
  61. package/dist/ui/composer.d.ts +21 -0
  62. package/dist/ui/format.d.ts +13 -0
  63. package/dist/ui/icons.d.ts +9 -0
  64. package/dist/ui/index.d.ts +12 -0
  65. package/dist/ui/index.js +1757 -0
  66. package/dist/ui/index.js.map +1 -0
  67. package/dist/ui/steps-sheet.d.ts +10 -0
  68. package/dist/ui/theme.d.ts +63 -0
  69. package/dist/ui/timeline.d.ts +48 -0
  70. package/dist/ui/web-tokens.gen.d.ts +90 -0
  71. package/dist/use-native-session.d.ts +40 -0
  72. package/dist/view-model.d.ts +52 -0
  73. package/dist/voice-input.d.ts +102 -0
  74. package/dist/webrtc.d.ts +9 -0
  75. package/dist/webrtc.js +19 -0
  76. package/dist/webrtc.js.map +1 -0
  77. package/expo-module.config.json +7 -0
  78. package/ios/OpenGeniCall.podspec +22 -0
  79. package/ios/OpenGeniCallAppDelegateSubscriber.swift +29 -0
  80. package/ios/OpenGeniCallCenter.swift +189 -0
  81. package/ios/OpenGeniCallModule.swift +64 -0
  82. package/package.json +93 -0
  83. package/src/adapters.ts +150 -0
  84. package/src/attachments.ts +396 -0
  85. package/src/environment.tsx +214 -0
  86. package/src/expo.ts +295 -0
  87. package/src/index.ts +98 -0
  88. package/src/presentation.ts +285 -0
  89. package/src/read-acknowledgement.ts +72 -0
  90. package/src/realtime/call-events.ts +41 -0
  91. package/src/realtime/call-view.tsx +220 -0
  92. package/src/realtime/expo-call.ts +49 -0
  93. package/src/realtime/native-call.ts +266 -0
  94. package/src/realtime/native-realtime.ts +115 -0
  95. package/src/realtime/use-native-session-realtime.ts +29 -0
  96. package/src/session-view.tsx +2251 -0
  97. package/src/timeline/activity.tsx +736 -0
  98. package/src/timeline/composer.tsx +686 -0
  99. package/src/timeline/controls.tsx +151 -0
  100. package/src/timeline/decisions.tsx +651 -0
  101. package/src/timeline/feedback.tsx +251 -0
  102. package/src/timeline/icon-nodes.generated.ts +1830 -0
  103. package/src/timeline/icon.tsx +44 -0
  104. package/src/timeline/index.ts +67 -0
  105. package/src/timeline/markdown.tsx +510 -0
  106. package/src/timeline/message-attachments.tsx +155 -0
  107. package/src/timeline/message-timeline.tsx +1042 -0
  108. package/src/timeline/model-picker.tsx +450 -0
  109. package/src/timeline/primitives.tsx +561 -0
  110. package/src/timeline/queue-dock.tsx +331 -0
  111. package/src/timeline/session-actions.tsx +379 -0
  112. package/src/timeline/session-list.tsx +338 -0
  113. package/src/timeline/session-screen.tsx +488 -0
  114. package/src/timeline/session-signals.tsx +312 -0
  115. package/src/timeline/sheet.tsx +153 -0
  116. package/src/timeline/theme.tsx +128 -0
  117. package/src/timeline/thinking-orb.tsx +93 -0
  118. package/src/timeline/turn.tsx +569 -0
  119. package/src/ui/attention.tsx +220 -0
  120. package/src/ui/cards.tsx +267 -0
  121. package/src/ui/composer.tsx +296 -0
  122. package/src/ui/format.ts +86 -0
  123. package/src/ui/icons.tsx +131 -0
  124. package/src/ui/index.ts +18 -0
  125. package/src/ui/steps-sheet.tsx +71 -0
  126. package/src/ui/theme.ts +261 -0
  127. package/src/ui/timeline.tsx +837 -0
  128. package/src/ui/web-tokens.gen.ts +94 -0
  129. package/src/use-native-session.ts +219 -0
  130. package/src/view-model.ts +485 -0
  131. package/src/voice-input.ts +382 -0
  132. package/src/webrtc.ts +30 -0
@@ -0,0 +1,450 @@
1
+ import type { ClientModel, LatencyMode, ReasoningEffort } from "@opengeni/sdk";
2
+ import {
3
+ compactEffortLabel,
4
+ groupPresentationFor,
5
+ modelPickerChoice,
6
+ modelPickerMenuView,
7
+ type ModelPickerGroupPresentation,
8
+ type ModelPickerInput,
9
+ } from "@opengeni/react/model-picker-model";
10
+ import { labelReasoningEffort, type PickerBillingClass } from "@opengeni/react/model-policy";
11
+ import { useState, type ReactNode } from "react";
12
+ import { Pressable, ScrollView, Text, TextInput, View } from "react-native";
13
+ import { Icon, type NativeIconName } from "./icon";
14
+ import { ModelMark } from "./session-list";
15
+ import { BottomSheet } from "./sheet";
16
+ import { fontStyle, useNativeTimelineTheme } from "./theme";
17
+
18
+ /* ----------------------------------------------------------------------------
19
+ The web composer's model menu as a native sheet: search, payment groups with
20
+ maker marks, the selected check, unavailable rows with their reason, and the
21
+ Thinking segmented control with Fast. Everything it shows comes from the
22
+ shared model picker model; only the chrome is native.
23
+ -------------------------------------------------------------------------- */
24
+
25
+ export type ModelPickerSheetProps = ModelPickerInput<ReactNode> & {
26
+ open: boolean;
27
+ onClose: () => void;
28
+ loading?: boolean | undefined;
29
+ error?: string | null | undefined;
30
+ disabled?: boolean | undefined;
31
+ onModelChange: (modelId: string) => void;
32
+ onEffortChange: (effort: ReasoningEffort) => void;
33
+ onLatencyModeChange: (mode: LatencyMode) => void;
34
+ /** Called after a choice, for host haptics. */
35
+ onSelectionFeedback?: (() => void) | undefined;
36
+ };
37
+
38
+ const GROUP_ICON: Record<PickerBillingClass, NativeIconName> = {
39
+ opengeni_credits: "sparkles",
40
+ external: "globe",
41
+ codex_subscription: "sparkles",
42
+ claude_subscription: "sparkles",
43
+ supergrok_subscription: "sparkles",
44
+ byok: "key-round",
45
+ organization_byok: "key-round",
46
+ };
47
+
48
+ /** The subscription rails carry their maker's mark, as on web. */
49
+ const GROUP_MARK_MODEL: Partial<Record<PickerBillingClass, string>> = {
50
+ codex_subscription: "codex/gpt",
51
+ claude_subscription: "claude",
52
+ supergrok_subscription: "grok",
53
+ };
54
+
55
+ function GroupMark(props: {
56
+ billingClass: PickerBillingClass;
57
+ icon: ReactNode | undefined;
58
+ color: string;
59
+ }) {
60
+ if (props.icon === null) return null;
61
+ if (props.icon !== undefined) return <>{props.icon}</>;
62
+ const model = GROUP_MARK_MODEL[props.billingClass];
63
+ if (model) return <ModelMark model={model} size={14} color={props.color} />;
64
+ return <Icon name={GROUP_ICON[props.billingClass]} size={14} color={props.color} />;
65
+ }
66
+
67
+ export function ModelPickerSheet(props: ModelPickerSheetProps) {
68
+ const theme = useNativeTimelineTheme();
69
+ const c = theme.colors;
70
+ const [query, setQuery] = useState("");
71
+ const view = modelPickerMenuView(props, query);
72
+ const { messages, groups, thinking } = view;
73
+ const close = () => {
74
+ setQuery("");
75
+ props.onClose();
76
+ };
77
+ const choose = (row: (typeof view.rows)[number]) => {
78
+ if (props.disabled) return;
79
+ const choice = modelPickerChoice(props, row);
80
+ if (!choice) return;
81
+ if (choice.model !== null) props.onModelChange(choice.model);
82
+ props.onEffortChange(choice.effort);
83
+ if (choice.latencyMode !== null) props.onLatencyModeChange(choice.latencyMode);
84
+ props.onSelectionFeedback?.();
85
+ };
86
+ const label = { ...fontStyle(theme), fontSize: 15, color: c.fg };
87
+ const muted = { ...fontStyle(theme), fontSize: 13, color: c["fg-muted"] };
88
+ return (
89
+ <BottomSheet open={props.open} onClose={close} accessibilityLabel={messages.label}>
90
+ <View
91
+ style={{
92
+ flexDirection: "row",
93
+ alignItems: "center",
94
+ justifyContent: "space-between",
95
+ paddingHorizontal: 20,
96
+ paddingTop: 0,
97
+ paddingBottom: 6,
98
+ }}
99
+ >
100
+ <Text
101
+ accessibilityRole="header"
102
+ style={{ ...fontStyle(theme, 600), fontSize: 17, color: c.fg }}
103
+ >
104
+ {messages.label}
105
+ </Text>
106
+ <Pressable
107
+ accessibilityRole="button"
108
+ accessibilityLabel="Done"
109
+ onPress={close}
110
+ hitSlop={8}
111
+ style={{ minHeight: 44, justifyContent: "center", paddingHorizontal: 4 }}
112
+ >
113
+ <Text style={{ ...fontStyle(theme, 600), fontSize: 16, color: c.accent }}>Done</Text>
114
+ </Pressable>
115
+ </View>
116
+ {view.anySelectable ? (
117
+ <View
118
+ style={{
119
+ flexDirection: "row",
120
+ alignItems: "center",
121
+ gap: 8,
122
+ marginHorizontal: 16,
123
+ marginBottom: 4,
124
+ paddingHorizontal: 12,
125
+ minHeight: 40,
126
+ borderRadius: theme.radius.md,
127
+ backgroundColor: c["surface-2"],
128
+ }}
129
+ >
130
+ <Icon name="search" size={16} color={c["fg-subtle"]} />
131
+ <TextInput
132
+ accessibilityLabel={messages.searchLabel}
133
+ placeholder={messages.searchPlaceholder}
134
+ placeholderTextColor={c["fg-subtle"]}
135
+ value={query}
136
+ onChangeText={setQuery}
137
+ autoCorrect={false}
138
+ autoCapitalize="none"
139
+ clearButtonMode="while-editing"
140
+ style={{
141
+ ...fontStyle(theme),
142
+ flex: 1,
143
+ fontSize: 16,
144
+ color: c.fg,
145
+ paddingVertical: 8,
146
+ }}
147
+ />
148
+ </View>
149
+ ) : null}
150
+ {props.error ? (
151
+ <Text
152
+ accessibilityRole="alert"
153
+ style={{
154
+ ...muted,
155
+ color: c["status-failed"],
156
+ paddingHorizontal: 20,
157
+ paddingVertical: 6,
158
+ }}
159
+ >
160
+ {props.error}
161
+ </Text>
162
+ ) : null}
163
+ <ScrollView
164
+ style={{ flexGrow: 0 }}
165
+ keyboardShouldPersistTaps="handled"
166
+ keyboardDismissMode="on-drag"
167
+ contentContainerStyle={{ paddingVertical: 4 }}
168
+ >
169
+ {props.loading ? (
170
+ <View accessibilityRole="progressbar" accessibilityLabel={messages.loading}>
171
+ {[58, 44, 66, 50].map((width) => (
172
+ <View
173
+ key={width}
174
+ style={{
175
+ flexDirection: "row",
176
+ alignItems: "center",
177
+ gap: 12,
178
+ height: 48,
179
+ paddingHorizontal: 20,
180
+ }}
181
+ >
182
+ <View
183
+ style={{
184
+ width: 16,
185
+ height: 16,
186
+ borderRadius: 4,
187
+ backgroundColor: c["surface-2"],
188
+ }}
189
+ />
190
+ <View
191
+ style={{
192
+ height: 10,
193
+ width: `${width}%`,
194
+ borderRadius: 4,
195
+ backgroundColor: c["surface-2"],
196
+ }}
197
+ />
198
+ </View>
199
+ ))}
200
+ </View>
201
+ ) : view.anySelectable ? (
202
+ <>
203
+ {groups.map((group, index) => (
204
+ <View
205
+ key={group.billingClass}
206
+ accessibilityLabel={group.label}
207
+ style={
208
+ index > 0
209
+ ? {
210
+ marginTop: 6,
211
+ paddingTop: 6,
212
+ borderTopWidth: 1,
213
+ borderTopColor: c.border,
214
+ }
215
+ : undefined
216
+ }
217
+ >
218
+ <View
219
+ style={{
220
+ flexDirection: "row",
221
+ alignItems: "center",
222
+ gap: 8,
223
+ paddingHorizontal: 20,
224
+ paddingTop: 8,
225
+ paddingBottom: 2,
226
+ }}
227
+ >
228
+ <GroupMark
229
+ billingClass={group.billingClass}
230
+ icon={groupPresentationFor(props, group.billingClass)?.icon}
231
+ color={c["fg-subtle"]}
232
+ />
233
+ <Text style={{ ...fontStyle(theme, 500), fontSize: 13, color: c["fg-muted"] }}>
234
+ {group.label}
235
+ </Text>
236
+ </View>
237
+ {group.description ? (
238
+ <Text style={{ ...muted, paddingHorizontal: 20, paddingBottom: 4 }}>
239
+ {group.description}
240
+ </Text>
241
+ ) : null}
242
+ {group.rows.map((row) => {
243
+ const active = row.selectable && row.id === props.model;
244
+ const enabled = row.selectable && !props.disabled;
245
+ return (
246
+ <Pressable
247
+ key={row.id}
248
+ accessibilityRole="button"
249
+ accessibilityLabel={[row.label, row.unavailableReason]
250
+ .filter(Boolean)
251
+ .join(", ")}
252
+ accessibilityState={{ selected: active, disabled: !enabled }}
253
+ disabled={!enabled}
254
+ onPress={() => choose(row)}
255
+ style={({ pressed }) => ({
256
+ flexDirection: "row",
257
+ alignItems: "center",
258
+ gap: 12,
259
+ minHeight: 48,
260
+ paddingHorizontal: 20,
261
+ paddingVertical: 6,
262
+ backgroundColor: pressed ? c.hover : "transparent",
263
+ opacity: row.selectable ? 1 : 0.5,
264
+ })}
265
+ >
266
+ {props.collapseScopes === false ? null : (
267
+ <ModelMark model={row.id} size={18} color={c["fg-muted"]} />
268
+ )}
269
+ <View style={{ flex: 1, minWidth: 0 }}>
270
+ <Text numberOfLines={1} style={label}>
271
+ {row.label}
272
+ </Text>
273
+ {row.unavailableReason ? (
274
+ <Text numberOfLines={2} style={muted}>
275
+ {row.unavailableReason}
276
+ </Text>
277
+ ) : null}
278
+ </View>
279
+ {row.catalog.cost === "free" ? (
280
+ <Text
281
+ style={{
282
+ ...muted,
283
+ fontSize: 12,
284
+ paddingHorizontal: 8,
285
+ paddingVertical: 2,
286
+ borderRadius: 999,
287
+ overflow: "hidden",
288
+ backgroundColor: c["surface-2"],
289
+ }}
290
+ >
291
+ {messages.free}
292
+ </Text>
293
+ ) : null}
294
+ {row.id === props.model ? <Icon name="check" size={18} color={c.fg} /> : null}
295
+ </Pressable>
296
+ );
297
+ })}
298
+ </View>
299
+ ))}
300
+ {view.matchCount === 0 ? (
301
+ <Text style={{ ...muted, paddingHorizontal: 20, paddingVertical: 12 }}>
302
+ {view.searching ? messages.noMatches : messages.noModels}
303
+ </Text>
304
+ ) : null}
305
+ </>
306
+ ) : (
307
+ <View style={{ paddingHorizontal: 20, paddingVertical: 12, gap: 4 }}>
308
+ <Text style={{ ...fontStyle(theme, 500), fontSize: 15, color: c.fg }}>
309
+ {messages.connectTitle}
310
+ </Text>
311
+ <Text style={muted}>
312
+ {view.rows.length > 0 ? messages.connectBody : messages.noModels}
313
+ </Text>
314
+ </View>
315
+ )}
316
+ </ScrollView>
317
+ {view.anySelectable && view.selected && (view.unsupportedAttachments || thinking) ? (
318
+ <View
319
+ style={{
320
+ borderTopWidth: 1,
321
+ borderTopColor: c.border,
322
+ paddingHorizontal: 20,
323
+ paddingTop: 12,
324
+ gap: 8,
325
+ }}
326
+ >
327
+ {view.unsupportedAttachments ? (
328
+ <Text style={{ ...muted, color: c["fg-subtle"] }}>
329
+ {messages.unsupportedAttachments}
330
+ </Text>
331
+ ) : null}
332
+ {thinking ? (
333
+ <>
334
+ <View
335
+ style={{
336
+ flexDirection: "row",
337
+ alignItems: "center",
338
+ justifyContent: "space-between",
339
+ minHeight: 32,
340
+ }}
341
+ >
342
+ {thinking.showThinking ? (
343
+ <Text style={{ ...muted, color: c["fg-subtle"] }}>{messages.thinking}</Text>
344
+ ) : (
345
+ <View />
346
+ )}
347
+ {thinking.showFast ? (
348
+ <Pressable
349
+ accessibilityRole="switch"
350
+ accessibilityLabel={`${messages.fast}, ${messages.fastRateHint}`}
351
+ accessibilityState={{
352
+ checked: props.latencyMode === "fast",
353
+ disabled: Boolean(props.disabled || thinking.disabled),
354
+ }}
355
+ disabled={props.disabled || thinking.disabled}
356
+ onPress={() => {
357
+ props.onLatencyModeChange(props.latencyMode === "fast" ? "standard" : "fast");
358
+ props.onSelectionFeedback?.();
359
+ }}
360
+ style={({ pressed }) => ({
361
+ flexDirection: "row",
362
+ alignItems: "center",
363
+ gap: 6,
364
+ minHeight: 36,
365
+ paddingHorizontal: 8,
366
+ borderRadius: theme.radius.sm,
367
+ backgroundColor: pressed ? c.hover : "transparent",
368
+ })}
369
+ >
370
+ <Icon
371
+ name="zap"
372
+ size={14}
373
+ color={c["fg-muted"]}
374
+ fill={props.latencyMode === "fast" ? c["fg-muted"] : undefined}
375
+ />
376
+ <Text style={muted}>{messages.fast}</Text>
377
+ <Text style={{ ...muted, color: c["fg-subtle"] }}>{messages.fastRateHint}</Text>
378
+ </Pressable>
379
+ ) : null}
380
+ </View>
381
+ {thinking.showThinking ? (
382
+ <View
383
+ accessibilityRole="radiogroup"
384
+ accessibilityLabel={messages.thinkingEffort}
385
+ style={{
386
+ flexDirection: "row",
387
+ gap: 2,
388
+ padding: 2,
389
+ borderRadius: theme.radius.md,
390
+ backgroundColor: c["surface-2"],
391
+ }}
392
+ >
393
+ {thinking.efforts.map((effort) => {
394
+ const checked = effort === thinking.value;
395
+ const disabled = Boolean(props.disabled || thinking.disabled);
396
+ return (
397
+ <Pressable
398
+ key={effort}
399
+ accessibilityRole="radio"
400
+ accessibilityLabel={labelReasoningEffort(effort)}
401
+ accessibilityState={{ checked, disabled }}
402
+ disabled={disabled}
403
+ onPress={() => {
404
+ if (checked) return;
405
+ props.onEffortChange(effort);
406
+ props.onSelectionFeedback?.();
407
+ }}
408
+ style={{
409
+ flex: 1,
410
+ minHeight: 36,
411
+ alignItems: "center",
412
+ justifyContent: "center",
413
+ borderRadius: theme.radius.sm,
414
+ backgroundColor: checked ? c["surface-1"] : "transparent",
415
+ opacity: disabled ? 0.5 : 1,
416
+ ...(checked
417
+ ? {
418
+ shadowColor: "#000",
419
+ shadowOpacity: 0.06,
420
+ shadowRadius: 2,
421
+ shadowOffset: { width: 0, height: 1 },
422
+ elevation: 1,
423
+ }
424
+ : null),
425
+ }}
426
+ >
427
+ <Text
428
+ numberOfLines={1}
429
+ style={{
430
+ ...fontStyle(theme),
431
+ fontSize: 13,
432
+ color: checked ? c.fg : c["fg-muted"],
433
+ }}
434
+ >
435
+ {compactEffortLabel(effort, labelReasoningEffort(effort))}
436
+ </Text>
437
+ </Pressable>
438
+ );
439
+ })}
440
+ </View>
441
+ ) : null}
442
+ </>
443
+ ) : null}
444
+ </View>
445
+ ) : null}
446
+ </BottomSheet>
447
+ );
448
+ }
449
+
450
+ export type { ClientModel, ModelPickerGroupPresentation };