@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,86 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * `<NativeTranscript>` — the plan walker + the §3.2 renderer obligations,
4
+ * restated over React Native's own mechanics:
5
+ *
6
+ * - **Scroll contract, the inverted way.** The list is an INVERTED
7
+ * FlatList over the reversed plan (the chat-app contract portal's
8
+ * agent-chat screen ratified — guuey#94/#100): content grows at the
9
+ * scroll ORIGIN, so "stick to bottom while streaming" and "never shift
10
+ * the viewport while the reader is up in history" hold by construction
11
+ * — no scrollTo calls during streaming, no re-pin race with content
12
+ * resize (an R6 card growing at the origin cannot move the reader's
13
+ * viewport). "Release on scroll-up" is likewise structural; the kit
14
+ * adds the jump-to-latest affordance when the reader has left the
15
+ * newest turn, and its scroll animation respects the platform
16
+ * reduce-motion setting.
17
+ * - **Windowing** is the list primitive's own virtualization — FlatList
18
+ * windows the DOM-equivalent natively; the plan's stable keys are the
19
+ * `keyExtractor`, so item identity survives streaming updates.
20
+ * - **Accessibility** lives on the item components (`components.tsx`).
21
+ *
22
+ * Platform chrome (keyboard insets, dismiss modes, dock scroll policies)
23
+ * stays HOST-OWNED: `listProps` passes the host's FlatList behavior
24
+ * through, with the kit composing `onScroll` so both the host's policy and
25
+ * the jump affordance see every event. The kit owns data/renderItem/keys/
26
+ * inversion — a host overriding those would be fighting the plan.
27
+ */
28
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
29
+ import { AccessibilityInfo, FlatList, Pressable, Text, View, } from "react-native";
30
+ import { DEFAULT_CHAT_THEME } from "../theme.js";
31
+ import { defaultChatStrings } from "../strings.js";
32
+ import { nativeTranscriptComponents, renderNativeItem, } from "./components.js";
33
+ import { resolveNativeTheme } from "./theme-native.js";
34
+ /** Offset (px) past which the reader counts as "up in history". */
35
+ const JUMP_THRESHOLD_PX = 160;
36
+ export function NativeTranscript(props) {
37
+ const { plan, components, strings = defaultChatStrings, theme = DEFAULT_CHAT_THEME, mode = "light", listProps, onToggle, onRetry, onPromptAction, onErrorAction, resolvedMounts, onViewPhase, } = props;
38
+ const tokens = useMemo(() => resolveNativeTheme(theme, mode), [theme, mode]);
39
+ const resolvedComponents = useMemo(() => ({ ...nativeTranscriptComponents, ...components }), [components]);
40
+ const ctx = useMemo(() => ({ strings, tokens, onToggle, onRetry, onPromptAction, onErrorAction, resolvedMounts, onViewPhase }), [strings, tokens, onToggle, onRetry, onPromptAction, onErrorAction, resolvedMounts, onViewPhase]);
41
+ // Inverted-list data: reversed plan, newest at index 0 (the visual
42
+ // bottom / scroll origin). Keys are the plan's stable keys.
43
+ const data = useMemo(() => [...plan.items].reverse(), [plan.items]);
44
+ // Reduce motion: the ONE animation the kit owns is the jump scroll.
45
+ const [reduceMotion, setReduceMotion] = useState(false);
46
+ useEffect(() => {
47
+ let alive = true;
48
+ void AccessibilityInfo.isReduceMotionEnabled().then((v) => {
49
+ if (alive)
50
+ setReduceMotion(v);
51
+ });
52
+ const sub = AccessibilityInfo.addEventListener("reduceMotionChanged", setReduceMotion);
53
+ return () => {
54
+ alive = false;
55
+ sub.remove();
56
+ };
57
+ }, []);
58
+ const listRef = useRef(null);
59
+ const [showJump, setShowJump] = useState(false);
60
+ const hostOnScroll = listProps?.onScroll;
61
+ const onScroll = useCallback((e) => {
62
+ // Inverted coordinates: offset 0 IS the newest turn.
63
+ setShowJump(e.nativeEvent.contentOffset.y > JUMP_THRESHOLD_PX);
64
+ hostOnScroll?.(e);
65
+ }, [hostOnScroll]);
66
+ const StatusComponent = resolvedComponents.status;
67
+ return (_jsxs(View, { style: { flex: 1, backgroundColor: tokens.palette.canvas }, children: [_jsx(FlatList, { ref: listRef, ...listProps, onScroll: onScroll, scrollEventThrottle: listProps?.scrollEventThrottle ?? 16,
68
+ // Inverted only while non-empty: an inverted ListEmptyComponent
69
+ // renders upside-down (RN gotcha — portal's receipt).
70
+ inverted: data.length > 0, data: data, keyExtractor: (item) => item.key, renderItem: ({ item }) => (_jsx(View, { style: { paddingVertical: tokens.gap / 2 }, children: renderNativeItem(item, resolvedComponents, ctx) })),
71
+ // Inverted list: the header renders at the VISUAL BOTTOM — where
72
+ // the live status line belongs.
73
+ ListHeaderComponent: plan.status !== null ? _jsx(StatusComponent, { item: plan.status, ctx: ctx }) : null }), showJump ? (_jsx(Pressable, { accessibilityRole: "button", onPress: () => {
74
+ setShowJump(false);
75
+ listRef.current?.scrollToOffset({ offset: 0, animated: !reduceMotion });
76
+ }, style: {
77
+ position: "absolute",
78
+ bottom: 12,
79
+ alignSelf: "center",
80
+ backgroundColor: tokens.palette.surface,
81
+ borderRadius: 999,
82
+ paddingHorizontal: 14,
83
+ paddingVertical: 8,
84
+ elevation: 3,
85
+ }, children: _jsx(Text, { style: { color: tokens.palette.ink, fontSize: tokens.fontSize - 2, fontFamily: tokens.fontFamily }, children: strings.jumpToLatest }) })) : null] }));
86
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * React Native entry point (`@guuey/chat/native`) — the RN renderer over
3
+ * the SAME headless view-model as `./react` (wave 3c, guuey#135).
4
+ *
5
+ * Zero duplication by construction: the view-model, policies, theme
6
+ * schema, and strings are the root subpath's exports, and the
7
+ * renderer-state hooks (`useTranscript`, `useTranscriptInputs`) are the
8
+ * SAME modules the web kit uses — they are React-generic (no DOM), so both
9
+ * arms share one implementation. Only the walk differs: RN primitives, the
10
+ * inverted-list scroll contract, and the schema's native theme projection
11
+ * (`resolveNativeTheme` — CSS custom properties are the web projection).
12
+ *
13
+ * The R6 default is the documented native default-gap: this tier ships
14
+ * WITHOUT a WebView dependency, so `view` is a required override (a
15
+ * labeled placeholder renders otherwise — never blank). See
16
+ * `native/components.tsx`.
17
+ *
18
+ * `react` and `react-native` are OPTIONAL peers — the root subpath stays
19
+ * importable everywhere; this arm loads only inside an RN runtime.
20
+ */
21
+ export { NativeTranscript, type NativeTranscriptProps, type NativeTranscriptListProps, } from "./native/transcript.js";
22
+ export { nativeTranscriptComponents, renderNativeItem, NativeUserMessage, NativeText, NativeReasoning, NativeTool, NativeToolGroup, NativeDataResult, NativeView, NativeMedia, NativeCode, NativeCitations, NativePrompt, NativeError, NativeHistoryBoundary, NativeCompaction, NativeUnknown, NativeStatus, type NativeTranscriptComponents, type NativeTranscriptItemContext, } from "./native/components.js";
23
+ export { NativeMarkdown } from "./native/markdown.js";
24
+ export { resolveNativeTheme, type NativeChatTokens, type NativeThemeMode, } from "./native/theme-native.js";
25
+ export { useTranscript, useTranscriptInputs, type UseTranscriptArgs, type UseTranscriptResult, type UseTranscriptInputsResult, } from "./react/use-transcript.js";
26
+ //# sourceMappingURL=native.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EACL,gBAAgB,EAChB,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,GAC/B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,0BAA0B,EAC1B,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,eAAe,EACf,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,UAAU,EACV,eAAe,EACf,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,GACjC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,GAC/B,MAAM,2BAA2B,CAAC"}
package/dist/native.js ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * React Native entry point (`@guuey/chat/native`) — the RN renderer over
3
+ * the SAME headless view-model as `./react` (wave 3c, guuey#135).
4
+ *
5
+ * Zero duplication by construction: the view-model, policies, theme
6
+ * schema, and strings are the root subpath's exports, and the
7
+ * renderer-state hooks (`useTranscript`, `useTranscriptInputs`) are the
8
+ * SAME modules the web kit uses — they are React-generic (no DOM), so both
9
+ * arms share one implementation. Only the walk differs: RN primitives, the
10
+ * inverted-list scroll contract, and the schema's native theme projection
11
+ * (`resolveNativeTheme` — CSS custom properties are the web projection).
12
+ *
13
+ * The R6 default is the documented native default-gap: this tier ships
14
+ * WITHOUT a WebView dependency, so `view` is a required override (a
15
+ * labeled placeholder renders otherwise — never blank). See
16
+ * `native/components.tsx`.
17
+ *
18
+ * `react` and `react-native` are OPTIONAL peers — the root subpath stays
19
+ * importable everywhere; this arm loads only inside an RN runtime.
20
+ */
21
+ export { NativeTranscript, } from "./native/transcript.js";
22
+ export { nativeTranscriptComponents, renderNativeItem, NativeUserMessage, NativeText, NativeReasoning, NativeTool, NativeToolGroup, NativeDataResult, NativeView, NativeMedia, NativeCode, NativeCitations, NativePrompt, NativeError, NativeHistoryBoundary, NativeCompaction, NativeUnknown, NativeStatus, } from "./native/components.js";
23
+ export { NativeMarkdown } from "./native/markdown.js";
24
+ export { resolveNativeTheme, } from "./native/theme-native.js";
25
+ // The React-generic renderer-state owner + live assembler — shared with
26
+ // the web kit (one implementation, two walks).
27
+ export { useTranscript, useTranscriptInputs, } from "./react/use-transcript.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@guuey/chat",
3
- "version": "0.4.0",
4
- "description": "The default end-user transcript UI for guuey agents — the headless view-model (planTranscript), calm/debug presets, the GuueyChatTheme token schema, the ChatStrings i18n seam, and (at ./react) the component kit that renders it: per-category components, <Transcript>, the live + history input assemblers, and stick-to-bottom/windowed/accessible transcript behavior.",
3
+ "version": "0.5.0",
4
+ "description": "The default end-user transcript UI for guuey agents — the headless view-model (planTranscript), calm/debug presets, the GuueyChatTheme token schema, the ChatStrings i18n seam, and (at ./react) the component kit that renders it: per-category components, <Transcript>, the live + history input assemblers, and stick-to-bottom/windowed/accessible transcript behavior; at ./native, the React Native renderer over the same view-model.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
@@ -27,21 +27,31 @@
27
27
  "import": "./dist/react.js",
28
28
  "default": "./dist/react.js"
29
29
  },
30
+ "./native": {
31
+ "react-native": "./src/native.tsx",
32
+ "types": "./dist/native.d.ts",
33
+ "import": "./dist/native.js",
34
+ "default": "./dist/native.js"
35
+ },
30
36
  "./styles.css": "./styles.css"
31
37
  },
32
38
  "dependencies": {
33
39
  "@silverprotocol/core": "0.4.1",
34
40
  "@silverprotocol/richtext": "0.4.1",
35
41
  "zod": "^4.3.6",
36
- "@guuey/agent-client": "0.4.0",
37
- "@guuey/mcp-apps-host": "0.4.0"
42
+ "@guuey/agent-client": "0.5.0",
43
+ "@guuey/mcp-apps-host": "0.5.0"
38
44
  },
39
45
  "peerDependencies": {
40
- "react": ">=18"
46
+ "react": ">=18",
47
+ "react-native": ">=0.76"
41
48
  },
42
49
  "peerDependenciesMeta": {
43
50
  "react": {
44
51
  "optional": true
52
+ },
53
+ "react-native": {
54
+ "optional": true
45
55
  }
46
56
  },
47
57
  "devDependencies": {
@@ -52,6 +62,7 @@
52
62
  "jsdom": "^25.0.1",
53
63
  "react": "^19.0.0",
54
64
  "react-dom": "^19.0.0",
65
+ "react-native": "0.81.5",
55
66
  "typescript": "^5.0.0",
56
67
  "vitest": "^3.0.0"
57
68
  },