@vvedantb/native-ui 0.3.17 → 0.5.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 (43) hide show
  1. package/README.md +146 -13
  2. package/package.json +10 -2
  3. package/src/CaptureButton.tsx +62 -0
  4. package/src/Card.tsx +37 -13
  5. package/src/ColorSwatches.tsx +133 -0
  6. package/src/DateTimeField.tsx +75 -19
  7. package/src/HeaderIconButton.tsx +18 -1
  8. package/src/IconButton.tsx +87 -0
  9. package/src/ListRow.tsx +104 -30
  10. package/src/PageIndicator.tsx +87 -0
  11. package/src/SectionLabel.tsx +48 -0
  12. package/src/SheetRow.tsx +89 -0
  13. package/src/StackPageHeader.tsx +100 -0
  14. package/src/StatGrid.tsx +80 -0
  15. package/src/StatusDot.tsx +14 -1
  16. package/src/ThemeToggle.tsx +17 -5
  17. package/src/ai/activity-step.tsx +57 -0
  18. package/src/ai/attachments.tsx +153 -0
  19. package/src/ai/chain-of-thought.tsx +143 -0
  20. package/src/ai/chat-text.ts +35 -0
  21. package/src/ai/code-block.tsx +104 -0
  22. package/src/ai/collapsible.tsx +111 -0
  23. package/src/ai/conversation.tsx +245 -0
  24. package/src/ai/file-chip.tsx +66 -0
  25. package/src/ai/icon-button.tsx +51 -0
  26. package/src/ai/index.ts +123 -0
  27. package/src/ai/message.tsx +146 -0
  28. package/src/ai/prompt-input.tsx +186 -0
  29. package/src/ai/reasoning.tsx +200 -0
  30. package/src/ai/response-text.tsx +56 -0
  31. package/src/ai/shimmer.tsx +86 -0
  32. package/src/ai/sources.tsx +100 -0
  33. package/src/ai/suggestion.tsx +93 -0
  34. package/src/ai/trace.tsx +234 -0
  35. package/src/ai/use-streaming-disclosure.ts +50 -0
  36. package/src/entries/ai.ts +4 -0
  37. package/src/entries/date-time.ts +1 -0
  38. package/src/entries/layout.ts +6 -2
  39. package/src/entries/page-title.ts +7 -2
  40. package/src/entries/primitives.ts +19 -1
  41. package/src/examples/AiChatGallery.tsx +233 -0
  42. package/src/examples/ParityGallery.tsx +205 -3
  43. package/src/index.ts +19 -0
@@ -0,0 +1,233 @@
1
+ // Reference screen for the AI chat parts (`@vvedantb/native-ui/ai`). Render
2
+ // `AiChatGallery` in a full-screen Expo route, with the same NativeWind setup
3
+ // as the rest of the package and a `SafeAreaProvider` above it. The thread is
4
+ // fixed sample text, not a model. It follows the app's larger-text setting.
5
+ // `ParityGallery` shows `AiChatSamples` in its "AI chat" section.
6
+ import { useState } from "react";
7
+ import { View } from "react-native";
8
+ import { IconFileText, IconPaperclip, IconSearch } from "@tabler/icons-react-native";
9
+ import {
10
+ ActivityStep,
11
+ AgentTrace,
12
+ AgentTraceCollapsible,
13
+ AgentTraceCollapsibleContent,
14
+ AgentTraceCollapsibleTrigger,
15
+ AgentTraceDescription,
16
+ AgentTraceIcon,
17
+ AgentTraceLine,
18
+ AgentTraceTitle,
19
+ Attachment,
20
+ Attachments,
21
+ CodeBlock,
22
+ Conversation,
23
+ ConversationContent,
24
+ ConversationScrollButton,
25
+ FileChip,
26
+ Message,
27
+ MessageAction,
28
+ MessageActionsBar,
29
+ MessageContent,
30
+ PromptInput,
31
+ PromptInputButton,
32
+ Reasoning,
33
+ ReasoningContent,
34
+ ReasoningTrigger,
35
+ ResponseText,
36
+ Source,
37
+ Sources,
38
+ SourcesContent,
39
+ SourcesTrigger,
40
+ Suggestion,
41
+ Suggestions,
42
+ formatElapsed,
43
+ useStreamingDisclosure,
44
+ type AttachmentData,
45
+ type ChatStatus,
46
+ } from "../ai";
47
+
48
+ const SUGGESTIONS = [
49
+ "Summarise this thread",
50
+ "Draft a reply",
51
+ "List the next steps",
52
+ "Explain it more simply",
53
+ ];
54
+
55
+ const SAMPLE_ATTACHMENTS: AttachmentData[] = [
56
+ { id: "notes", filename: "meeting-notes.pdf", mediaType: "application/pdf" },
57
+ { id: "sketch", filename: "sketch.png", mediaType: "image/png" },
58
+ ];
59
+
60
+ const FINISHED_REASONING =
61
+ "The question is how to keep one list the same on two devices.\nCompare a shared server copy with device-to-device sync, then suggest the simpler one.";
62
+
63
+ const FINISHED_REPLY =
64
+ "Keep one copy of the list on a server and have each device read and write it.\n\nWhen a device changes an item, it sends the change with a timestamp. The other device fetches changes it has not seen.";
65
+
66
+ const SAMPLE_CODE = `type Change = {\n itemId: string;\n updatedAt: number;\n};`;
67
+
68
+ const STREAMING_REASONING =
69
+ "Offline edits need to wait in a queue on the device.\nWhen the connection returns, send the queue in order and merge by timestamp…";
70
+
71
+ /**
72
+ * The sample thread: a user message, a finished assistant message (folded
73
+ * reasoning and trace, reply, code, sources and actions), a second user
74
+ * message, and an assistant message that is still working while `streaming`.
75
+ */
76
+ export function AiChatSamples({ streaming = true }: { streaming?: boolean }) {
77
+ const [feedback, setFeedback] = useState<"like" | "dislike" | null>(null);
78
+ const searchFold = useStreamingDisclosure(streaming);
79
+ const toggle = (kind: "like" | "dislike") => setFeedback(feedback === kind ? null : kind);
80
+
81
+ return (
82
+ <>
83
+ <Message from="user">
84
+ <MessageContent>How do I keep a list in sync across two devices?</MessageContent>
85
+ </Message>
86
+
87
+ <Message from="assistant">
88
+ <MessageContent>
89
+ <Reasoning duration={8}>
90
+ <ReasoningTrigger />
91
+ <ReasoningContent>{FINISHED_REASONING}</ReasoningContent>
92
+ </Reasoning>
93
+ <AgentTraceCollapsible>
94
+ <AgentTraceCollapsibleTrigger>
95
+ <AgentTraceLine>
96
+ <AgentTraceIcon icon={IconSearch} />
97
+ <AgentTraceTitle>Used 2 tools</AgentTraceTitle>
98
+ <AgentTraceDescription>{`Worked for ${formatElapsed(14)}`}</AgentTraceDescription>
99
+ </AgentTraceLine>
100
+ </AgentTraceCollapsibleTrigger>
101
+ <AgentTraceCollapsibleContent>
102
+ <AgentTrace>
103
+ <ActivityStep icon={IconSearch} label="Searched the web" description="5 results" />
104
+ <AgentTraceLine>
105
+ <AgentTraceIcon icon={IconFileText} />
106
+ <AgentTraceTitle>Read</AgentTraceTitle>
107
+ <FileChip name="sync-notes.md" />
108
+ </AgentTraceLine>
109
+ </AgentTrace>
110
+ </AgentTraceCollapsibleContent>
111
+ </AgentTraceCollapsible>
112
+ <ResponseText>{FINISHED_REPLY}</ResponseText>
113
+ {/* The gallery has no clipboard: the button only shows its copied state. */}
114
+ <CodeBlock language="ts" code={SAMPLE_CODE} onCopy={() => undefined} />
115
+ <Sources>
116
+ <SourcesTrigger count={2} />
117
+ <SourcesContent>
118
+ <Source href="https://example.com/sync" title="Syncing data between devices" />
119
+ <Source href="https://example.com/timestamps" title="Merging changes by timestamp" />
120
+ </SourcesContent>
121
+ </Sources>
122
+ </MessageContent>
123
+ <MessageActionsBar>
124
+ <MessageAction kind="copy" />
125
+ <MessageAction kind="retry" />
126
+ <MessageAction kind="like" selected={feedback === "like"} onPress={() => toggle("like")} />
127
+ <MessageAction kind="dislike" selected={feedback === "dislike"} onPress={() => toggle("dislike")} />
128
+ </MessageActionsBar>
129
+ </Message>
130
+
131
+ <Message from="user">
132
+ <MessageContent>And when one device is offline?</MessageContent>
133
+ </Message>
134
+
135
+ <Message from="assistant">
136
+ <MessageContent>
137
+ <Reasoning isStreaming={streaming}>
138
+ <ReasoningTrigger />
139
+ <ReasoningContent>{STREAMING_REASONING}</ReasoningContent>
140
+ </Reasoning>
141
+ <AgentTraceCollapsible {...searchFold}>
142
+ <AgentTraceCollapsibleTrigger>
143
+ <AgentTraceLine>
144
+ <AgentTraceIcon icon={IconSearch} />
145
+ <AgentTraceTitle shimmer={streaming}>
146
+ {streaming ? "Searching for offline sync" : "Searched for offline sync"}
147
+ </AgentTraceTitle>
148
+ </AgentTraceLine>
149
+ </AgentTraceCollapsibleTrigger>
150
+ <AgentTraceCollapsibleContent>
151
+ Offline-first sync, change queues, conflict handling
152
+ </AgentTraceCollapsibleContent>
153
+ </AgentTraceCollapsible>
154
+ <ActivityStep
155
+ icon={IconFileText}
156
+ label={streaming ? "Reading sync-notes.md" : "Read sync-notes.md"}
157
+ status={streaming ? "running" : "complete"}
158
+ description={streaming ? undefined : `Worked for ${formatElapsed(6)}`}
159
+ />
160
+ {streaming ? null : (
161
+ <ResponseText>
162
+ {"Queue each change on the device while it is offline.\n\nWhen it reconnects, send the queue in order. If both devices changed the same item, keep the newer change."}
163
+ </ResponseText>
164
+ )}
165
+ </MessageContent>
166
+ </Message>
167
+ </>
168
+ );
169
+ }
170
+
171
+ /**
172
+ * A full chat screen: the sample thread in a `Conversation` with its scroll
173
+ * button, suggestions, and the composer with sample attachments and an
174
+ * attach tool. The last message streams until Stop is pressed; then sent
175
+ * messages are added to the thread.
176
+ */
177
+ export function AiChatGallery() {
178
+ const [status, setStatus] = useState<ChatStatus>("streaming");
179
+ const [draft, setDraft] = useState("");
180
+ const [sent, setSent] = useState<string[]>([]);
181
+ const [attachments, setAttachments] = useState(SAMPLE_ATTACHMENTS);
182
+
183
+ return (
184
+ <View className="flex-1">
185
+ <Conversation>
186
+ <ConversationContent>
187
+ <AiChatSamples streaming={status === "streaming"} />
188
+ {sent.map((text, index) => (
189
+ <Message key={`${index}-${text}`} from="user">
190
+ <MessageContent>{text}</MessageContent>
191
+ </Message>
192
+ ))}
193
+ </ConversationContent>
194
+ <ConversationScrollButton />
195
+ </Conversation>
196
+ <Suggestions className="pt-2" contentContainerClassName="px-3">
197
+ {SUGGESTIONS.map((suggestion) => (
198
+ <Suggestion key={suggestion} suggestion={suggestion} onPress={setDraft} />
199
+ ))}
200
+ </Suggestions>
201
+ <PromptInput
202
+ value={draft}
203
+ onChangeText={setDraft}
204
+ status={status}
205
+ onStop={() => setStatus("ready")}
206
+ onSubmit={(text) => {
207
+ setSent([...sent, text]);
208
+ setAttachments([]);
209
+ }}
210
+ header={
211
+ attachments.length > 0 ? (
212
+ <Attachments>
213
+ {attachments.map((attachment) => (
214
+ <Attachment
215
+ key={attachment.id}
216
+ data={attachment}
217
+ onRemove={() => setAttachments(attachments.filter((item) => item.id !== attachment.id))}
218
+ />
219
+ ))}
220
+ </Attachments>
221
+ ) : null
222
+ }
223
+ toolbar={
224
+ <PromptInputButton
225
+ icon={IconPaperclip}
226
+ label="Attach files"
227
+ onPress={() => setAttachments(SAMPLE_ATTACHMENTS)}
228
+ />
229
+ }
230
+ />
231
+ </View>
232
+ );
233
+ }
@@ -1,11 +1,18 @@
1
1
  // Reference screen for the components that mirror `@vvedantb/ui`. Render it in
2
2
  // an Expo route to compare against the web: it needs the same NativeWind setup
3
3
  // as the rest of the package and a `SafeAreaProvider` above it. It always
4
- // renders the default sizes (larger text off), as the web does. Prices below
5
- // are placeholders, not real plan prices.
4
+ // renders the default sizes (larger text off), as the web does. Prices, names
5
+ // and figures below are placeholders, not real data.
6
6
  import { useState, type ReactNode } from "react";
7
7
  import { ScrollView, Text, View, type TextProps } from "react-native";
8
- import { IconInbox } from "@tabler/icons-react-native";
8
+ import {
9
+ IconCopy,
10
+ IconInbox,
11
+ IconPhoto,
12
+ IconRefresh,
13
+ IconTrash,
14
+ IconX,
15
+ } from "@tabler/icons-react-native";
9
16
  import {
10
17
  APP_ICONS,
11
18
  APP_NAMES,
@@ -18,12 +25,14 @@ import {
18
25
  AlertDialogHeader,
19
26
  AlertDialogTitle,
20
27
  AlertDialogTrigger,
28
+ AmbientBackground,
21
29
  AppMark,
22
30
  AppWordmark,
23
31
  Avatar,
24
32
  AvatarFallback,
25
33
  Badge,
26
34
  Button,
35
+ CaptureButton,
27
36
  Card,
28
37
  CardContent,
29
38
  CardDescription,
@@ -32,7 +41,9 @@ import {
32
41
  CardTitle,
33
42
  CenteredSpinner,
34
43
  Checkbox,
44
+ ColorSwatches,
35
45
  ConfirmDialog,
46
+ DateTimeField,
36
47
  Dialog,
37
48
  DialogBody,
38
49
  DialogClose,
@@ -44,15 +55,20 @@ import {
44
55
  DialogTrigger,
45
56
  EmptyState,
46
57
  GoogleMark,
58
+ IconButton,
47
59
  Input,
48
60
  Label,
49
61
  LabeledSwitchRow,
62
+ ListRow,
63
+ NEUTRAL_900,
50
64
  NativeUiProvider,
51
65
  PageHeader,
66
+ PageIndicator,
52
67
  PageTitle,
53
68
  PricingModal,
54
69
  Progress,
55
70
  SearchInput,
71
+ SectionLabel,
56
72
  Select,
57
73
  Separator,
58
74
  Sheet,
@@ -60,10 +76,13 @@ import {
60
76
  SheetDescription,
61
77
  SheetFooter,
62
78
  SheetHeader,
79
+ SheetRow,
63
80
  SheetTitle,
64
81
  SheetTrigger,
65
82
  Skeleton,
66
83
  Spinner,
84
+ StackPageHeader,
85
+ StatGrid,
67
86
  StatusDot,
68
87
  Switch,
69
88
  Tabs,
@@ -75,6 +94,7 @@ import {
75
94
  Toaster,
76
95
  UpgradeToProCard,
77
96
  initials,
97
+ palette,
78
98
  toast,
79
99
  tokens,
80
100
  useNativeUi,
@@ -85,6 +105,18 @@ import {
85
105
  } from "../index";
86
106
  import { typeStyle } from "../controlStyles";
87
107
  import { useScheme } from "../useScheme";
108
+ import {
109
+ ChainOfThought,
110
+ ChainOfThoughtContent,
111
+ ChainOfThoughtHeader,
112
+ ChainOfThoughtSearchResult,
113
+ ChainOfThoughtSearchResults,
114
+ ChainOfThoughtStep,
115
+ ConversationEmptyState,
116
+ Suggestion,
117
+ Suggestions,
118
+ } from "../ai";
119
+ import { AiChatSamples } from "./AiChatGallery";
88
120
 
89
121
  const BUTTON_VARIANTS: ButtonVariant[] = [
90
122
  "default",
@@ -113,9 +145,13 @@ const STATUS_TONES: StatusTone[] = [
113
145
  "cancelled",
114
146
  "active",
115
147
  "critical",
148
+ "info",
116
149
  "neutral",
117
150
  ];
118
151
 
152
+ const SWATCHES = ["#db2777", "#2563eb", "#16a34a", "#f59e0b", "#181818", "#ffffff"];
153
+ const SWATCH_LABELS = ["Pink", "Blue", "Green", "Amber", "Black", "White"];
154
+
119
155
  // Space after the last section, so Card, Upgrade and Overlays can scroll to the
120
156
  // top of an 844-tall phone for screenshot crops.
121
157
  const GALLERY_END_SPACE = 720;
@@ -182,6 +218,13 @@ function GalleryScreen() {
182
218
  const [progress, setProgress] = useState(40);
183
219
  const [pricingOpen, setPricingOpen] = useState(false);
184
220
  const [confirmOpen, setConfirmOpen] = useState(false);
221
+ const [swatch, setSwatch] = useState(SWATCHES[0]!);
222
+ const [due, setDue] = useState<Date | null>(null);
223
+ const [when, setWhen] = useState(() => new Date());
224
+ const [muted, setMuted] = useState(false);
225
+ const [sort, setSort] = useState<"newest" | "name">("newest");
226
+ const [page, setPage] = useState(0);
227
+ const scheme = useScheme();
185
228
 
186
229
  return (
187
230
  <View className="flex-1">
@@ -355,6 +398,9 @@ function GalleryScreen() {
355
398
  <Card title="Today" description="Card props, as before.">
356
399
  <GalleryText>Body</GalleryText>
357
400
  </Card>
401
+ <Card title="Pressable" description="onPress makes the whole card a button." onPress={() => toast("Card pressed")}>
402
+ <GalleryText>Tap anywhere on the card.</GalleryText>
403
+ </Card>
358
404
  <Card>
359
405
  <CardHeader>
360
406
  <CardTitle>Composed</CardTitle>
@@ -502,6 +548,162 @@ function GalleryScreen() {
502
548
  <EmptyState.Description>New messages land here.</EmptyState.Description>
503
549
  </EmptyState.Root>
504
550
  </Section>
551
+
552
+ {/* 0.5.0: the Verve, vibot and Velth pieces. */}
553
+ <Section title="ColorSwatches">
554
+ <ColorSwatches
555
+ value={swatch}
556
+ onChange={setSwatch}
557
+ colors={SWATCHES}
558
+ labels={SWATCH_LABELS}
559
+ accessibilityLabel="Accent colour"
560
+ />
561
+ <ColorSwatches
562
+ value={swatch}
563
+ onChange={setSwatch}
564
+ colors={SWATCHES}
565
+ labels={SWATCH_LABELS}
566
+ accessibilityLabel="Accent colour"
567
+ size="sm"
568
+ disabled
569
+ />
570
+ </Section>
571
+
572
+ <Section title="DateTimeField">
573
+ {/* Date only, empty until picked, with a clear button once set. */}
574
+ <DateTimeField
575
+ mode="date"
576
+ value={due}
577
+ onChange={setDue}
578
+ placeholder="No due date"
579
+ onClear={() => setDue(null)}
580
+ />
581
+ <DateTimeField value={when} onChange={setWhen} />
582
+ </Section>
583
+
584
+ <Section title="ListRow">
585
+ {/* Long-press the first row for its second action. */}
586
+ <ListRow
587
+ title="Ada Lovelace"
588
+ meta="See you at nine"
589
+ leading={
590
+ <View>
591
+ <Avatar>
592
+ <AvatarFallback>{initials("Ada Lovelace")}</AvatarFallback>
593
+ </Avatar>
594
+ <StatusDot tone="info" size="md" className="absolute right-0 top-0" />
595
+ </View>
596
+ }
597
+ titleEnd={<Badge variant="secondary">09:41</Badge>}
598
+ accessibilityLabel="Ada Lovelace, unread, 09:41"
599
+ onPress={() => toast("Open thread")}
600
+ onLongPress={() => toast("Thread actions")}
601
+ />
602
+ {/* The switch sits outside the pressable area: it toggles without opening the row. */}
603
+ <ListRow
604
+ title="Mute thread"
605
+ meta="Tap the row or the switch"
606
+ onPress={() => toast("Row pressed")}
607
+ control={<Switch checked={muted} onCheckedChange={setMuted} accessibilityLabel="Mute thread" />}
608
+ />
609
+ </Section>
610
+
611
+ <Section title="SectionLabel and StatGrid">
612
+ <SectionLabel>Today</SectionLabel>
613
+ <StatGrid
614
+ items={[
615
+ { label: "Done", value: "6" },
616
+ { label: "This week", value: "31" },
617
+ { label: "Streak", value: "12 days" },
618
+ ]}
619
+ />
620
+ <SectionLabel uppercase>Library</SectionLabel>
621
+ <StatGrid
622
+ columns={3}
623
+ items={[
624
+ { label: "Books", value: "24" },
625
+ { label: "Notes", value: "130" },
626
+ { label: "Clips", value: "8" },
627
+ ]}
628
+ />
629
+ </Section>
630
+
631
+ <Section title="SheetRow">
632
+ {/* Static rows on the sheet surface; in an app they sit in a `SheetContent`. */}
633
+ <View className="py-2" style={{ backgroundColor: palette.overlay[scheme], borderRadius: tokens.radius.lg }}>
634
+ <SheetRow icon={IconCopy} title="Copy link" onPress={() => toast("Copied")} />
635
+ <SheetRow icon={IconPhoto} title="Save image" detail="To your photo library" onPress={() => toast("Saved")} />
636
+ <SheetRow title="Newest first" active={sort === "newest"} onPress={() => setSort("newest")} />
637
+ <SheetRow title="By name" active={sort === "name"} onPress={() => setSort("name")} />
638
+ <SheetRow icon={IconPhoto} title="Export" detail="Needs V Pro" disabled />
639
+ <SheetRow icon={IconTrash} title="Delete" danger onPress={() => toast.error("Deleted")} />
640
+ </View>
641
+ </Section>
642
+
643
+ <Section title="PageIndicator">
644
+ <PageIndicator count={4} index={page} />
645
+ <PageIndicator count={4} index={page} onChange={setPage} />
646
+ </Section>
647
+
648
+ <Section title="StackPageHeader">
649
+ {/* `safeTop={false}`: the strip is not at the top of the screen. */}
650
+ <AmbientBackground className="flex-none overflow-hidden rounded-lg">
651
+ <StackPageHeader
652
+ title="Details"
653
+ description="Edited 2 min ago"
654
+ safeTop={false}
655
+ onBack={() => toast("Back")}
656
+ headerRight={<ThemeToggle />}
657
+ />
658
+ </AmbientBackground>
659
+ <AmbientBackground className="flex-none overflow-hidden rounded-lg">
660
+ <StackPageHeader title="A much longer page title that is cut off" safeTop={false} onBack={() => toast("Back")} />
661
+ </AmbientBackground>
662
+ </Section>
663
+
664
+ <Section title="Camera chrome">
665
+ {/* A dark stand-in for the live preview. */}
666
+ <View className="h-72 justify-between overflow-hidden rounded-lg p-4" style={{ backgroundColor: NEUTRAL_900 }}>
667
+ <View className="flex-row justify-between">
668
+ <IconButton variant="overlay" icon={IconX} label="Close camera" onPress={() => toast("Close")} />
669
+ <IconButton variant="overlay" icon={IconRefresh} label="Flip camera" onPress={() => toast("Flip")} />
670
+ </View>
671
+ <View className="flex-row items-center justify-between">
672
+ <IconButton variant="overlay" size="lg" icon={IconPhoto} label="Open library" onPress={() => toast("Library")} />
673
+ <CaptureButton onPress={() => toast.success("Captured")} />
674
+ <View style={{ width: 48 }} />
675
+ </View>
676
+ </View>
677
+ <View className="flex-row items-center gap-3">
678
+ <IconButton icon={IconCopy} label="Copy" size="sm" onPress={() => toast("Copied")} />
679
+ <IconButton icon={IconCopy} label="Copy" onPress={() => toast("Copied")} />
680
+ <IconButton icon={IconCopy} label="Copy" size="lg" onPress={() => toast("Copied")} />
681
+ <IconButton icon={IconTrash} label="Delete" disabled />
682
+ </View>
683
+ </Section>
684
+
685
+ {/* The full chat screen, with the composer and keyboard, is `AiChatGallery` (`@vvedantb/native-ui/ai-gallery`). */}
686
+ <Section title="AI chat">
687
+ <AiChatSamples />
688
+ <ChainOfThought defaultOpen>
689
+ <ChainOfThoughtHeader />
690
+ <ChainOfThoughtContent>
691
+ <ChainOfThoughtStep label="Read the question" />
692
+ <ChainOfThoughtStep label="Search for sync options" status="active">
693
+ <ChainOfThoughtSearchResults>
694
+ <ChainOfThoughtSearchResult>example.com</ChainOfThoughtSearchResult>
695
+ <ChainOfThoughtSearchResult>example.org</ChainOfThoughtSearchResult>
696
+ </ChainOfThoughtSearchResults>
697
+ </ChainOfThoughtStep>
698
+ <ChainOfThoughtStep label="Write the answer" status="pending" />
699
+ </ChainOfThoughtContent>
700
+ </ChainOfThought>
701
+ <Suggestions>
702
+ <Suggestion suggestion="Summarise this thread" onPress={(text) => toast(text)} />
703
+ <Suggestion suggestion="Draft a reply" onPress={(text) => toast(text)} />
704
+ </Suggestions>
705
+ <ConversationEmptyState className="flex-none" />
706
+ </Section>
505
707
  </ScrollView>
506
708
 
507
709
  <PricingModal
package/src/index.ts CHANGED
@@ -59,6 +59,20 @@ export { Badge } from "./Badge";
59
59
  export type { BadgeProps, BadgeVariant } from "./Badge";
60
60
  export { StatusDot } from "./StatusDot";
61
61
  export type { StatusDotProps, StatusTone } from "./StatusDot";
62
+ export { ColorSwatches } from "./ColorSwatches";
63
+ export type { ColorSwatchesProps, ColorSwatchesSize } from "./ColorSwatches";
64
+ export { StatGrid } from "./StatGrid";
65
+ export type { StatGridItem, StatGridProps } from "./StatGrid";
66
+ export { SectionLabel } from "./SectionLabel";
67
+ export type { SectionLabelProps } from "./SectionLabel";
68
+ export { SheetRow } from "./SheetRow";
69
+ export type { SheetRowProps } from "./SheetRow";
70
+ export { PageIndicator } from "./PageIndicator";
71
+ export type { PageIndicatorProps } from "./PageIndicator";
72
+ export { IconButton } from "./IconButton";
73
+ export type { IconButtonProps, IconButtonSize, IconButtonVariant } from "./IconButton";
74
+ export { CaptureButton } from "./CaptureButton";
75
+ export type { CaptureButtonProps } from "./CaptureButton";
62
76
  export { Separator } from "./Separator";
63
77
  export type { SeparatorProps } from "./Separator";
64
78
  export { Progress } from "./Progress";
@@ -79,6 +93,7 @@ export type {
79
93
  TabsContentProps,
80
94
  } from "./Tabs";
81
95
  export { ListRow } from "./ListRow";
96
+ export type { ListRowProps } from "./ListRow";
82
97
  export { HeaderIconButton } from "./HeaderIconButton";
83
98
  export { ThemeToggle } from "./ThemeToggle";
84
99
  export type { ThemeToggleProps } from "./ThemeToggle";
@@ -90,6 +105,7 @@ export {
90
105
  CardContent,
91
106
  CardFooter,
92
107
  } from "./Card";
108
+ export type { CardProps } from "./Card";
93
109
  export { Skeleton, CardSkeleton } from "./Skeleton";
94
110
  export { EmptyState } from "./EmptyState";
95
111
  export type { EmptyStateVariant } from "./EmptyState";
@@ -136,11 +152,14 @@ export { Toaster } from "./Toast";
136
152
  export { toast } from "./toastStore";
137
153
  export type { ToastOptions, ToastVariant } from "./toastStore";
138
154
  export { DateTimeField } from "./DateTimeField";
155
+ export type { DateTimeFieldProps } from "./DateTimeField";
139
156
  export { BottomSheetContainer } from "./BottomSheetContainer";
140
157
  export { SafeAreaContainer } from "./SafeAreaContainer";
141
158
  export { ScreenContainer } from "./ScreenContainer";
142
159
  export { PageTitle, PageHeader } from "./PageTitle";
143
160
  export type { PageTitleProps, PageHeaderProps } from "./PageTitle";
161
+ export { StackPageHeader } from "./StackPageHeader";
162
+ export type { StackPageHeaderProps } from "./StackPageHeader";
144
163
  export { SettingsSection } from "./settings/SettingsSection";
145
164
  export { SettingsField } from "./settings/SettingsField";
146
165
  export { SettingsToggleRow } from "./settings/SettingsToggleRow";