@nexub/react-native 0.0.0-stage → 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.
Files changed (124) hide show
  1. package/README.md +111 -2
  2. package/lib/module/components/actions.js +222 -0
  3. package/lib/module/components/actions.js.map +1 -0
  4. package/lib/module/components/containers.js +292 -0
  5. package/lib/module/components/containers.js.map +1 -0
  6. package/lib/module/components/display.js +266 -0
  7. package/lib/module/components/display.js.map +1 -0
  8. package/lib/module/components/feedback.js +464 -0
  9. package/lib/module/components/feedback.js.map +1 -0
  10. package/lib/module/components/inputs.js +746 -0
  11. package/lib/module/components/inputs.js.map +1 -0
  12. package/lib/module/components/mobile.js +811 -0
  13. package/lib/module/components/mobile.js.map +1 -0
  14. package/lib/module/components/overlays.js +418 -0
  15. package/lib/module/components/overlays.js.map +1 -0
  16. package/lib/module/components/shared.js +57 -0
  17. package/lib/module/components/shared.js.map +1 -0
  18. package/lib/module/hooks/useControllableState.js +23 -0
  19. package/lib/module/hooks/useControllableState.js.map +1 -0
  20. package/lib/module/index.js +30 -0
  21. package/lib/module/index.js.map +1 -0
  22. package/lib/module/package.json +1 -0
  23. package/lib/module/patterns/app.js +709 -0
  24. package/lib/module/patterns/app.js.map +1 -0
  25. package/lib/module/patterns/auth.js +224 -0
  26. package/lib/module/patterns/auth.js.map +1 -0
  27. package/lib/module/patterns/index.js +7 -0
  28. package/lib/module/patterns/index.js.map +1 -0
  29. package/lib/module/patterns/money.js +419 -0
  30. package/lib/module/patterns/money.js.map +1 -0
  31. package/lib/module/primitives/Icon.js +17 -0
  32. package/lib/module/primitives/Icon.js.map +1 -0
  33. package/lib/module/primitives/Layout.js +79 -0
  34. package/lib/module/primitives/Layout.js.map +1 -0
  35. package/lib/module/primitives/Pressable.js +77 -0
  36. package/lib/module/primitives/Pressable.js.map +1 -0
  37. package/lib/module/primitives/ScreenContainer.js +58 -0
  38. package/lib/module/primitives/ScreenContainer.js.map +1 -0
  39. package/lib/module/primitives/Surface.js +57 -0
  40. package/lib/module/primitives/Surface.js.map +1 -0
  41. package/lib/module/primitives/Text.js +70 -0
  42. package/lib/module/primitives/Text.js.map +1 -0
  43. package/lib/module/theme/ThemeProvider.js +127 -0
  44. package/lib/module/theme/ThemeProvider.js.map +1 -0
  45. package/lib/module/theme/fonts.js +56 -0
  46. package/lib/module/theme/fonts.js.map +1 -0
  47. package/lib/module/theme/haptics.js +56 -0
  48. package/lib/module/theme/haptics.js.map +1 -0
  49. package/lib/module/theme/navigation.js +99 -0
  50. package/lib/module/theme/navigation.js.map +1 -0
  51. package/lib/typescript/components/actions.d.ts +53 -0
  52. package/lib/typescript/components/actions.d.ts.map +1 -0
  53. package/lib/typescript/components/containers.d.ts +59 -0
  54. package/lib/typescript/components/containers.d.ts.map +1 -0
  55. package/lib/typescript/components/display.d.ts +62 -0
  56. package/lib/typescript/components/display.d.ts.map +1 -0
  57. package/lib/typescript/components/feedback.d.ts +93 -0
  58. package/lib/typescript/components/feedback.d.ts.map +1 -0
  59. package/lib/typescript/components/inputs.d.ts +141 -0
  60. package/lib/typescript/components/inputs.d.ts.map +1 -0
  61. package/lib/typescript/components/mobile.d.ts +173 -0
  62. package/lib/typescript/components/mobile.d.ts.map +1 -0
  63. package/lib/typescript/components/overlays.d.ts +74 -0
  64. package/lib/typescript/components/overlays.d.ts.map +1 -0
  65. package/lib/typescript/components/shared.d.ts +20 -0
  66. package/lib/typescript/components/shared.d.ts.map +1 -0
  67. package/lib/typescript/hooks/useControllableState.d.ts +6 -0
  68. package/lib/typescript/hooks/useControllableState.d.ts.map +1 -0
  69. package/lib/typescript/index.d.ts +37 -0
  70. package/lib/typescript/index.d.ts.map +1 -0
  71. package/lib/typescript/package.json +1 -0
  72. package/lib/typescript/patterns/app.d.ts +139 -0
  73. package/lib/typescript/patterns/app.d.ts.map +1 -0
  74. package/lib/typescript/patterns/auth.d.ts +54 -0
  75. package/lib/typescript/patterns/auth.d.ts.map +1 -0
  76. package/lib/typescript/patterns/index.d.ts +7 -0
  77. package/lib/typescript/patterns/index.d.ts.map +1 -0
  78. package/lib/typescript/patterns/money.d.ts +106 -0
  79. package/lib/typescript/patterns/money.d.ts.map +1 -0
  80. package/lib/typescript/primitives/Icon.d.ts +12 -0
  81. package/lib/typescript/primitives/Icon.d.ts.map +1 -0
  82. package/lib/typescript/primitives/Layout.d.ts +26 -0
  83. package/lib/typescript/primitives/Layout.d.ts.map +1 -0
  84. package/lib/typescript/primitives/Pressable.d.ts +21 -0
  85. package/lib/typescript/primitives/Pressable.d.ts.map +1 -0
  86. package/lib/typescript/primitives/ScreenContainer.d.ts +25 -0
  87. package/lib/typescript/primitives/ScreenContainer.d.ts.map +1 -0
  88. package/lib/typescript/primitives/Surface.d.ts +23 -0
  89. package/lib/typescript/primitives/Surface.d.ts.map +1 -0
  90. package/lib/typescript/primitives/Text.d.ts +21 -0
  91. package/lib/typescript/primitives/Text.d.ts.map +1 -0
  92. package/lib/typescript/theme/ThemeProvider.d.ts +69 -0
  93. package/lib/typescript/theme/ThemeProvider.d.ts.map +1 -0
  94. package/lib/typescript/theme/fonts.d.ts +25 -0
  95. package/lib/typescript/theme/fonts.d.ts.map +1 -0
  96. package/lib/typescript/theme/haptics.d.ts +3 -0
  97. package/lib/typescript/theme/haptics.d.ts.map +1 -0
  98. package/lib/typescript/theme/navigation.d.ts +158 -0
  99. package/lib/typescript/theme/navigation.d.ts.map +1 -0
  100. package/package.json +138 -3
  101. package/src/components/actions.tsx +259 -0
  102. package/src/components/containers.tsx +341 -0
  103. package/src/components/display.tsx +314 -0
  104. package/src/components/feedback.tsx +522 -0
  105. package/src/components/inputs.tsx +851 -0
  106. package/src/components/mobile.tsx +879 -0
  107. package/src/components/overlays.tsx +485 -0
  108. package/src/components/shared.ts +54 -0
  109. package/src/hooks/useControllableState.ts +27 -0
  110. package/src/index.ts +175 -0
  111. package/src/patterns/app.tsx +715 -0
  112. package/src/patterns/auth.tsx +266 -0
  113. package/src/patterns/index.ts +57 -0
  114. package/src/patterns/money.tsx +458 -0
  115. package/src/primitives/Icon.tsx +15 -0
  116. package/src/primitives/Layout.tsx +71 -0
  117. package/src/primitives/Pressable.tsx +94 -0
  118. package/src/primitives/ScreenContainer.tsx +65 -0
  119. package/src/primitives/Surface.tsx +75 -0
  120. package/src/primitives/Text.tsx +79 -0
  121. package/src/theme/ThemeProvider.tsx +210 -0
  122. package/src/theme/fonts.ts +52 -0
  123. package/src/theme/haptics.ts +57 -0
  124. package/src/theme/navigation.ts +68 -0
@@ -0,0 +1,709 @@
1
+ "use strict";
2
+
3
+ import * as React from 'react';
4
+ import { FlatList, KeyboardAvoidingView, Platform, TextInput, View } from 'react-native';
5
+ import { ArrowUp, Bell, Check, Sparkles } from '@nexub/icons/native';
6
+ import { Button, IconButton } from "../components/actions.js";
7
+ import { Card, ListItem, ListSection } from "../components/containers.js";
8
+ import { Avatar, Badge } from "../components/display.js";
9
+ import { EmptyState } from "../components/feedback.js";
10
+ import { SearchField, SegmentedControl, Chip } from "../components/inputs.js";
11
+ import { BottomSheet } from "../components/overlays.js";
12
+ import { Carousel, StickyFooter } from "../components/mobile.js";
13
+ import { Pressable } from "../primitives/Pressable.js";
14
+ import { ScreenContainer } from "../primitives/ScreenContainer.js";
15
+ import { Heading, Text } from "../primitives/Text.js";
16
+ import { fontStyle } from "../theme/fonts.js";
17
+ import { useTheme } from "../theme/ThemeProvider.js";
18
+
19
+ // ─── Onboarding ──────────────────────────────────────────────────────────────
20
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
21
+ /** Swipeable value slides + Get started / Sign in. */
22
+ export function OnboardingScreen({
23
+ slides,
24
+ onGetStarted,
25
+ onSignIn
26
+ }) {
27
+ const {
28
+ tokens
29
+ } = useTheme();
30
+ return /*#__PURE__*/_jsx(StickyFooter, {
31
+ footer: /*#__PURE__*/_jsxs(_Fragment, {
32
+ children: [/*#__PURE__*/_jsx(Button, {
33
+ size: "lg",
34
+ fullWidth: true,
35
+ onPress: onGetStarted,
36
+ children: "Get started"
37
+ }), onSignIn && /*#__PURE__*/_jsx(Button, {
38
+ size: "lg",
39
+ fullWidth: true,
40
+ variant: "text",
41
+ onPress: onSignIn,
42
+ children: "I already have an account"
43
+ })]
44
+ }),
45
+ children: /*#__PURE__*/_jsx(View, {
46
+ style: {
47
+ flex: 1,
48
+ justifyContent: 'center'
49
+ },
50
+ children: /*#__PURE__*/_jsx(Carousel, {
51
+ data: slides,
52
+ renderItem: ({
53
+ item
54
+ }) => /*#__PURE__*/_jsxs(View, {
55
+ style: {
56
+ padding: tokens.spacing[6],
57
+ gap: tokens.spacing[4],
58
+ alignItems: 'center'
59
+ },
60
+ children: [item.art, /*#__PURE__*/_jsx(Heading, {
61
+ variant: "headline-md",
62
+ style: {
63
+ textAlign: 'center'
64
+ },
65
+ children: item.title
66
+ }), /*#__PURE__*/_jsx(Text, {
67
+ variant: "body-lg",
68
+ color: "on-surface-variant",
69
+ style: {
70
+ textAlign: 'center'
71
+ },
72
+ children: item.body
73
+ })]
74
+ })
75
+ })
76
+ })
77
+ });
78
+ }
79
+ /** Explain a permission before the one-shot OS prompt (notifications, camera, location). */
80
+ export function PermissionPrimer({
81
+ icon: Glyph = Bell,
82
+ title,
83
+ body,
84
+ onAllow,
85
+ onNotNow
86
+ }) {
87
+ const {
88
+ roles,
89
+ tokens
90
+ } = useTheme();
91
+ return /*#__PURE__*/_jsx(StickyFooter, {
92
+ footer: /*#__PURE__*/_jsxs(_Fragment, {
93
+ children: [/*#__PURE__*/_jsx(Button, {
94
+ size: "lg",
95
+ fullWidth: true,
96
+ onPress: onAllow,
97
+ children: "Continue"
98
+ }), /*#__PURE__*/_jsx(Button, {
99
+ size: "lg",
100
+ fullWidth: true,
101
+ variant: "text",
102
+ onPress: onNotNow,
103
+ children: "Not now"
104
+ })]
105
+ }),
106
+ children: /*#__PURE__*/_jsxs(View, {
107
+ style: {
108
+ flex: 1,
109
+ alignItems: 'center',
110
+ justifyContent: 'center',
111
+ gap: tokens.spacing[4],
112
+ padding: tokens.spacing[6]
113
+ },
114
+ children: [/*#__PURE__*/_jsx(View, {
115
+ style: {
116
+ width: 88,
117
+ height: 88,
118
+ borderRadius: 44,
119
+ alignItems: 'center',
120
+ justifyContent: 'center',
121
+ backgroundColor: roles['primary-container']
122
+ },
123
+ children: /*#__PURE__*/_jsx(Glyph, {
124
+ size: 44,
125
+ color: roles['on-primary-container']
126
+ })
127
+ }), /*#__PURE__*/_jsx(Heading, {
128
+ variant: "headline-sm",
129
+ style: {
130
+ textAlign: 'center'
131
+ },
132
+ children: title
133
+ }), /*#__PURE__*/_jsx(Text, {
134
+ variant: "body-md",
135
+ color: "on-surface-variant",
136
+ style: {
137
+ textAlign: 'center'
138
+ },
139
+ children: body
140
+ })]
141
+ })
142
+ });
143
+ }
144
+
145
+ // ─── Settings & profile ──────────────────────────────────────────────────────
146
+
147
+ /** Grouped settings (web SettingsLayout + SettingsSection). */
148
+ export function SettingsScreen({
149
+ header,
150
+ groups
151
+ }) {
152
+ return /*#__PURE__*/_jsxs(ScreenContainer, {
153
+ gap: "6",
154
+ children: [header, groups.map((g, i) => /*#__PURE__*/_jsx(ListSection, {
155
+ header: g.header,
156
+ footer: g.footer,
157
+ inset: g.items.some(it => it.leading) ? 56 : 16,
158
+ children: g.items.map(it => /*#__PURE__*/_jsx(ListItem, {
159
+ ...it
160
+ }, it.title))
161
+ }, g.header ?? i))]
162
+ });
163
+ }
164
+ /** Theme, glass intensity dial and haptics: the user preferences NexubNativeProvider takes. */
165
+ export function AppearanceSection({
166
+ scheme,
167
+ onSchemeChange,
168
+ glass,
169
+ onGlassChange,
170
+ haptics,
171
+ onHapticsChange
172
+ }) {
173
+ const {
174
+ tokens,
175
+ messages
176
+ } = useTheme();
177
+ return /*#__PURE__*/_jsxs(ListSection, {
178
+ header: "Appearance",
179
+ children: [/*#__PURE__*/_jsxs(View, {
180
+ style: {
181
+ padding: tokens.spacing[4],
182
+ gap: tokens.spacing[3]
183
+ },
184
+ children: [/*#__PURE__*/_jsx(SegmentedControl, {
185
+ label: "Theme",
186
+ value: scheme,
187
+ onValueChange: onSchemeChange,
188
+ options: [{
189
+ value: 'system',
190
+ label: 'System'
191
+ }, {
192
+ value: 'light',
193
+ label: 'Light'
194
+ }, {
195
+ value: 'dark',
196
+ label: 'Dark'
197
+ }]
198
+ }), /*#__PURE__*/_jsx(SegmentedControl, {
199
+ label: messages.glass,
200
+ value: glass,
201
+ onValueChange: onGlassChange,
202
+ options: [{
203
+ value: 'clear',
204
+ label: messages.glassClear
205
+ }, {
206
+ value: 'balanced',
207
+ label: messages.glassBalanced
208
+ }, {
209
+ value: 'tinted',
210
+ label: messages.glassTinted
211
+ }, {
212
+ value: 'off',
213
+ label: messages.glassSolid
214
+ }]
215
+ })]
216
+ }), /*#__PURE__*/_jsx(ListItem, {
217
+ variant: "switch",
218
+ title: "Haptics",
219
+ subtitle: "Vibration feedback on selections",
220
+ checked: haptics,
221
+ onCheckedChange: onHapticsChange
222
+ })]
223
+ });
224
+ }
225
+ /** Account card at the top of Settings / Profile (web UserCard). */
226
+ export function ProfileHeader({
227
+ name,
228
+ detail,
229
+ image,
230
+ onPress
231
+ }) {
232
+ const {
233
+ tokens
234
+ } = useTheme();
235
+ return /*#__PURE__*/_jsxs(Card, {
236
+ onPress: onPress,
237
+ accessibilityLabel: [name, detail].filter(Boolean).join(', '),
238
+ style: {
239
+ flexDirection: 'row',
240
+ alignItems: 'center',
241
+ gap: tokens.spacing[4]
242
+ },
243
+ children: [/*#__PURE__*/_jsx(Avatar, {
244
+ name: name,
245
+ src: image,
246
+ size: "lg"
247
+ }), /*#__PURE__*/_jsxs(View, {
248
+ style: {
249
+ flex: 1,
250
+ gap: 2
251
+ },
252
+ children: [/*#__PURE__*/_jsx(Text, {
253
+ variant: "title-lg",
254
+ children: name
255
+ }), detail && /*#__PURE__*/_jsx(Text, {
256
+ variant: "body-sm",
257
+ color: "on-surface-variant",
258
+ children: detail
259
+ })]
260
+ })]
261
+ });
262
+ }
263
+
264
+ // ─── Notifications ───────────────────────────────────────────────────────────
265
+
266
+ /** Inbox list with unread dots (web NotificationItem). */
267
+ export function NotificationsInbox({
268
+ notifications,
269
+ onPress,
270
+ onMarkAllRead
271
+ }) {
272
+ const {
273
+ roles,
274
+ tokens
275
+ } = useTheme();
276
+ const unread = notifications.filter(n => n.unread).length;
277
+ return /*#__PURE__*/_jsxs(ScreenContainer, {
278
+ children: [unread > 0 && onMarkAllRead && /*#__PURE__*/_jsx(Button, {
279
+ variant: "text",
280
+ size: "sm",
281
+ leadingIcon: Check,
282
+ onPress: onMarkAllRead,
283
+ style: {
284
+ alignSelf: 'flex-end'
285
+ },
286
+ children: "Mark all as read"
287
+ }), notifications.length === 0 ? /*#__PURE__*/_jsx(EmptyState, {
288
+ icon: Bell,
289
+ title: "You\u2019re all caught up"
290
+ }) : /*#__PURE__*/_jsx(ListSection, {
291
+ inset: 56,
292
+ children: notifications.map(n => {
293
+ const Glyph = n.icon ?? Bell;
294
+ return /*#__PURE__*/_jsx(ListItem, {
295
+ title: n.title,
296
+ subtitle: [n.body, n.time].filter(Boolean).join(' · '),
297
+ leading: /*#__PURE__*/_jsxs(View, {
298
+ children: [/*#__PURE__*/_jsx(Glyph, {
299
+ size: "md",
300
+ color: roles['on-surface-variant']
301
+ }), n.unread && /*#__PURE__*/_jsx(View, {
302
+ style: {
303
+ position: 'absolute',
304
+ top: -2,
305
+ end: -2,
306
+ width: 8,
307
+ height: 8,
308
+ borderRadius: 4,
309
+ backgroundColor: roles.primary
310
+ }
311
+ })]
312
+ }),
313
+ trailing: n.unread ? /*#__PURE__*/_jsx(Badge, {
314
+ tone: "info",
315
+ size: "sm",
316
+ children: "New"
317
+ }) : null,
318
+ onPress: onPress && (() => onPress(n)),
319
+ variant: onPress ? 'button' : 'static'
320
+ }, n.id);
321
+ })
322
+ }), /*#__PURE__*/_jsx(View, {
323
+ style: {
324
+ height: tokens.spacing[4]
325
+ }
326
+ })]
327
+ });
328
+ }
329
+
330
+ // ─── Search ──────────────────────────────────────────────────────────────────
331
+
332
+ /** Search field, recent searches, results and a no-results state. */
333
+ export function SearchScreen({
334
+ query,
335
+ onQueryChange,
336
+ results,
337
+ renderResult,
338
+ keyOf,
339
+ recent = [],
340
+ placeholder
341
+ }) {
342
+ const {
343
+ tokens,
344
+ messages
345
+ } = useTheme();
346
+ return /*#__PURE__*/_jsxs(ScreenContainer, {
347
+ scroll: false,
348
+ children: [/*#__PURE__*/_jsx(SearchField, {
349
+ value: query,
350
+ onChangeText: onQueryChange,
351
+ placeholder: placeholder,
352
+ autoFocus: true
353
+ }), !query && recent.length > 0 && /*#__PURE__*/_jsxs(View, {
354
+ style: {
355
+ gap: tokens.spacing[2]
356
+ },
357
+ children: [/*#__PURE__*/_jsx(Text, {
358
+ variant: "title-sm",
359
+ color: "on-surface-variant",
360
+ accessibilityRole: "header",
361
+ children: "Recent"
362
+ }), /*#__PURE__*/_jsx(View, {
363
+ style: {
364
+ flexDirection: 'row',
365
+ flexWrap: 'wrap',
366
+ gap: tokens.spacing[2]
367
+ },
368
+ children: recent.map(r => /*#__PURE__*/_jsx(Chip, {
369
+ label: r,
370
+ onPress: () => onQueryChange(r)
371
+ }, r))
372
+ })]
373
+ }), !!query && /*#__PURE__*/_jsx(FlatList, {
374
+ data: results,
375
+ keyExtractor: keyOf,
376
+ renderItem: ({
377
+ item
378
+ }) => /*#__PURE__*/_jsx(_Fragment, {
379
+ children: renderResult(item)
380
+ }),
381
+ keyboardDismissMode: "on-drag",
382
+ ListEmptyComponent: /*#__PURE__*/_jsx(EmptyState, {
383
+ title: messages.noResultsFor(query)
384
+ })
385
+ })]
386
+ });
387
+ }
388
+
389
+ // ─── Chat / AI assistant ─────────────────────────────────────────────────────
390
+
391
+ /** Conversation with a composer above the keyboard (web Conversation + PromptInput + MessageBubble). */
392
+ export function ChatScreen({
393
+ messages: list,
394
+ onSend,
395
+ thinking,
396
+ placeholder,
397
+ empty
398
+ }) {
399
+ const {
400
+ roles,
401
+ tokens,
402
+ fonts,
403
+ messages
404
+ } = useTheme();
405
+ const [text, setText] = React.useState('');
406
+ const send = () => {
407
+ const t = text.trim();
408
+ if (!t) return;
409
+ onSend(t);
410
+ setText('');
411
+ };
412
+ return /*#__PURE__*/_jsxs(KeyboardAvoidingView, {
413
+ behavior: Platform.OS === 'ios' ? 'padding' : 'height',
414
+ keyboardVerticalOffset: Platform.OS === 'ios' ? 100 : 0,
415
+ style: {
416
+ flex: 1,
417
+ backgroundColor: roles.surface
418
+ },
419
+ children: [/*#__PURE__*/_jsx(FlatList, {
420
+ data: [...list].reverse(),
421
+ inverted: true,
422
+ keyExtractor: m => m.id,
423
+ contentContainerStyle: {
424
+ padding: tokens.spacing[4],
425
+ gap: tokens.spacing[3],
426
+ flexGrow: 1
427
+ },
428
+ ListHeaderComponent: thinking ? /*#__PURE__*/_jsx(ThinkingDots, {}) : undefined,
429
+ ListEmptyComponent: /*#__PURE__*/_jsx(View, {
430
+ style: {
431
+ flex: 1,
432
+ transform: [{
433
+ scaleY: -1
434
+ }]
435
+ },
436
+ children: empty ?? /*#__PURE__*/_jsx(EmptyState, {
437
+ icon: Sparkles,
438
+ title: "Ask anything"
439
+ })
440
+ }),
441
+ renderItem: ({
442
+ item
443
+ }) => {
444
+ const mine = item.role === 'user';
445
+ return /*#__PURE__*/_jsx(View, {
446
+ accessible: true,
447
+ accessibilityLabel: `${mine ? messages.youSaid : messages.assistantSaid}: ${item.text}`,
448
+ style: {
449
+ alignSelf: mine ? 'flex-end' : 'flex-start',
450
+ maxWidth: '85%',
451
+ paddingHorizontal: tokens.spacing[4],
452
+ paddingVertical: tokens.spacing[3],
453
+ borderRadius: tokens.shape.container,
454
+ borderBottomEndRadius: mine ? tokens.shape.min : tokens.shape.container,
455
+ borderBottomStartRadius: mine ? tokens.shape.container : tokens.shape.min,
456
+ backgroundColor: roles[mine ? 'primary' : 'surface-container-high']
457
+ },
458
+ children: /*#__PURE__*/_jsxs(Text, {
459
+ color: mine ? 'on-primary' : 'on-surface',
460
+ children: [item.text, item.streaming ? ' ▍' : '']
461
+ })
462
+ });
463
+ }
464
+ }), /*#__PURE__*/_jsxs(View, {
465
+ style: {
466
+ flexDirection: 'row',
467
+ alignItems: 'flex-end',
468
+ gap: tokens.spacing[2],
469
+ padding: tokens.spacing[3],
470
+ borderTopWidth: 1,
471
+ borderTopColor: roles['outline-variant'],
472
+ backgroundColor: roles.surface
473
+ },
474
+ children: [/*#__PURE__*/_jsx(TextInput, {
475
+ accessibilityLabel: messages.promptLabel,
476
+ value: text,
477
+ onChangeText: setText,
478
+ placeholder: placeholder ?? messages.promptLabel,
479
+ placeholderTextColor: roles['on-surface-muted'],
480
+ multiline: true,
481
+ style: {
482
+ flex: 1,
483
+ minHeight: 44,
484
+ maxHeight: 140,
485
+ paddingHorizontal: tokens.spacing[4],
486
+ paddingTop: 12,
487
+ paddingBottom: 12,
488
+ borderRadius: 22,
489
+ backgroundColor: roles['surface-container-high'],
490
+ color: roles['on-surface'],
491
+ fontSize: tokens.type['body-md'].size,
492
+ ...fontStyle('sans', 400, fonts)
493
+ }
494
+ }), /*#__PURE__*/_jsx(IconButton, {
495
+ icon: ArrowUp,
496
+ label: messages.sendMessage,
497
+ variant: "filled",
498
+ disabled: !text.trim(),
499
+ onPress: send
500
+ })]
501
+ })]
502
+ });
503
+ }
504
+ function ThinkingDots() {
505
+ const {
506
+ roles,
507
+ messages
508
+ } = useTheme();
509
+ return /*#__PURE__*/_jsx(View, {
510
+ accessible: true,
511
+ accessibilityLabel: messages.thinking,
512
+ accessibilityLiveRegion: "polite",
513
+ style: {
514
+ flexDirection: 'row',
515
+ gap: 4,
516
+ padding: 12,
517
+ alignSelf: 'flex-start'
518
+ },
519
+ children: [0, 1, 2].map(i => /*#__PURE__*/_jsx(View, {
520
+ style: {
521
+ width: 8,
522
+ height: 8,
523
+ borderRadius: 4,
524
+ backgroundColor: roles['on-surface-variant'],
525
+ opacity: 0.4 + i * 0.2
526
+ }
527
+ }, i))
528
+ });
529
+ }
530
+
531
+ // ─── Paywall ─────────────────────────────────────────────────────────────────
532
+
533
+ /** Plan choice + subscribe (web PlanCard). */
534
+ export function PaywallScreen({
535
+ title,
536
+ plans,
537
+ defaultPlan,
538
+ onSubscribe,
539
+ onRestore,
540
+ terms
541
+ }) {
542
+ const {
543
+ roles,
544
+ tokens
545
+ } = useTheme();
546
+ const [plan, setPlan] = React.useState(defaultPlan ?? plans[0]?.id);
547
+ return /*#__PURE__*/_jsx(StickyFooter, {
548
+ footer: /*#__PURE__*/_jsxs(_Fragment, {
549
+ children: [/*#__PURE__*/_jsx(Button, {
550
+ size: "lg",
551
+ fullWidth: true,
552
+ onPress: () => plan && onSubscribe(plan),
553
+ children: "Continue"
554
+ }), onRestore && /*#__PURE__*/_jsx(Button, {
555
+ size: "lg",
556
+ fullWidth: true,
557
+ variant: "text",
558
+ onPress: onRestore,
559
+ children: "Restore purchases"
560
+ }), terms && /*#__PURE__*/_jsx(Text, {
561
+ variant: "label-sm",
562
+ color: "on-surface-variant",
563
+ style: {
564
+ textAlign: 'center'
565
+ },
566
+ children: terms
567
+ })]
568
+ }),
569
+ children: /*#__PURE__*/_jsxs(ScreenContainer, {
570
+ children: [/*#__PURE__*/_jsx(Heading, {
571
+ variant: "headline-md",
572
+ children: title
573
+ }), /*#__PURE__*/_jsx(View, {
574
+ accessibilityRole: "radiogroup",
575
+ style: {
576
+ gap: tokens.spacing[3]
577
+ },
578
+ children: plans.map(p => {
579
+ const on = p.id === plan;
580
+ return /*#__PURE__*/_jsxs(Pressable, {
581
+ accessibilityRole: "radio",
582
+ accessibilityLabel: `${p.name}, ${p.price}`,
583
+ accessibilityState: {
584
+ checked: on
585
+ },
586
+ haptic: on ? undefined : 'selection',
587
+ onPress: () => setPlan(p.id),
588
+ pressScale: 0.98,
589
+ style: {
590
+ padding: tokens.spacing[4],
591
+ gap: tokens.spacing[2],
592
+ borderRadius: tokens.shape.container,
593
+ borderWidth: on ? 2 : 1,
594
+ borderColor: on ? roles.primary : roles['outline-variant'],
595
+ backgroundColor: roles['surface-container-lowest']
596
+ },
597
+ children: [/*#__PURE__*/_jsxs(View, {
598
+ style: {
599
+ flexDirection: 'row',
600
+ alignItems: 'center',
601
+ gap: tokens.spacing[2]
602
+ },
603
+ children: [/*#__PURE__*/_jsx(Text, {
604
+ variant: "title-md",
605
+ style: {
606
+ flex: 1
607
+ },
608
+ children: p.name
609
+ }), p.badge && /*#__PURE__*/_jsx(Badge, {
610
+ tone: "info",
611
+ children: p.badge
612
+ })]
613
+ }), /*#__PURE__*/_jsx(Text, {
614
+ variant: "title-lg",
615
+ tabular: true,
616
+ children: p.price
617
+ }), p.features.map(f => /*#__PURE__*/_jsxs(View, {
618
+ style: {
619
+ flexDirection: 'row',
620
+ gap: tokens.spacing[2]
621
+ },
622
+ children: [/*#__PURE__*/_jsx(Check, {
623
+ size: "sm",
624
+ color: roles.success
625
+ }), /*#__PURE__*/_jsx(Text, {
626
+ variant: "body-sm",
627
+ color: "on-surface-variant",
628
+ style: {
629
+ flex: 1
630
+ },
631
+ children: f
632
+ })]
633
+ }, f))]
634
+ }, p.id);
635
+ })
636
+ })]
637
+ })
638
+ });
639
+ }
640
+
641
+ // ─── App switcher ────────────────────────────────────────────────────────────
642
+
643
+ /** Nexub apps in a sheet (web AppLauncher). */
644
+ export function AppSwitcherSheet({
645
+ open,
646
+ onOpenChange,
647
+ apps,
648
+ current,
649
+ onOpenApp
650
+ }) {
651
+ const {
652
+ roles,
653
+ tokens
654
+ } = useTheme();
655
+ return /*#__PURE__*/_jsx(BottomSheet, {
656
+ open: open,
657
+ onOpenChange: onOpenChange,
658
+ title: "Nexub apps",
659
+ children: /*#__PURE__*/_jsx(View, {
660
+ style: {
661
+ flexDirection: 'row',
662
+ flexWrap: 'wrap',
663
+ rowGap: tokens.spacing[4]
664
+ },
665
+ children: apps.map(a => {
666
+ const tone = a.tone ?? 'primary';
667
+ return /*#__PURE__*/_jsxs(Pressable, {
668
+ accessibilityLabel: a.name,
669
+ accessibilityState: {
670
+ selected: a.id === current
671
+ },
672
+ onPress: () => {
673
+ onOpenChange(false);
674
+ onOpenApp(a);
675
+ },
676
+ stateLayer: false,
677
+ style: {
678
+ width: '25%',
679
+ alignItems: 'center',
680
+ gap: 6
681
+ },
682
+ children: [/*#__PURE__*/_jsx(View, {
683
+ style: {
684
+ width: 56,
685
+ height: 56,
686
+ borderRadius: 14,
687
+ overflow: 'hidden',
688
+ alignItems: 'center',
689
+ justifyContent: 'center',
690
+ backgroundColor: roles[`${tone}-container`],
691
+ borderWidth: a.id === current ? 2 : 0,
692
+ borderColor: roles.primary
693
+ },
694
+ children: a.icon ?? /*#__PURE__*/_jsx(Text, {
695
+ variant: "title-md",
696
+ color: `on-${tone}-container`,
697
+ children: a.name.slice(0, 2)
698
+ })
699
+ }), /*#__PURE__*/_jsx(Text, {
700
+ variant: "label-md",
701
+ numberOfLines: 1,
702
+ children: a.name
703
+ })]
704
+ }, a.id);
705
+ })
706
+ })
707
+ });
708
+ }
709
+ //# sourceMappingURL=app.js.map