@ggui-ai/mcp-apps-react-native 0.12.0 → 0.13.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggui-ai/mcp-apps-react-native",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "React Native SDK for ggui — components and hooks for embedding agent-generated UIs in mobile apps.",
5
5
  "keywords": [
6
6
  "ggui",
@@ -58,9 +58,9 @@
58
58
  },
59
59
  "dependencies": {
60
60
  "@modelcontextprotocol/ext-apps": "^1.7.2",
61
- "@ggui-ai/iframe-runtime": "0.12.0",
62
- "@ggui-ai/protocol": "0.12.0",
63
- "@ggui-ai/design": "0.12.0"
61
+ "@ggui-ai/design": "0.13.0",
62
+ "@ggui-ai/protocol": "0.13.0",
63
+ "@ggui-ai/iframe-runtime": "0.13.0"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@modelcontextprotocol/sdk": "^1.27.0",
@@ -3,28 +3,32 @@
3
3
  * shell chrome ("did this generated UI work for you?").
4
4
  *
5
5
  * Platform delta (vs `ggui-react/src/components/UiFeedback.tsx`):
6
- * - DOM elements (`div` / `button` / `form` / `input`) become RN
7
- * primitives (`View` / `Pressable` / `Text` / `TextInput`).
6
+ * - DOM elements (`div` / `button`) become RN primitives
7
+ * (`View` / `Pressable` / `Text`).
8
8
  * - Inline styles with `--ggui-*` CSS-variable hooks become
9
9
  * `StyleSheet.create` constants carrying the same neutral palette
10
10
  * the web copy uses as its variable fallbacks (RN has no CSS
11
11
  * variable layer).
12
12
  * - `data-ggui-ui-feedback*` styling/test hooks become `testID`s
13
13
  * (`ggui-ui-feedback`, `ggui-ui-feedback-verdict-<verdict>`, …).
14
- * - The comment `<form>` submit becomes a Send `Pressable` plus the
15
- * `TextInput`'s `onSubmitEditing` (keyboard return key).
16
14
  * - `role` / `aria-label` become `accessibilityLabel` /
17
15
  * `accessibilityRole` props.
16
+ * - The web copy's monochrome stroked SVG thumbs become 👍 / 👎
17
+ * emoji glyphs in `Text` (#653 lean call: `react-native-svg` is
18
+ * not a dependency of this package and one chrome row does not
19
+ * justify adding it to the lockstep wave; the glyph renders in the
20
+ * platform's emoji face rather than the theme ink — revisit if a
21
+ * ggui icon dependency ever lands for other reasons).
18
22
  * The exported surface is identical to the web copy — pinned by the
19
23
  * twin-parity gate (`DOCUMENTED_DELTA_TWINS`).
20
24
  *
21
- * Three verdicts — Love / Dislike / Other (free-text comment) — plus a
22
- * dismiss control. Entirely host-driven: the component renders NOTHING
23
- * unless the host passes `onUiFeedback`, so deployments that don't
24
- * collect feedback never show a dead affordance. Where the payload
25
- * goes is the host's choice — a logger, an analytics client, a support
26
- * inbox; this component only builds the typed {@link UiFeedbackPayload}
27
- * and hands it over.
25
+ * Two verdicts — thumbs up / thumbs down (#653) — plus a dismiss
26
+ * control. Entirely host-driven: the component renders NOTHING unless
27
+ * the host passes `onUiFeedback`, so deployments that don't collect
28
+ * feedback never show a dead affordance. Where the payload goes is
29
+ * the host's choice — a logger, an analytics client, a support inbox;
30
+ * this component only builds the typed {@link UiFeedbackPayload} and
31
+ * hands it over.
28
32
  *
29
33
  * Zero wire surface: feedback never crosses the agent ↔ UI contract.
30
34
  * It is host-app chrome (Data vs Behavior — the agent cannot observe
@@ -50,20 +54,17 @@
50
54
  * choice safe: omitting `onUiFeedback` fully disables this surface.
51
55
  */
52
56
  import { useCallback, useState } from 'react';
53
- import { Pressable, StyleSheet, Text, TextInput, View } from 'react-native';
57
+ import { Pressable, StyleSheet, Text, View } from 'react-native';
54
58
 
55
- /** The three feedback verdicts the affordance can emit. */
56
- export type UiFeedbackVerdict = 'love' | 'dislike' | 'other';
59
+ /** The two feedback verdicts the affordance can emit. */
60
+ export type UiFeedbackVerdict = 'up' | 'down';
57
61
 
58
62
  /**
59
63
  * Payload handed to {@link UiFeedbackProps.onUiFeedback}. Context
60
- * fields are present exactly when the host supplied them as props;
61
- * `comment` is present only for `verdict: 'other'` with a non-empty
62
- * trimmed comment.
64
+ * fields are present exactly when the host supplied them as props.
63
65
  */
64
66
  export interface UiFeedbackPayload {
65
67
  verdict: UiFeedbackVerdict;
66
- comment?: string;
67
68
  /** GguiSession id of the render the feedback is about. */
68
69
  sessionId?: string;
69
70
  /** Tool that produced the render (e.g. `ggui_render`). */
@@ -82,19 +83,26 @@ export interface UiFeedbackProps {
82
83
  toolName?: string;
83
84
  }
84
85
 
85
- type Phase = 'idle' | 'comment' | 'sent' | 'dismissed';
86
+ type Phase = 'idle' | 'sent' | 'dismissed';
87
+
88
+ const VERDICT_GLYPHS: Record<UiFeedbackVerdict, string> = {
89
+ up: '👍',
90
+ down: '👎',
91
+ };
92
+
93
+ const VERDICT_LABELS: Record<UiFeedbackVerdict, string> = {
94
+ up: 'Thumbs up',
95
+ down: 'Thumbs down',
96
+ };
86
97
 
87
98
  export function UiFeedback({ onUiFeedback, sessionId, toolName }: UiFeedbackProps) {
88
99
  const [phase, setPhase] = useState<Phase>('idle');
89
- const [comment, setComment] = useState('');
90
100
 
91
101
  const emit = useCallback(
92
- (verdict: UiFeedbackVerdict, commentText?: string) => {
102
+ (verdict: UiFeedbackVerdict) => {
93
103
  if (!onUiFeedback) return;
94
- const trimmed = commentText?.trim();
95
104
  onUiFeedback({
96
105
  verdict,
97
- ...(trimmed !== undefined && trimmed.length > 0 ? { comment: trimmed } : {}),
98
106
  ...(sessionId !== undefined ? { sessionId } : {}),
99
107
  ...(toolName !== undefined ? { toolName } : {}),
100
108
  });
@@ -103,10 +111,6 @@ export function UiFeedback({ onUiFeedback, sessionId, toolName }: UiFeedbackProp
103
111
  [onUiFeedback, sessionId, toolName],
104
112
  );
105
113
 
106
- const onCommentSubmit = useCallback(() => {
107
- emit('other', comment);
108
- }, [emit, comment]);
109
-
110
114
  // Hidden entirely: no sink wired, or the user dismissed it.
111
115
  if (!onUiFeedback || phase === 'dismissed') return null;
112
116
 
@@ -120,54 +124,19 @@ export function UiFeedback({ onUiFeedback, sessionId, toolName }: UiFeedbackProp
120
124
  <Text testID="ggui-ui-feedback-thanks" style={styles.thanksText}>
121
125
  Thanks for the feedback
122
126
  </Text>
123
- ) : phase === 'comment' ? (
124
- <View style={styles.commentForm} testID="ggui-ui-feedback-comment-form">
125
- <TextInput
126
- value={comment}
127
- onChangeText={setComment}
128
- onSubmitEditing={onCommentSubmit}
129
- placeholder="What happened?"
130
- accessibilityLabel="Feedback comment"
131
- testID="ggui-ui-feedback-comment"
132
- style={styles.commentInput}
133
- autoFocus
134
- />
135
- <Pressable
136
- accessibilityRole="button"
137
- style={styles.verdictButton}
138
- testID="ggui-ui-feedback-send"
139
- onPress={onCommentSubmit}
140
- >
141
- <Text style={styles.verdictText}>Send</Text>
142
- </Pressable>
143
- </View>
144
127
  ) : (
145
- <>
128
+ (['up', 'down'] as const).map((verdict) => (
146
129
  <Pressable
130
+ key={verdict}
147
131
  accessibilityRole="button"
132
+ accessibilityLabel={VERDICT_LABELS[verdict]}
148
133
  style={styles.verdictButton}
149
- testID="ggui-ui-feedback-verdict-love"
150
- onPress={() => emit('love')}
134
+ testID={`ggui-ui-feedback-verdict-${verdict}`}
135
+ onPress={() => emit(verdict)}
151
136
  >
152
- <Text style={styles.verdictText}>Love</Text>
137
+ <Text style={styles.verdictText}>{VERDICT_GLYPHS[verdict]}</Text>
153
138
  </Pressable>
154
- <Pressable
155
- accessibilityRole="button"
156
- style={styles.verdictButton}
157
- testID="ggui-ui-feedback-verdict-dislike"
158
- onPress={() => emit('dislike')}
159
- >
160
- <Text style={styles.verdictText}>Dislike</Text>
161
- </Pressable>
162
- <Pressable
163
- accessibilityRole="button"
164
- style={styles.verdictButton}
165
- testID="ggui-ui-feedback-verdict-other"
166
- onPress={() => setPhase('comment')}
167
- >
168
- <Text style={styles.verdictText}>Other…</Text>
169
- </Pressable>
170
- </>
139
+ ))
171
140
  )}
172
141
  <Pressable
173
142
  accessibilityRole="button"
@@ -199,12 +168,12 @@ const styles = StyleSheet.create({
199
168
  borderWidth: 1,
200
169
  borderColor: '#e5e7eb',
201
170
  borderRadius: 4,
202
- paddingVertical: 2,
203
- paddingHorizontal: 8,
171
+ paddingVertical: 3,
172
+ paddingHorizontal: 7,
204
173
  },
205
174
  verdictText: {
206
175
  fontSize: 12,
207
- color: '#4b5563',
176
+ lineHeight: 14,
208
177
  },
209
178
  dismissButton: {
210
179
  paddingVertical: 2,
@@ -214,20 +183,4 @@ const styles = StyleSheet.create({
214
183
  fontSize: 12,
215
184
  color: '#9ca3af',
216
185
  },
217
- commentForm: {
218
- flexDirection: 'row',
219
- alignItems: 'center',
220
- gap: 4,
221
- },
222
- commentInput: {
223
- fontSize: 12,
224
- paddingVertical: 2,
225
- paddingHorizontal: 6,
226
- minWidth: 140,
227
- borderWidth: 1,
228
- borderColor: '#e5e7eb',
229
- borderRadius: 4,
230
- backgroundColor: '#ffffff',
231
- color: '#1f2937',
232
- },
233
186
  });