@guuey/chat 0.4.0 → 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.
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The React Native per-category component kit (spec §3's override slots,
3
+ * §3.2's RN-parity obligation): one component per `DisplayItem` variant,
4
+ * each a THIN walk of its item — every rendering decision was already made
5
+ * by `planTranscript`; these translate decided items into RN primitives and
6
+ * never consult policy or invent copy. The override contract mirrors the
7
+ * web kit: `NativeTranscriptComponents` is the component map.
8
+ *
9
+ * Accessibility (spec §3.2 restated over RN):
10
+ * - every `expanded` toggle is a `Pressable` with `accessibilityRole`
11
+ * "button" + `accessibilityState.expanded`;
12
+ * - the status line and streaming text announce via
13
+ * `accessibilityLiveRegion="polite"` (Android) and are `accessible`
14
+ * grouped nodes for screen readers on both platforms;
15
+ * - R10 prompts are a live-region group so their appearance announces
16
+ * (RN has no document focus to move — the web kit's focus contract maps
17
+ * to announcement here);
18
+ * - there are NO decorative animations in the native defaults, so
19
+ * reduce-motion holds trivially at this tier (the transcript's scroll
20
+ * animation is where motion lives — see `transcript.tsx`).
21
+ *
22
+ * The R6 DEFAULT is the documented native default-gap: the kit ships
23
+ * WITHOUT a WebView dependency, so the default `view` renderer is a
24
+ * labeled placeholder (`strings.viewSandboxUnavailable`) — never blank —
25
+ * and a host app supplies its own WebView-based mount as the `view` slot
26
+ * override (portal's card machinery is the reference consumer).
27
+ */
28
+ import type { ComponentType, ReactNode } from "react";
29
+ import type { ResolvedViewMount, ViewHostPhase } from "@guuey/mcp-apps-host";
30
+ import type { ChatStrings } from "../strings.js";
31
+ import type { CitationsItem, CodeItem, CompactionItem, DataResultItem, DisplayItem, ErrorItem, HistoryBoundaryItem, ItemKey, MediaItem, PromptItem, ReasoningItem, StatusLineItem, ToolGroupItem, ToolItem, UnknownItem, UserMessageItem, TextItem, ViewMountItem } from "../types.js";
32
+ import type { NativeChatTokens } from "./theme-native.js";
33
+ /** Everything a rendered native item may need beyond itself. */
34
+ export interface NativeTranscriptItemContext {
35
+ strings: ChatStrings;
36
+ /** The resolved theme tokens (see `resolveNativeTheme`). */
37
+ tokens: NativeChatTokens;
38
+ /** Flip an item's collapse state (the renderer owns override state). */
39
+ onToggle: (key: ItemKey) => void;
40
+ /** R0 failed-send retry. */
41
+ onRetry?: (item: UserMessageItem) => void;
42
+ /** R10 prompt actions — the host owns what accept/decline DO. */
43
+ onPromptAction?: (item: PromptItem, action: "accept" | "decline" | "dismiss") => void;
44
+ /** R11 action slots (sign-in / retry affordances). */
45
+ onErrorAction?: (item: ErrorItem) => void;
46
+ /** R6: locator mounts resolved by `useTranscript` ("expired" = failed). */
47
+ resolvedMounts: ReadonlyMap<ItemKey, ResolvedViewMount | "expired">;
48
+ /** R6: live phase reports wired back into the next plan. */
49
+ onViewPhase: (key: ItemKey, phase: ViewHostPhase) => void;
50
+ }
51
+ interface ItemProps<T> {
52
+ item: T;
53
+ ctx: NativeTranscriptItemContext;
54
+ }
55
+ export declare function NativeUserMessage({ item, ctx }: ItemProps<UserMessageItem>): ReactNode;
56
+ export declare function NativeText({ item, ctx }: ItemProps<TextItem>): ReactNode;
57
+ export declare function NativeReasoning({ item, ctx }: ItemProps<ReasoningItem>): ReactNode;
58
+ export declare function NativeDataResult({ item, ctx }: ItemProps<DataResultItem>): ReactNode;
59
+ export declare function NativeTool({ item, ctx }: ItemProps<ToolItem>): ReactNode;
60
+ export declare function NativeToolGroup({ item, ctx }: ItemProps<ToolGroupItem>): ReactNode;
61
+ export declare function NativeView({ item, ctx }: ItemProps<ViewMountItem>): ReactNode;
62
+ export declare function NativeMedia({ item, ctx }: ItemProps<MediaItem>): ReactNode;
63
+ export declare function NativeCode({ item, ctx }: ItemProps<CodeItem>): ReactNode;
64
+ export declare function NativeCitations({ item, ctx }: ItemProps<CitationsItem>): ReactNode;
65
+ export declare function NativePrompt({ item, ctx }: ItemProps<PromptItem>): ReactNode;
66
+ export declare function NativeError({ item, ctx }: ItemProps<ErrorItem>): ReactNode;
67
+ export declare function NativeHistoryBoundary({ item, ctx }: ItemProps<HistoryBoundaryItem>): ReactNode;
68
+ export declare function NativeCompaction({ item, ctx }: ItemProps<CompactionItem>): ReactNode;
69
+ export declare function NativeUnknown({ item, ctx }: ItemProps<UnknownItem>): ReactNode;
70
+ export declare function NativeStatus({ item, ctx }: ItemProps<StatusLineItem>): ReactNode;
71
+ /** One component per §3 override slot — the native mirror of the web map. */
72
+ export interface NativeTranscriptComponents {
73
+ userMessage: ComponentType<ItemProps<UserMessageItem>>;
74
+ text: ComponentType<ItemProps<TextItem>>;
75
+ reasoning: ComponentType<ItemProps<ReasoningItem>>;
76
+ tool: ComponentType<ItemProps<ToolItem>>;
77
+ toolGroup: ComponentType<ItemProps<ToolGroupItem>>;
78
+ dataResult: ComponentType<ItemProps<DataResultItem>>;
79
+ view: ComponentType<ItemProps<ViewMountItem>>;
80
+ media: ComponentType<ItemProps<MediaItem>>;
81
+ code: ComponentType<ItemProps<CodeItem>>;
82
+ citations: ComponentType<ItemProps<CitationsItem>>;
83
+ prompt: ComponentType<ItemProps<PromptItem>>;
84
+ error: ComponentType<ItemProps<ErrorItem>>;
85
+ history: ComponentType<ItemProps<HistoryBoundaryItem>>;
86
+ compaction: ComponentType<ItemProps<CompactionItem>>;
87
+ unknown: ComponentType<ItemProps<UnknownItem>>;
88
+ status: ComponentType<ItemProps<StatusLineItem>>;
89
+ }
90
+ export declare const nativeTranscriptComponents: NativeTranscriptComponents;
91
+ /** Dispatch one display item through the (possibly overridden) map. */
92
+ export declare function renderNativeItem(item: DisplayItem, components: NativeTranscriptComponents, ctx: NativeTranscriptItemContext): ReactNode;
93
+ export {};
94
+ //# sourceMappingURL=components.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../src/native/components.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,292 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Image, Pressable, Text, View } from "react-native";
3
+ import { Linking } from "react-native";
4
+ import { NativeMarkdown } from "./markdown.js";
5
+ /** A collapse toggle + body pair with the RN accessibility plumbing. */
6
+ function Collapsible({ itemKey, expanded, header, ctx, children, }) {
7
+ const { tokens } = ctx;
8
+ return (_jsxs(View, { style: {
9
+ backgroundColor: tokens.palette.surface,
10
+ borderRadius: tokens.radius,
11
+ paddingHorizontal: tokens.pad,
12
+ paddingVertical: tokens.pad - 4,
13
+ }, children: [_jsxs(Pressable, { accessibilityRole: "button", accessibilityState: { expanded }, onPress: () => ctx.onToggle(itemKey), style: { flexDirection: "row", alignItems: "center", gap: 6 }, children: [_jsx(View, { style: { flexShrink: 1, flexDirection: "row", alignItems: "center", gap: 6 }, children: header }), _jsx(Text, { accessibilityElementsHidden: true, style: { color: tokens.palette.inkMuted, fontSize: tokens.fontSize - 3 }, children: expanded ? "▾" : "▸" })] }), expanded ? _jsx(View, { style: { marginTop: 6, gap: 6 }, children: children }) : null] }));
14
+ }
15
+ function MutedText({ ctx, children }) {
16
+ const { tokens } = ctx;
17
+ return (_jsx(Text, { style: {
18
+ color: tokens.palette.inkMuted,
19
+ fontSize: tokens.fontSize - 2,
20
+ fontFamily: tokens.fontFamily,
21
+ }, children: children }));
22
+ }
23
+ // ─── R0 ────────────────────────────────────────────────────────────────────
24
+ export function NativeUserMessage({ item, ctx }) {
25
+ const { tokens } = ctx;
26
+ return (_jsxs(View, { style: { alignItems: "flex-end", gap: 4, opacity: item.state === "sending" ? 0.6 : 1 }, children: [_jsx(View, { style: {
27
+ maxWidth: "82%",
28
+ backgroundColor: tokens.palette.ink,
29
+ borderRadius: tokens.radius,
30
+ paddingHorizontal: tokens.pad,
31
+ paddingVertical: tokens.pad - 2,
32
+ }, children: _jsx(Text, { style: { color: tokens.palette.canvas, fontSize: tokens.fontSize, fontFamily: tokens.fontFamily }, children: item.text }) }), item.state === "failed" ? (_jsxs(View, { accessibilityLiveRegion: "polite", style: { flexDirection: "row", alignItems: "center", gap: 8 }, children: [_jsx(Text, { style: { color: tokens.palette.error, fontSize: tokens.fontSize - 2 }, children: ctx.strings.userCouldntSend }), item.retry ? (_jsx(Pressable, { accessibilityRole: "button", onPress: () => ctx.onRetry?.(item), children: _jsx(Text, { style: { color: tokens.palette.accent, fontSize: tokens.fontSize - 2, fontWeight: "600" }, children: ctx.strings.userRetry }) })) : null] })) : null] }));
33
+ }
34
+ // ─── R1 ────────────────────────────────────────────────────────────────────
35
+ export function NativeText({ item, ctx }) {
36
+ const { tokens } = ctx;
37
+ const caret = item.streaming ? (_jsx(Text, { style: { color: tokens.palette.accent }, children: " ▍" })) : undefined;
38
+ return (_jsxs(View, { accessibilityLiveRegion: item.streaming ? "polite" : "none", style: {
39
+ alignSelf: "flex-start",
40
+ maxWidth: "92%",
41
+ backgroundColor: tokens.palette.surface,
42
+ borderRadius: tokens.radius,
43
+ paddingHorizontal: tokens.pad,
44
+ paddingVertical: tokens.pad - 2,
45
+ gap: 4,
46
+ }, children: [item.markdown ? (_jsx(NativeMarkdown, { text: item.text, color: tokens.palette.ink, tokens: tokens, trailing: caret })) : (_jsxs(Text, { style: { color: tokens.palette.ink, fontSize: tokens.fontSize, fontFamily: tokens.fontFamily }, children: [item.text, caret] })), item.stopped ? _jsx(MutedText, { ctx: ctx, children: ctx.strings.stopped }) : null] }));
47
+ }
48
+ // ─── R2 ────────────────────────────────────────────────────────────────────
49
+ export function NativeReasoning({ item, ctx }) {
50
+ return (_jsx(Collapsible, { itemKey: item.key, expanded: item.expanded, ctx: ctx, header: _jsx(MutedText, { ctx: ctx, children: item.label }), children: _jsx(MutedText, { ctx: ctx, children: item.text }) }));
51
+ }
52
+ // ─── R5 ────────────────────────────────────────────────────────────────────
53
+ export function NativeDataResult({ item, ctx }) {
54
+ const { tokens } = ctx;
55
+ const bytes = ctx.strings.bytes(item.byteCount);
56
+ if (item.state === "empty")
57
+ return _jsx(MutedText, { ctx: ctx, children: ctx.strings.noOutput });
58
+ if (item.preview === null)
59
+ return _jsx(MutedText, { ctx: ctx, children: bytes });
60
+ return (_jsxs(View, { style: { gap: 4 }, children: [_jsx(View, { style: {
61
+ maxHeight: 220, // R5's scroll-cap projected to a fixed clamp (native has no rem)
62
+ overflow: "hidden",
63
+ backgroundColor: tokens.palette.canvasMuted,
64
+ borderRadius: tokens.radius,
65
+ padding: tokens.pad - 4,
66
+ }, children: _jsx(Text, { style: { color: tokens.palette.ink, fontSize: tokens.fontSize - 3, fontFamily: tokens.monoFontFamily }, children: item.preview }) }), item.showBytes ? _jsx(MutedText, { ctx: ctx, children: bytes }) : null] }));
67
+ }
68
+ // ─── R3 ────────────────────────────────────────────────────────────────────
69
+ const TOOL_GLYPH = {
70
+ running: "◌",
71
+ done: "✓",
72
+ failed: "✕",
73
+ orphaned: "–",
74
+ };
75
+ export function NativeTool({ item, ctx }) {
76
+ // R4's display-bearing rule: in calm this call's line lives in its view
77
+ // row's chrome ("via {tool}") — rendering it here too would double it.
78
+ if (item.attribution)
79
+ return null;
80
+ const { tokens } = ctx;
81
+ const failed = item.state === "failed";
82
+ const header = (_jsxs(_Fragment, { children: [_jsx(Text, { style: { color: failed ? tokens.palette.error : tokens.palette.inkMuted, fontSize: tokens.fontSize - 2 }, children: TOOL_GLYPH[item.state] }), _jsxs(Text, { style: {
83
+ color: failed ? tokens.palette.error : tokens.palette.inkMuted,
84
+ fontSize: tokens.fontSize - 2,
85
+ fontFamily: tokens.fontFamily,
86
+ flexShrink: 1,
87
+ }, children: [item.title, item.state === "orphaned" ? ` — ${ctx.strings.toolDidntFinish}` : ""] })] }));
88
+ const expandable = item.argsPreview !== null || item.result !== null;
89
+ if (!expandable) {
90
+ return _jsx(View, { style: { flexDirection: "row", alignItems: "center", gap: 6, paddingHorizontal: ctx.tokens.pad }, children: header });
91
+ }
92
+ return (_jsxs(Collapsible, { itemKey: item.key, expanded: item.expanded, ctx: ctx, header: header, children: [item.argsPreview !== null ? (_jsx(Text, { style: { color: tokens.palette.inkMuted, fontSize: tokens.fontSize - 3, fontFamily: tokens.monoFontFamily }, children: item.argsPreview })) : null, item.result !== null ? _jsx(NativeDataResult, { item: item.result, ctx: ctx }) : null] }));
93
+ }
94
+ // ─── R4 ────────────────────────────────────────────────────────────────────
95
+ export function NativeToolGroup({ item, ctx }) {
96
+ const { tokens } = ctx;
97
+ return (_jsx(Collapsible, { itemKey: item.key, expanded: item.expanded, ctx: ctx, header: _jsxs(_Fragment, { children: [_jsx(MutedText, { ctx: ctx, children: item.label }), item.failureBadge !== null ? (_jsx(Text, { style: { color: tokens.palette.error, fontSize: tokens.fontSize - 3 }, children: item.failureBadge })) : null] }), children: item.tools.map((tool) => (_jsx(NativeTool, { item: tool, ctx: ctx }, tool.key))) }));
98
+ }
99
+ // ─── R6 — the documented native default-gap ────────────────────────────────
100
+ export function NativeView({ item, ctx }) {
101
+ const { tokens } = ctx;
102
+ // The kit's native tier ships without a WebView dependency, so there is
103
+ // no default mount host — the `view` slot is a REQUIRED override on
104
+ // native (labeled here, never blank; portal's card machinery is the
105
+ // reference override). The expired/labeled states still render honestly.
106
+ const label = item.phase === "expired"
107
+ ? ctx.strings.viewExpired
108
+ : (item.label ?? ctx.strings.viewSandboxUnavailable);
109
+ return (_jsxs(View, { accessibilityRole: "summary", style: {
110
+ backgroundColor: tokens.palette.canvasMuted,
111
+ borderRadius: tokens.radius,
112
+ padding: tokens.pad,
113
+ gap: 4,
114
+ }, children: [_jsx(MutedText, { ctx: ctx, children: label }), item.attribution !== null ? _jsx(MutedText, { ctx: ctx, children: item.attribution }) : null] }));
115
+ }
116
+ // ─── R7 ────────────────────────────────────────────────────────────────────
117
+ /** Inline images allow https URLs and image/* base64 data — nothing else. */
118
+ function imageSrc(item) {
119
+ const source = item.source;
120
+ if (source.type === "url" && /^https:\/\//i.test(source.url))
121
+ return source.url;
122
+ if (source.type === "base64" && /^image\//.test(source.mediaType)) {
123
+ return `data:${source.mediaType};base64,${source.data}`;
124
+ }
125
+ return null;
126
+ }
127
+ export function NativeMedia({ item, ctx }) {
128
+ const { tokens } = ctx;
129
+ if (item.presentation === "inline" && item.media === "image") {
130
+ const src = imageSrc(item);
131
+ if (src !== null) {
132
+ return (_jsx(Image, { accessibilityLabel: item.name ?? "", source: { uri: src }, resizeMode: "contain", style: { width: "100%", height: 220, borderRadius: tokens.radius, backgroundColor: tokens.palette.canvasMuted } }));
133
+ }
134
+ }
135
+ // Attachment chip: files, documents, audio (RN has no default audio
136
+ // element — a chip is the honest default), unloadable/oversized media.
137
+ return (_jsx(View, { style: {
138
+ alignSelf: "flex-start",
139
+ backgroundColor: tokens.palette.canvasMuted,
140
+ borderRadius: tokens.radius,
141
+ paddingHorizontal: tokens.pad,
142
+ paddingVertical: 4,
143
+ }, children: _jsx(MutedText, { ctx: ctx, children: item.name ?? item.media }) }));
144
+ }
145
+ // ─── R8 ────────────────────────────────────────────────────────────────────
146
+ export function NativeCode({ item, ctx }) {
147
+ const { tokens } = ctx;
148
+ return (_jsxs(View, { style: { backgroundColor: tokens.palette.canvasMuted, borderRadius: tokens.radius, padding: tokens.pad - 2, gap: 4 }, children: [_jsx(MutedText, { ctx: ctx, children: item.language }), _jsx(Text, { style: { color: tokens.palette.ink, fontSize: tokens.fontSize - 3, fontFamily: tokens.monoFontFamily }, children: item.code })] }));
149
+ }
150
+ // ─── R9 ────────────────────────────────────────────────────────────────────
151
+ /** Citation links navigate only to http(s) targets. */
152
+ function safeCitationUrl(url) {
153
+ return url !== null && /^https?:\/\//i.test(url) ? url : null;
154
+ }
155
+ export function NativeCitations({ item, ctx }) {
156
+ const { tokens } = ctx;
157
+ return (_jsx(Collapsible, { itemKey: item.key, expanded: item.expanded, ctx: ctx, header: _jsx(MutedText, { ctx: ctx, children: item.label }), children: item.sources.map((source, i) => {
158
+ const url = safeCitationUrl(source.url);
159
+ const label = source.title ?? source.url ?? "";
160
+ if (url === null)
161
+ return _jsx(MutedText, { ctx: ctx, children: label }, i);
162
+ return (_jsx(Pressable, { accessibilityRole: "link", onPress: () => {
163
+ void Linking.openURL(url).catch(() => { });
164
+ }, children: _jsx(Text, { style: { color: tokens.palette.accent, fontSize: tokens.fontSize - 2, textDecorationLine: "underline" }, children: label }) }, i));
165
+ }) }));
166
+ }
167
+ // ─── R10 ───────────────────────────────────────────────────────────────────
168
+ export function NativePrompt({ item, ctx }) {
169
+ const { tokens } = ctx;
170
+ if (item.state !== "pending") {
171
+ return (_jsxs(MutedText, { ctx: ctx, children: [item.promptKind, ": ", item.state] }));
172
+ }
173
+ return (_jsxs(View
174
+ // RN has no document focus to move; the live region announces the ask
175
+ // (the web kit's focus contract, restated over the platform).
176
+ , {
177
+ // RN has no document focus to move; the live region announces the ask
178
+ // (the web kit's focus contract, restated over the platform).
179
+ accessibilityLiveRegion: "polite", accessible: true, style: { backgroundColor: tokens.palette.surface, borderRadius: tokens.radius, padding: tokens.pad, gap: 8 }, children: [_jsx(Text, { style: { color: tokens.palette.ink, fontSize: tokens.fontSize, fontFamily: tokens.fontFamily }, children: item.promptKind === "consent"
180
+ ? `${item.appId} requests ${item.requested} access`
181
+ : `Link your account to ${item.appId}` }), _jsxs(View, { style: { flexDirection: "row", gap: 12 }, children: [_jsx(Pressable, { accessibilityRole: "button", onPress: () => ctx.onPromptAction?.(item, "accept"), children: _jsx(Text, { style: { color: tokens.palette.accent, fontWeight: "700", fontSize: tokens.fontSize }, children: "Allow" }) }), _jsx(Pressable, { accessibilityRole: "button", onPress: () => ctx.onPromptAction?.(item, "decline"), children: _jsx(Text, { style: { color: tokens.palette.inkMuted, fontSize: tokens.fontSize }, children: "Decline" }) })] })] }));
182
+ }
183
+ // ─── R11 ───────────────────────────────────────────────────────────────────
184
+ export function NativeError({ item, ctx }) {
185
+ const { tokens } = ctx;
186
+ return (_jsxs(View, { accessibilityLiveRegion: "assertive", accessible: true, style: {
187
+ backgroundColor: tokens.palette.surface,
188
+ borderLeftWidth: 3,
189
+ borderLeftColor: tokens.palette.error,
190
+ borderRadius: tokens.radius,
191
+ padding: tokens.pad,
192
+ gap: 6,
193
+ }, children: [_jsx(Text, { style: { color: tokens.palette.ink, fontSize: tokens.fontSize - 1, fontFamily: tokens.fontFamily }, children: item.copy }), ctx.onErrorAction !== undefined && (item.family === "transient" || item.family === "auth") ? (_jsx(Pressable, { accessibilityRole: "button", onPress: () => ctx.onErrorAction?.(item), children: _jsx(Text, { style: { color: tokens.palette.accent, fontSize: tokens.fontSize - 1, fontWeight: "600" }, children: item.family === "auth" ? ctx.strings.errorAuth : ctx.strings.userRetry }) })) : null, item.verbatim !== null ? (_jsx(Text, { style: { color: tokens.palette.inkMuted, fontSize: tokens.fontSize - 3, fontFamily: tokens.monoFontFamily }, children: item.verbatim })) : null] }));
194
+ }
195
+ // ─── R13 / R14 / R15 ───────────────────────────────────────────────────────
196
+ export function NativeHistoryBoundary({ item, ctx }) {
197
+ return (_jsx(View, { accessibilityLiveRegion: "polite", style: { alignItems: "center", paddingVertical: 4 }, children: _jsx(MutedText, { ctx: ctx, children: item.label }) }));
198
+ }
199
+ export function NativeCompaction({ item, ctx }) {
200
+ return (_jsx(View, { style: { alignItems: "center", paddingVertical: 4 }, children: _jsx(MutedText, { ctx: ctx, children: item.label }) }));
201
+ }
202
+ export function NativeUnknown({ item, ctx }) {
203
+ return (_jsx(Collapsible, { itemKey: item.key, expanded: item.expanded, ctx: ctx, header: _jsxs(MutedText, { ctx: ctx, children: [item.label, " (", item.typeName, ")"] }), children: item.raw !== null ? (_jsx(Text, { style: { color: ctx.tokens.palette.inkMuted, fontSize: ctx.tokens.fontSize - 3, fontFamily: ctx.tokens.monoFontFamily }, children: JSON.stringify(item.raw, null, 2) })) : (_jsx(MutedText, { ctx: ctx, children: ctx.strings.bytes(item.byteSize) })) }));
204
+ }
205
+ // ─── R12 / §4 ──────────────────────────────────────────────────────────────
206
+ export function NativeStatus({ item, ctx }) {
207
+ const { tokens } = ctx;
208
+ return (_jsx(View, { accessibilityLiveRegion: "polite", accessible: true, style: { paddingHorizontal: tokens.pad, paddingVertical: 2 }, children: _jsxs(MutedText, { ctx: ctx, children: [item.copy, item.detail !== null ? ` · ${item.detail}` : ""] }) }));
209
+ }
210
+ export const nativeTranscriptComponents = {
211
+ userMessage: NativeUserMessage,
212
+ text: NativeText,
213
+ reasoning: NativeReasoning,
214
+ tool: NativeTool,
215
+ toolGroup: NativeToolGroup,
216
+ dataResult: NativeDataResult,
217
+ view: NativeView,
218
+ media: NativeMedia,
219
+ code: NativeCode,
220
+ citations: NativeCitations,
221
+ prompt: NativePrompt,
222
+ error: NativeError,
223
+ history: NativeHistoryBoundary,
224
+ compaction: NativeCompaction,
225
+ unknown: NativeUnknown,
226
+ status: NativeStatus,
227
+ };
228
+ /** Dispatch one display item through the (possibly overridden) map. */
229
+ export function renderNativeItem(item, components, ctx) {
230
+ switch (item.kind) {
231
+ case "user": {
232
+ const C = components.userMessage;
233
+ return _jsx(C, { item: item, ctx: ctx }, item.key);
234
+ }
235
+ case "text": {
236
+ const C = components.text;
237
+ return _jsx(C, { item: item, ctx: ctx }, item.key);
238
+ }
239
+ case "reasoning": {
240
+ const C = components.reasoning;
241
+ return _jsx(C, { item: item, ctx: ctx }, item.key);
242
+ }
243
+ case "tool": {
244
+ const C = components.tool;
245
+ return _jsx(C, { item: item, ctx: ctx }, item.key);
246
+ }
247
+ case "tool-group": {
248
+ const C = components.toolGroup;
249
+ return _jsx(C, { item: item, ctx: ctx }, item.key);
250
+ }
251
+ case "data-result": {
252
+ const C = components.dataResult;
253
+ return _jsx(C, { item: item, ctx: ctx }, item.key);
254
+ }
255
+ case "view": {
256
+ const C = components.view;
257
+ return _jsx(C, { item: item, ctx: ctx }, item.key);
258
+ }
259
+ case "media": {
260
+ const C = components.media;
261
+ return _jsx(C, { item: item, ctx: ctx }, item.key);
262
+ }
263
+ case "code": {
264
+ const C = components.code;
265
+ return _jsx(C, { item: item, ctx: ctx }, item.key);
266
+ }
267
+ case "citations": {
268
+ const C = components.citations;
269
+ return _jsx(C, { item: item, ctx: ctx }, item.key);
270
+ }
271
+ case "prompt": {
272
+ const C = components.prompt;
273
+ return _jsx(C, { item: item, ctx: ctx }, item.key);
274
+ }
275
+ case "error": {
276
+ const C = components.error;
277
+ return _jsx(C, { item: item, ctx: ctx }, item.key);
278
+ }
279
+ case "history-boundary": {
280
+ const C = components.history;
281
+ return _jsx(C, { item: item, ctx: ctx }, item.key);
282
+ }
283
+ case "compaction": {
284
+ const C = components.compaction;
285
+ return _jsx(C, { item: item, ctx: ctx }, item.key);
286
+ }
287
+ case "unknown": {
288
+ const C = components.unknown;
289
+ return _jsx(C, { item: item, ctx: ctx }, item.key);
290
+ }
291
+ }
292
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The R1 markdown surface, React Native projection.
3
+ *
4
+ * Same sanitizer boundary as the web kit (`react/markdown.tsx`), same
5
+ * pipeline: `@silverprotocol/richtext` parses markdown into a TYPED AST in
6
+ * which raw HTML is structurally unrepresentable — `<script>` in model
7
+ * output can only ever be literal text, there is no image node type (the
8
+ * spec's F5 ruling holds structurally), and link `href` is populated
9
+ * upstream only for http/https/mailto (`SAFE_HREF`). This module is pure
10
+ * presentation: AST node → themed RN elements. Navigable links open
11
+ * through `Linking` — the href is already allowlist-gated upstream, so no
12
+ * second gate is re-derived here.
13
+ *
14
+ * Streaming-tolerant by upstream design: an unclosed `**bol` renders as
15
+ * bold-so-far and completes in place as deltas arrive.
16
+ *
17
+ * Prior art: portal's `MarkdownText` (guuey#95) — this is its published,
18
+ * token-driven descendant (colors/fonts come from the kit's resolved
19
+ * `NativeChatTokens`, not an app theme context).
20
+ */
21
+ import type { ReactNode } from "react";
22
+ import type { NativeChatTokens } from "./theme-native.js";
23
+ /** Sanitized markdown → themed RN elements (see the module docblock). */
24
+ export declare function NativeMarkdown({ text, color, tokens, trailing, }: {
25
+ text: string;
26
+ color: string;
27
+ tokens: NativeChatTokens;
28
+ /** Appended inside the LAST block so a streaming caret hugs the text. */
29
+ trailing?: ReactNode;
30
+ }): ReactNode;
31
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/native/markdown.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAOvC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAsJ1D,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,KAAK,EACL,MAAM,EACN,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,gBAAgB,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,GAAG,SAAS,CAeZ"}
@@ -0,0 +1,69 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Linking, Text, View } from "react-native";
3
+ import { parseRichText, } from "@silverprotocol/richtext";
4
+ function InlineRuns({ nodes, color, tokens, bold, italic, }) {
5
+ return nodes.map((node, i) => {
6
+ const baseStyle = {
7
+ color,
8
+ fontSize: tokens.fontSize,
9
+ fontFamily: tokens.fontFamily,
10
+ fontWeight: bold ? "700" : "400",
11
+ fontStyle: italic ? "italic" : "normal",
12
+ };
13
+ switch (node.type) {
14
+ case "text":
15
+ return (_jsx(Text, { style: baseStyle, children: node.text }, i));
16
+ case "break":
17
+ return _jsx(Text, { children: "\n" }, i);
18
+ case "strong":
19
+ return (_jsx(InlineRuns, { nodes: node.children, color: color, tokens: tokens, bold: true, italic: italic }, i));
20
+ case "em":
21
+ return (_jsx(InlineRuns, { nodes: node.children, color: color, tokens: tokens, bold: bold, italic: true }, i));
22
+ case "code":
23
+ return (_jsx(Text, { style: {
24
+ color,
25
+ fontSize: tokens.fontSize - 1,
26
+ fontFamily: tokens.monoFontFamily,
27
+ backgroundColor: tokens.palette.canvasMuted,
28
+ }, children: node.code }, i));
29
+ case "link": {
30
+ const href = node.href;
31
+ if (href === undefined) {
32
+ // Unsafe or unresolvable target: the styled text, no press target.
33
+ return (_jsx(Text, { style: { ...baseStyle, textDecorationLine: "underline" }, children: _jsx(InlineRuns, { nodes: node.children, color: color, tokens: tokens, bold: bold, italic: italic }) }, i));
34
+ }
35
+ return (_jsx(Text, { accessibilityRole: "link", style: { ...baseStyle, color: tokens.palette.accent, textDecorationLine: "underline" }, onPress: () => {
36
+ void Linking.openURL(href).catch(() => {
37
+ // An unopenable-but-safe URL is a platform condition, not an error surface.
38
+ });
39
+ }, children: _jsx(InlineRuns, { nodes: node.children, color: color, tokens: tokens, bold: bold, italic: italic }) }, i));
40
+ }
41
+ }
42
+ });
43
+ }
44
+ function BlockView({ block, color, tokens, trailing, }) {
45
+ switch (block.type) {
46
+ case "paragraph":
47
+ return (_jsxs(Text, { style: { fontSize: tokens.fontSize, lineHeight: Math.round(tokens.fontSize * 1.45) }, children: [_jsx(InlineRuns, { nodes: block.children, color: color, tokens: tokens }), trailing] }));
48
+ case "heading":
49
+ return (_jsxs(Text, { style: {
50
+ color,
51
+ fontSize: tokens.fontSize + (block.level <= 3 ? 2 : 0),
52
+ fontFamily: tokens.fontFamily,
53
+ fontWeight: "700",
54
+ }, children: [_jsx(InlineRuns, { nodes: block.children, color: color, tokens: tokens, bold: true }), trailing] }));
55
+ case "code-fence":
56
+ return (_jsxs(View, { style: {
57
+ backgroundColor: tokens.palette.canvasMuted,
58
+ borderRadius: tokens.radius,
59
+ padding: tokens.pad,
60
+ }, children: [_jsx(Text, { style: { color, fontSize: tokens.fontSize - 2, fontFamily: tokens.monoFontFamily }, children: block.code }), trailing] }));
61
+ case "list":
62
+ return (_jsx(View, { style: { gap: 2 }, children: block.items.map((item, j) => (_jsxs(View, { style: { flexDirection: "row" }, children: [_jsx(Text, { style: { color, fontSize: tokens.fontSize, fontFamily: tokens.fontFamily }, children: block.ordered ? `${(block.start ?? 1) + j}. ` : "• " }), _jsxs(Text, { style: { flexShrink: 1, fontSize: tokens.fontSize, lineHeight: Math.round(tokens.fontSize * 1.45) }, children: [_jsx(InlineRuns, { nodes: item.children, color: color, tokens: tokens }), j === block.items.length - 1 ? trailing : null] })] }, j))) }));
63
+ }
64
+ }
65
+ /** Sanitized markdown → themed RN elements (see the module docblock). */
66
+ export function NativeMarkdown({ text, color, tokens, trailing, }) {
67
+ const blocks = parseRichText(text);
68
+ return (_jsx(View, { style: { gap: 6 }, children: blocks.map((block, i) => (_jsx(BlockView, { block: block, color: color, tokens: tokens, trailing: i === blocks.length - 1 ? trailing : undefined }, i))) }));
69
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The React Native theme projection (spec §6): `GuueyChatTheme` is the
3
+ * cross-platform contract; CSS custom properties are the WEB projection
4
+ * (`react/theme-css.ts`), and this module is the native one — the same
5
+ * schema resolved into a flat token object RN components read as style
6
+ * values. No new vocabulary: every field here derives from the schema.
7
+ */
8
+ import { type GuueyChatPalette, type GuueyChatTheme } from "../theme.js";
9
+ export type NativeThemeMode = "light" | "dark";
10
+ /** The resolved style tokens the native components consume. */
11
+ export interface NativeChatTokens {
12
+ /** The mode-resolved palette (per-token fallback already applied). */
13
+ palette: GuueyChatPalette;
14
+ /** shape.radius → px: none 0, soft 10, round 18. */
15
+ radius: number;
16
+ /** shape.density → the bubble padding / row gap pair. */
17
+ pad: number;
18
+ gap: number;
19
+ /** typography — undefined means the platform default font. */
20
+ fontFamily: string | undefined;
21
+ monoFontFamily: string | undefined;
22
+ /** Base body size in sp, typography.scale applied (default 15). */
23
+ fontSize: number;
24
+ }
25
+ /** Resolve a (possibly partial/stale) theme + mode into native tokens. */
26
+ export declare function resolveNativeTheme(theme: GuueyChatTheme, mode: NativeThemeMode): NativeChatTokens;
27
+ //# sourceMappingURL=theme-native.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-native.d.ts","sourceRoot":"","sources":["../../src/native/theme-native.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAgB,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAEvF,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,CAAC;AAE/C,+DAA+D;AAC/D,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,OAAO,EAAE,gBAAgB,CAAC;IAC1B,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,8DAA8D;IAC9D,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAQD,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,eAAe,GAAG,gBAAgB,CAajG"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The React Native theme projection (spec §6): `GuueyChatTheme` is the
3
+ * cross-platform contract; CSS custom properties are the WEB projection
4
+ * (`react/theme-css.ts`), and this module is the native one — the same
5
+ * schema resolved into a flat token object RN components read as style
6
+ * values. No new vocabulary: every field here derives from the schema.
7
+ */
8
+ import { resolveTheme } from "../theme.js";
9
+ const RADIUS = {
10
+ none: 0,
11
+ soft: 10,
12
+ round: 18,
13
+ };
14
+ /** Resolve a (possibly partial/stale) theme + mode into native tokens. */
15
+ export function resolveNativeTheme(theme, mode) {
16
+ const resolved = resolveTheme(theme);
17
+ const palette = resolved.colors[mode];
18
+ const comfortable = resolved.shape.density === "comfortable";
19
+ return {
20
+ palette,
21
+ radius: RADIUS[resolved.shape.radius],
22
+ pad: comfortable ? 12 : 8,
23
+ gap: comfortable ? 10 : 6,
24
+ fontFamily: resolved.typography.fontFamily,
25
+ monoFontFamily: resolved.typography.monoFontFamily,
26
+ fontSize: Math.round(15 * (resolved.typography.scale ?? 1)),
27
+ };
28
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * `<NativeTranscript>` — the plan walker + the §3.2 renderer obligations,
3
+ * restated over React Native's own mechanics:
4
+ *
5
+ * - **Scroll contract, the inverted way.** The list is an INVERTED
6
+ * FlatList over the reversed plan (the chat-app contract portal's
7
+ * agent-chat screen ratified — guuey#94/#100): content grows at the
8
+ * scroll ORIGIN, so "stick to bottom while streaming" and "never shift
9
+ * the viewport while the reader is up in history" hold by construction
10
+ * — no scrollTo calls during streaming, no re-pin race with content
11
+ * resize (an R6 card growing at the origin cannot move the reader's
12
+ * viewport). "Release on scroll-up" is likewise structural; the kit
13
+ * adds the jump-to-latest affordance when the reader has left the
14
+ * newest turn, and its scroll animation respects the platform
15
+ * reduce-motion setting.
16
+ * - **Windowing** is the list primitive's own virtualization — FlatList
17
+ * windows the DOM-equivalent natively; the plan's stable keys are the
18
+ * `keyExtractor`, so item identity survives streaming updates.
19
+ * - **Accessibility** lives on the item components (`components.tsx`).
20
+ *
21
+ * Platform chrome (keyboard insets, dismiss modes, dock scroll policies)
22
+ * stays HOST-OWNED: `listProps` passes the host's FlatList behavior
23
+ * through, with the kit composing `onScroll` so both the host's policy and
24
+ * the jump affordance see every event. The kit owns data/renderItem/keys/
25
+ * inversion — a host overriding those would be fighting the plan.
26
+ */
27
+ import { type ReactNode } from "react";
28
+ import { type FlatListProps } from "react-native";
29
+ import { type GuueyChatTheme } from "../theme.js";
30
+ import { type ChatStrings } from "../strings.js";
31
+ import type { DisplayItem, TranscriptPlan } from "../types.js";
32
+ import { type NativeTranscriptComponents, type NativeTranscriptItemContext } from "./components.js";
33
+ import { type NativeThemeMode } from "./theme-native.js";
34
+ /** The host-behavior pass-through — everything the kit deliberately does NOT own. */
35
+ export type NativeTranscriptListProps = Pick<FlatListProps<DisplayItem>, "style" | "contentContainerStyle" | "keyboardDismissMode" | "keyboardShouldPersistTaps" | "automaticallyAdjustKeyboardInsets" | "onScroll" | "scrollEventThrottle" | "ListEmptyComponent" | "scrollIndicatorInsets">;
36
+ export interface NativeTranscriptProps extends Pick<NativeTranscriptItemContext, "onToggle" | "onRetry" | "onPromptAction" | "onErrorAction" | "resolvedMounts" | "onViewPhase"> {
37
+ plan: TranscriptPlan;
38
+ /** Per-slot component overrides (spec §3's override column). */
39
+ components?: Partial<NativeTranscriptComponents>;
40
+ /** The i18n seam — pass the same strings the policy carries. */
41
+ strings?: ChatStrings;
42
+ theme?: GuueyChatTheme;
43
+ mode?: NativeThemeMode;
44
+ /** Host-owned FlatList behavior (keyboard/scroll chrome). */
45
+ listProps?: NativeTranscriptListProps;
46
+ }
47
+ export declare function NativeTranscript(props: NativeTranscriptProps): ReactNode;
48
+ //# sourceMappingURL=transcript.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../../src/native/transcript.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAqD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC1F,OAAO,EAML,KAAK,aAAa,EAGnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAGL,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,EACjC,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAK7E,qFAAqF;AACrF,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAC1C,aAAa,CAAC,WAAW,CAAC,EACxB,OAAO,GACP,uBAAuB,GACvB,qBAAqB,GACrB,2BAA2B,GAC3B,mCAAmC,GACnC,UAAU,GACV,qBAAqB,GACrB,oBAAoB,GACpB,uBAAuB,CAC1B,CAAC;AAEF,MAAM,WAAW,qBACf,SAAQ,IAAI,CACV,2BAA2B,EAC3B,UAAU,GAAG,SAAS,GAAG,gBAAgB,GAAG,eAAe,GAAG,gBAAgB,GAAG,aAAa,CAC/F;IACD,IAAI,EAAE,cAAc,CAAC;IACrB,gEAAgE;IAChE,UAAU,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;IACjD,gEAAgE;IAChE,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,yBAAyB,CAAC;CACvC;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,SAAS,CA0GxE"}