yaver-feedback-react-native 0.9.7 → 0.9.8
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/README.md +39 -7
- package/android/src/main/java/io/yaver/feedback/YaverDogfoodGestureModule.java +241 -0
- package/android/src/main/java/io/yaver/feedback/YaverHotReloadPackage.java +1 -0
- package/app.plugin.js +12 -2
- package/dist/AuthOverlay.js +13 -2
- package/dist/DogfoodQuickControls.d.ts +8 -0
- package/dist/DogfoodQuickControls.js +394 -0
- package/dist/DogfoodSessionUi.d.ts +50 -0
- package/dist/DogfoodSessionUi.js +135 -0
- package/dist/FeedbackModal.js +50 -22
- package/dist/YaverFeedback.d.ts +35 -1
- package/dist/YaverFeedback.js +302 -12
- package/dist/__tests__/NativeDogfoodShortcut.test.js +14 -0
- package/dist/__tests__/YaverFeedback.test.js +148 -0
- package/dist/auth.d.ts +15 -0
- package/dist/auth.js +41 -2
- package/dist/dogfoodPolicy.d.ts +22 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +8 -2
- package/dist/preferences.d.ts +13 -0
- package/dist/preferences.js +84 -0
- package/ios/YaverDogfoodGesture.m +14 -0
- package/ios/YaverDogfoodGesture.swift +175 -0
- package/package.json +1 -1
- package/src/AuthOverlay.tsx +20 -3
- package/src/DogfoodQuickControls.tsx +454 -0
- package/src/DogfoodSessionUi.tsx +213 -0
- package/src/FeedbackModal.tsx +62 -34
- package/src/YaverFeedback.ts +333 -12
- package/src/__tests__/NativeDogfoodShortcut.test.ts +15 -0
- package/src/__tests__/YaverFeedback.test.ts +155 -0
- package/src/auth.ts +53 -2
- package/src/dogfoodPolicy.ts +22 -1
- package/src/index.ts +13 -1
- package/src/preferences.ts +89 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Pressable, StyleSheet, Text, View } from 'react-native';
|
|
3
|
+
import type {
|
|
4
|
+
DogfoodFailure,
|
|
5
|
+
DogfoodLane,
|
|
6
|
+
DogfoodLaneOption,
|
|
7
|
+
DogfoodLogLine,
|
|
8
|
+
DogfoodPhase,
|
|
9
|
+
} from './DogfoodRuntime';
|
|
10
|
+
|
|
11
|
+
export type DogfoodStatusTone = 'ready' | 'attention' | 'blocked' | 'pending';
|
|
12
|
+
|
|
13
|
+
export interface DogfoodStatusStep {
|
|
14
|
+
key: string;
|
|
15
|
+
label: string;
|
|
16
|
+
detail: string;
|
|
17
|
+
tone: DogfoodStatusTone;
|
|
18
|
+
actionLabel?: string;
|
|
19
|
+
actionDisabled?: boolean;
|
|
20
|
+
expanded?: boolean;
|
|
21
|
+
onAction?: () => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface DogfoodUiColors {
|
|
25
|
+
background: string;
|
|
26
|
+
border: string;
|
|
27
|
+
text: string;
|
|
28
|
+
muted: string;
|
|
29
|
+
accent: string;
|
|
30
|
+
accentSoft: string;
|
|
31
|
+
ready: string;
|
|
32
|
+
attention: string;
|
|
33
|
+
blocked: string;
|
|
34
|
+
console: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const DEFAULT_COLORS: DogfoodUiColors = {
|
|
38
|
+
background: '#111827',
|
|
39
|
+
border: '#334155',
|
|
40
|
+
text: '#f8fafc',
|
|
41
|
+
muted: '#94a3b8',
|
|
42
|
+
accent: '#818cf8',
|
|
43
|
+
accentSoft: '#312e81',
|
|
44
|
+
ready: '#22c55e',
|
|
45
|
+
attention: '#f59e0b',
|
|
46
|
+
blocked: '#ef4444',
|
|
47
|
+
console: '#070b12',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
function resolvedColors(colors?: Partial<DogfoodUiColors>): DogfoodUiColors {
|
|
51
|
+
return { ...DEFAULT_COLORS, ...colors };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function toneColor(tone: DogfoodStatusTone, colors: DogfoodUiColors): string {
|
|
55
|
+
if (tone === 'ready') return colors.ready;
|
|
56
|
+
if (tone === 'blocked') return colors.blocked;
|
|
57
|
+
if (tone === 'attention') return colors.attention;
|
|
58
|
+
return colors.muted;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Shared readiness rail used by Yaver itself and every embedded SDK host. */
|
|
62
|
+
export const DogfoodStatusRail: React.FC<{
|
|
63
|
+
steps: readonly DogfoodStatusStep[];
|
|
64
|
+
colors?: Partial<DogfoodUiColors>;
|
|
65
|
+
}> = ({ steps, colors: colorOverrides }) => {
|
|
66
|
+
const colors = resolvedColors(colorOverrides);
|
|
67
|
+
return (
|
|
68
|
+
<View style={styles.rail} accessibilityLabel="Dogfood session readiness">
|
|
69
|
+
{steps.map((step) => {
|
|
70
|
+
const tone = toneColor(step.tone, colors);
|
|
71
|
+
return (
|
|
72
|
+
<View key={step.key} style={styles.statusRow}>
|
|
73
|
+
<View style={[styles.statusDot, { backgroundColor: tone }]} />
|
|
74
|
+
<View style={styles.statusCopy}>
|
|
75
|
+
<Text style={[styles.statusLabel, { color: colors.text }]}>{step.label}</Text>
|
|
76
|
+
<Text style={[styles.statusDetail, { color: tone }]}>{step.detail}</Text>
|
|
77
|
+
</View>
|
|
78
|
+
{step.actionLabel && step.onAction ? (
|
|
79
|
+
<Pressable
|
|
80
|
+
accessibilityRole="button"
|
|
81
|
+
accessibilityLabel={`${step.actionLabel} ${step.label}`}
|
|
82
|
+
accessibilityState={{ expanded: step.expanded, disabled: step.actionDisabled }}
|
|
83
|
+
disabled={step.actionDisabled}
|
|
84
|
+
onPress={step.onAction}
|
|
85
|
+
style={({ pressed }) => [
|
|
86
|
+
styles.statusAction,
|
|
87
|
+
{ borderColor: colors.border, opacity: step.actionDisabled ? 0.5 : pressed ? 0.7 : 1 },
|
|
88
|
+
]}
|
|
89
|
+
>
|
|
90
|
+
<Text style={[styles.statusActionText, { color: colors.accent }]}>{step.actionLabel}</Text>
|
|
91
|
+
</Pressable>
|
|
92
|
+
) : null}
|
|
93
|
+
</View>
|
|
94
|
+
);
|
|
95
|
+
})}
|
|
96
|
+
</View>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/** One lane selector and one default policy across Yaver, SFMG, and Talos. */
|
|
101
|
+
export const DogfoodLanePicker: React.FC<{
|
|
102
|
+
options: readonly DogfoodLaneOption[];
|
|
103
|
+
selected: DogfoodLane;
|
|
104
|
+
onSelect: (lane: DogfoodLane) => void;
|
|
105
|
+
colors?: Partial<DogfoodUiColors>;
|
|
106
|
+
showUnsupportedReasons?: boolean;
|
|
107
|
+
}> = ({ options, selected, onSelect, colors: colorOverrides, showUnsupportedReasons = true }) => {
|
|
108
|
+
const colors = resolvedColors(colorOverrides);
|
|
109
|
+
return (
|
|
110
|
+
<View accessibilityRole="radiogroup" accessibilityLabel="Dogfood runtime lane">
|
|
111
|
+
<View style={styles.choiceRow}>
|
|
112
|
+
{options.map((option) => {
|
|
113
|
+
const active = selected === option.lane;
|
|
114
|
+
return (
|
|
115
|
+
<Pressable
|
|
116
|
+
key={option.lane}
|
|
117
|
+
disabled={!option.supported}
|
|
118
|
+
onPress={() => onSelect(option.lane)}
|
|
119
|
+
accessibilityRole="radio"
|
|
120
|
+
accessibilityState={{ checked: active, disabled: !option.supported }}
|
|
121
|
+
style={({ pressed }) => [
|
|
122
|
+
styles.choice,
|
|
123
|
+
{
|
|
124
|
+
borderColor: active ? colors.accent : colors.border,
|
|
125
|
+
backgroundColor: active ? colors.accentSoft : colors.background,
|
|
126
|
+
opacity: !option.supported ? 0.45 : pressed ? 0.72 : 1,
|
|
127
|
+
},
|
|
128
|
+
]}
|
|
129
|
+
>
|
|
130
|
+
<Text style={[styles.choiceText, { color: colors.text }, active && styles.choiceTextActive]}>
|
|
131
|
+
{option.label}{option.default ? ' · default' : ''}
|
|
132
|
+
</Text>
|
|
133
|
+
</Pressable>
|
|
134
|
+
);
|
|
135
|
+
})}
|
|
136
|
+
</View>
|
|
137
|
+
{showUnsupportedReasons ? options.filter((option) => !option.supported && option.reason).map((option) => (
|
|
138
|
+
<Text key={`${option.lane}-reason`} style={[styles.reason, { color: colors.muted }]}>
|
|
139
|
+
{option.label}: {option.reason}
|
|
140
|
+
</Text>
|
|
141
|
+
)) : null}
|
|
142
|
+
</View>
|
|
143
|
+
);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
function runtimeTone(phase: DogfoodPhase, colors: DogfoodUiColors): string {
|
|
147
|
+
if (phase === 'ready') return colors.ready;
|
|
148
|
+
if (phase === 'failed') return colors.blocked;
|
|
149
|
+
if (phase === 'idle' || phase === 'stopped') return colors.muted;
|
|
150
|
+
return colors.attention;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Shared second-stage live console. Browser lane deliberately names Browser
|
|
154
|
+
* Logs; Hermes/WebRTC use the same lifecycle and failure/remedy treatment. */
|
|
155
|
+
export const DogfoodLiveConsole: React.FC<{
|
|
156
|
+
lane: DogfoodLane;
|
|
157
|
+
phase: DogfoodPhase;
|
|
158
|
+
message: string;
|
|
159
|
+
logs: readonly DogfoodLogLine[];
|
|
160
|
+
failure?: DogfoodFailure;
|
|
161
|
+
maxLines?: number;
|
|
162
|
+
colors?: Partial<DogfoodUiColors>;
|
|
163
|
+
renderText?: (text: string) => React.ReactNode;
|
|
164
|
+
}> = ({ lane, phase, message, logs, failure, maxLines = 80, colors: colorOverrides, renderText }) => {
|
|
165
|
+
const colors = resolvedColors(colorOverrides);
|
|
166
|
+
const text = logs.slice(-maxLines).map((line) => line.text).join('\n');
|
|
167
|
+
const title = lane === 'browser' ? 'Browser Logs' : lane === 'hermes' ? 'Hermes Logs' : 'WebRTC Logs';
|
|
168
|
+
return (
|
|
169
|
+
<View style={[styles.console, { backgroundColor: colors.console, borderColor: colors.border }]} accessibilityLabel={title}>
|
|
170
|
+
<View style={styles.consoleHeader}>
|
|
171
|
+
<View style={[styles.statusDot, { backgroundColor: runtimeTone(phase, colors) }]} />
|
|
172
|
+
<Text style={[styles.consoleTitle, { color: colors.text }]}>{title}</Text>
|
|
173
|
+
</View>
|
|
174
|
+
<Text style={[styles.consoleStatus, { color: colors.muted }]}>{message}</Text>
|
|
175
|
+
{text ? (
|
|
176
|
+
renderText ? renderText(text) : <Text selectable style={[styles.consoleText, { color: colors.text }]}>{text}</Text>
|
|
177
|
+
) : (
|
|
178
|
+
<Text style={[styles.consoleEmpty, { color: colors.muted }]}>Waiting for the first line from the remote PC…</Text>
|
|
179
|
+
)}
|
|
180
|
+
{failure ? (
|
|
181
|
+
<View style={[styles.failure, { borderColor: colors.blocked }]}>
|
|
182
|
+
<Text style={[styles.failureText, { color: colors.text }]}>{failure.error}</Text>
|
|
183
|
+
<Text style={[styles.failureRemedy, { color: colors.muted }]}>{failure.remedy}</Text>
|
|
184
|
+
</View>
|
|
185
|
+
) : null}
|
|
186
|
+
</View>
|
|
187
|
+
);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const styles = StyleSheet.create({
|
|
191
|
+
rail: { gap: 4 },
|
|
192
|
+
statusRow: { minHeight: 46, flexDirection: 'row', alignItems: 'center', gap: 9 },
|
|
193
|
+
statusDot: { width: 8, height: 8, borderRadius: 4 },
|
|
194
|
+
statusCopy: { flex: 1 },
|
|
195
|
+
statusLabel: { fontSize: 12, fontWeight: '700' },
|
|
196
|
+
statusDetail: { fontSize: 11, lineHeight: 16, marginTop: 1 },
|
|
197
|
+
statusAction: { borderWidth: 1, borderRadius: 8, paddingHorizontal: 10, paddingVertical: 7 },
|
|
198
|
+
statusActionText: { fontSize: 11, fontWeight: '700' },
|
|
199
|
+
choiceRow: { flexDirection: 'row', flexWrap: 'wrap', gap: 8 },
|
|
200
|
+
choice: { borderWidth: 1, borderRadius: 8, paddingHorizontal: 11, paddingVertical: 8 },
|
|
201
|
+
choiceText: { fontSize: 12, fontWeight: '500' },
|
|
202
|
+
choiceTextActive: { fontWeight: '700' },
|
|
203
|
+
reason: { fontSize: 10, lineHeight: 14, marginTop: 5 },
|
|
204
|
+
console: { width: '100%', maxHeight: 320, overflow: 'hidden', marginTop: 10, borderWidth: 1, borderRadius: 10, padding: 11, gap: 7 },
|
|
205
|
+
consoleHeader: { flexDirection: 'row', alignItems: 'center', gap: 7 },
|
|
206
|
+
consoleTitle: { fontSize: 12, fontWeight: '800' },
|
|
207
|
+
consoleStatus: { fontSize: 11, lineHeight: 16 },
|
|
208
|
+
consoleText: { fontFamily: 'monospace', fontSize: 10, lineHeight: 15 },
|
|
209
|
+
consoleEmpty: { fontSize: 10, fontStyle: 'italic' },
|
|
210
|
+
failure: { borderWidth: 1, borderRadius: 8, padding: 9, gap: 4 },
|
|
211
|
+
failureText: { fontSize: 11, fontWeight: '700' },
|
|
212
|
+
failureRemedy: { fontSize: 10, lineHeight: 15 },
|
|
213
|
+
});
|
package/src/FeedbackModal.tsx
CHANGED
|
@@ -38,6 +38,7 @@ import { AuthOverlay } from './AuthOverlay';
|
|
|
38
38
|
import { QuickActionIcon } from './QuickActionIcon';
|
|
39
39
|
import { YaverModeBadge } from './YaverModeBadge';
|
|
40
40
|
import { VibeChatScreen } from './VibeChatScreen';
|
|
41
|
+
import { DogfoodQuickControls } from './DogfoodQuickControls';
|
|
41
42
|
import { DeployPanel } from './DeployPanel';
|
|
42
43
|
import { listReachableDevices, RemoteDevice } from './auth';
|
|
43
44
|
import { reloadActions } from './reloadActions';
|
|
@@ -60,6 +61,7 @@ import {
|
|
|
60
61
|
type DogfoodSnapshot,
|
|
61
62
|
} from './DogfoodRuntime';
|
|
62
63
|
import { createP2PDogfoodDriver } from './P2PDogfoodDriver';
|
|
64
|
+
import { DogfoodLanePicker, DogfoodLiveConsole, DogfoodStatusRail } from './DogfoodSessionUi';
|
|
63
65
|
|
|
64
66
|
/**
|
|
65
67
|
* Simplified feedback modal — launch scope is 3 actions:
|
|
@@ -1149,7 +1151,45 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1149
1151
|
&& (selectedDogfoodRunner.ready || selectedDogfoodRunner.authConfigured);
|
|
1150
1152
|
const dogfoodModelReady = !selectedDogfoodRunner?.models?.length
|
|
1151
1153
|
|| !!preferredModel && selectedDogfoodRunner.models.some((model) => model.id === preferredModel);
|
|
1152
|
-
const
|
|
1154
|
+
const dogfoodLaneChoices = dogfoodLaneOptions(
|
|
1155
|
+
dogfoodProject?.framework || YaverFeedback.getDogfoodOnboarding()?.framework || 'expo',
|
|
1156
|
+
{ nativeRuntimeAvailable: dogfoodNativeAvailable },
|
|
1157
|
+
);
|
|
1158
|
+
const dogfoodLaneReady = dogfoodLaneChoices.some((option) => option.lane === dogfoodLane && option.supported);
|
|
1159
|
+
const dogfoodReadinessSteps = [
|
|
1160
|
+
{
|
|
1161
|
+
key: 'oauth', label: 'Yaver OAuth',
|
|
1162
|
+
detail: YaverFeedback.isAuthed() ? 'Signed in · session saved' : 'Sign in required',
|
|
1163
|
+
tone: YaverFeedback.isAuthed() ? 'ready' as const : 'attention' as const,
|
|
1164
|
+
},
|
|
1165
|
+
{
|
|
1166
|
+
key: 'machine', label: 'Remote PC',
|
|
1167
|
+
detail: machineCard.device ? machineCard.title : 'Choose a reachable development machine',
|
|
1168
|
+
tone: machineCard.device && machineCard.status === 'live' ? 'ready' as const : 'attention' as const,
|
|
1169
|
+
},
|
|
1170
|
+
{
|
|
1171
|
+
key: 'installation', label: 'This installation',
|
|
1172
|
+
detail: dogfoodEnrollment?.status === 'active' ? 'Device key approved' : dogfoodEnrollment?.status || 'Checking device key',
|
|
1173
|
+
tone: dogfoodEnrollment?.status === 'active' ? 'ready' as const
|
|
1174
|
+
: dogfoodEnrollment?.status === 'failed' ? 'blocked' as const : 'pending' as const,
|
|
1175
|
+
},
|
|
1176
|
+
{
|
|
1177
|
+
key: 'runner', label: 'Runner',
|
|
1178
|
+
detail: dogfoodRunnerReady ? selectedDogfoodRunner?.name || preferredRunner || 'Ready' : 'Choose or configure a coding runner',
|
|
1179
|
+
tone: dogfoodRunnerReady ? 'ready' as const : 'attention' as const,
|
|
1180
|
+
},
|
|
1181
|
+
{
|
|
1182
|
+
key: 'model', label: 'Model',
|
|
1183
|
+
detail: dogfoodModelReady ? preferredModel || 'Runner default' : 'Choose a model',
|
|
1184
|
+
tone: dogfoodModelReady ? 'ready' as const : 'attention' as const,
|
|
1185
|
+
},
|
|
1186
|
+
{
|
|
1187
|
+
key: 'lane', label: 'Runtime lane',
|
|
1188
|
+
detail: dogfoodLaneChoices.find((option) => option.lane === dogfoodLane)?.label || dogfoodLane,
|
|
1189
|
+
tone: dogfoodLaneReady ? 'ready' as const : 'blocked' as const,
|
|
1190
|
+
},
|
|
1191
|
+
];
|
|
1192
|
+
const dogfoodStartBlocked = !dogfoodProject || !dogfoodRunnerReady || !dogfoodModelReady || !dogfoodLaneReady;
|
|
1153
1193
|
|
|
1154
1194
|
// Once the user fires off a vibe task, swap the entire modal body
|
|
1155
1195
|
// for the live chat screen. The chat manages its own SSE
|
|
@@ -1160,6 +1200,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1160
1200
|
return (
|
|
1161
1201
|
<>
|
|
1162
1202
|
<AuthOverlay />
|
|
1203
|
+
<DogfoodQuickControls />
|
|
1163
1204
|
<QuickActionIcon />
|
|
1164
1205
|
<YaverModeBadgeGate />
|
|
1165
1206
|
<Modal
|
|
@@ -1201,6 +1242,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1201
1242
|
return (
|
|
1202
1243
|
<>
|
|
1203
1244
|
<AuthOverlay />
|
|
1245
|
+
<DogfoodQuickControls />
|
|
1204
1246
|
<QuickActionIcon />
|
|
1205
1247
|
<YaverModeBadgeGate />
|
|
1206
1248
|
{visible && (
|
|
@@ -1289,6 +1331,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1289
1331
|
<Text style={styles.dogfoodWizardHint}>
|
|
1290
1332
|
OAuth ✓ · machine {machineCard.device ? '✓' : 'required'} · installation {dogfoodEnrollment?.status || 'checking'}
|
|
1291
1333
|
</Text>
|
|
1334
|
+
<DogfoodStatusRail steps={dogfoodReadinessSteps} />
|
|
1292
1335
|
{dogfoodEnrollment?.installationId ? (
|
|
1293
1336
|
<Text selectable style={styles.dogfoodInstallationId}>
|
|
1294
1337
|
This device · {dogfoodEnrollment.installationId}
|
|
@@ -1372,30 +1415,15 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1372
1415
|
</>
|
|
1373
1416
|
) : null}
|
|
1374
1417
|
<Text style={styles.dogfoodStepLabel}>Runtime lane</Text>
|
|
1375
|
-
<
|
|
1376
|
-
{
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
setDogfoodLane(option.lane);
|
|
1385
|
-
const appId = YaverFeedback.getDogfoodOnboarding()?.appId;
|
|
1386
|
-
if (appId) void setPreferredDogfoodLane(appId, option.lane);
|
|
1387
|
-
}}
|
|
1388
|
-
style={[
|
|
1389
|
-
styles.dogfoodChoice,
|
|
1390
|
-
dogfoodLane === option.lane && styles.dogfoodChoiceSelected,
|
|
1391
|
-
!option.supported && styles.actionBtnDisabled,
|
|
1392
|
-
]}
|
|
1393
|
-
accessibilityState={{ disabled: !option.supported, selected: dogfoodLane === option.lane }}
|
|
1394
|
-
>
|
|
1395
|
-
<Text style={[styles.dogfoodChoiceText, dogfoodLane === option.lane && styles.dogfoodChoiceTextSelected]}>{option.label}</Text>
|
|
1396
|
-
</Pressable>
|
|
1397
|
-
))}
|
|
1398
|
-
</View>
|
|
1418
|
+
<DogfoodLanePicker
|
|
1419
|
+
options={dogfoodLaneChoices}
|
|
1420
|
+
selected={dogfoodLane}
|
|
1421
|
+
onSelect={(lane) => {
|
|
1422
|
+
setDogfoodLane(lane);
|
|
1423
|
+
const appId = YaverFeedback.getDogfoodOnboarding()?.appId;
|
|
1424
|
+
if (appId) void setPreferredDogfoodLane(appId, lane);
|
|
1425
|
+
}}
|
|
1426
|
+
/>
|
|
1399
1427
|
<Text style={styles.dogfoodWizardHint}>
|
|
1400
1428
|
{[preferredRunner || 'Choose a coding agent', preferredModel].filter(Boolean).join(' · ')}
|
|
1401
1429
|
</Text>
|
|
@@ -1407,20 +1435,20 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1407
1435
|
busy={!!dogfoodRuntime && !['idle', 'ready', 'failed', 'stopped'].includes(dogfoodRuntime.phase)}
|
|
1408
1436
|
/>
|
|
1409
1437
|
{dogfoodRuntime ? (
|
|
1410
|
-
|
|
1411
|
-
<
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1438
|
+
<>
|
|
1439
|
+
<DogfoodLiveConsole
|
|
1440
|
+
lane={dogfoodRuntime.project.lane}
|
|
1441
|
+
phase={dogfoodRuntime.phase}
|
|
1442
|
+
message={dogfoodRuntime.message}
|
|
1443
|
+
logs={dogfoodRuntime.logs}
|
|
1444
|
+
failure={dogfoodRuntime.failure}
|
|
1445
|
+
/>
|
|
1418
1446
|
{dogfoodRuntime.result?.url ? (
|
|
1419
1447
|
<Pressable onPress={() => void Linking.openURL(dogfoodRuntime.result!.url!)} style={styles.dogfoodOpenPreview}>
|
|
1420
1448
|
<Text style={styles.dogfoodOpenPreviewText}>Open dogfooded app</Text>
|
|
1421
1449
|
</Pressable>
|
|
1422
1450
|
) : null}
|
|
1423
|
-
|
|
1451
|
+
</>
|
|
1424
1452
|
) : null}
|
|
1425
1453
|
</>
|
|
1426
1454
|
)}
|