yaver-feedback-react-native 0.9.10 → 0.9.12
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/dist/AuthOverlay.js +4 -2
- package/dist/DogfoodRuntime.d.ts +26 -0
- package/dist/DogfoodRuntime.js +85 -11
- package/dist/DogfoodSessionUi.d.ts +1 -0
- package/dist/DogfoodSessionUi.js +3 -2
- package/dist/FeedbackModal.js +126 -105
- package/dist/LoginScreen.d.ts +2 -0
- package/dist/LoginScreen.js +2 -2
- package/dist/MachinePickerScreen.d.ts +3 -1
- package/dist/MachinePickerScreen.js +43 -28
- package/dist/__tests__/DogfoodRuntime.test.js +45 -0
- package/dist/__tests__/FeedbackModalContract.test.js +16 -6
- package/dist/__tests__/MachinePickerScreenContract.test.d.ts +1 -0
- package/dist/__tests__/MachinePickerScreenContract.test.js +25 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -2
- package/package.json +1 -1
- package/src/AuthOverlay.tsx +8 -1
- package/src/DogfoodRuntime.ts +108 -11
- package/src/DogfoodSessionUi.tsx +4 -2
- package/src/FeedbackModal.tsx +45 -16
- package/src/LoginScreen.tsx +4 -1
- package/src/MachinePickerScreen.tsx +43 -30
- package/src/__tests__/DogfoodRuntime.test.ts +51 -0
- package/src/__tests__/FeedbackModalContract.test.ts +17 -6
- package/src/__tests__/MachinePickerScreenContract.test.ts +24 -0
- package/src/index.ts +2 -0
package/dist/FeedbackModal.js
CHANGED
|
@@ -316,12 +316,17 @@ const FeedbackModal = () => {
|
|
|
316
316
|
return;
|
|
317
317
|
await dogfoodControllerRef.current?.stop().catch(() => { });
|
|
318
318
|
const framework = dogfoodProject.framework || onboarding.framework || 'expo';
|
|
319
|
+
const lanePlan = (0, DogfoodRuntime_1.dogfoodLanePlan)(framework, {
|
|
320
|
+
nativeRuntimeAvailable: dogfoodNativeAvailable,
|
|
321
|
+
browserRuntimeAvailable: dogfoodBrowserAvailable,
|
|
322
|
+
}, dogfoodLane);
|
|
319
323
|
const controller = new DogfoodRuntime_1.DogfoodController({
|
|
320
324
|
name: dogfoodProject.name,
|
|
321
325
|
workDir: dogfoodProject.path,
|
|
322
326
|
framework,
|
|
323
|
-
lane:
|
|
324
|
-
|
|
327
|
+
lane: lanePlan.preferred,
|
|
328
|
+
fallbackLane: lanePlan.fallback,
|
|
329
|
+
nativeTargetId: lanePlan.preferred === 'webrtc' ? dogfoodNativeTargetId : undefined,
|
|
325
330
|
}, (0, P2PDogfoodDriver_1.createP2PDogfoodDriver)(client), {
|
|
326
331
|
onChange: (snapshot) => { if (mountedRef.current)
|
|
327
332
|
setDogfoodRuntime(snapshot); },
|
|
@@ -415,10 +420,6 @@ const FeedbackModal = () => {
|
|
|
415
420
|
status = 'attention';
|
|
416
421
|
detail = 'Machine needs pairing again before feedback actions can run.';
|
|
417
422
|
}
|
|
418
|
-
else if (device.runnerDown) {
|
|
419
|
-
status = 'attention';
|
|
420
|
-
detail = 'Machine is online but the coding agent is down.';
|
|
421
|
-
}
|
|
422
423
|
else if (reachable === false) {
|
|
423
424
|
status = 'offline';
|
|
424
425
|
detail = 'Machine selected, but the agent is not responding.';
|
|
@@ -533,7 +534,11 @@ const FeedbackModal = () => {
|
|
|
533
534
|
setShowVibeInput(authenticated || directDogfood);
|
|
534
535
|
setVibePrompt('');
|
|
535
536
|
if (onboarding) {
|
|
536
|
-
|
|
537
|
+
// A Dogfood shortcut is an explicit setup/runtime intent. Opening on
|
|
538
|
+
// Chat hid the machine/runner/checkout gate for signed-in SFMG users;
|
|
539
|
+
// keep the SDK-owned Dogfood surface visible, then show its live logs
|
|
540
|
+
// immediately when Start is tapped.
|
|
541
|
+
setActiveTab('settings');
|
|
537
542
|
setDogfoodSetupStage('setup');
|
|
538
543
|
setDogfoodExpandedStep(null);
|
|
539
544
|
setDogfoodRuntime(null);
|
|
@@ -954,10 +959,15 @@ const FeedbackModal = () => {
|
|
|
954
959
|
const dogfoodModelReady = !selectedDogfoodRunner?.models?.length
|
|
955
960
|
|| !!preferredModel && selectedDogfoodRunner.models.some((model) => model.id === preferredModel);
|
|
956
961
|
const dogfoodFramework = dogfoodProject?.framework || YaverFeedback_1.YaverFeedback.getDogfoodOnboarding()?.framework || 'expo';
|
|
962
|
+
const dogfoodOnboarding = YaverFeedback_1.YaverFeedback.getDogfoodOnboarding();
|
|
957
963
|
const dogfoodLaneChoices = (0, DogfoodRuntime_1.dogfoodLaneOptions)(dogfoodFramework, {
|
|
958
964
|
nativeRuntimeAvailable: dogfoodNativeAvailable,
|
|
959
965
|
browserRuntimeAvailable: dogfoodBrowserAvailable,
|
|
960
966
|
});
|
|
967
|
+
const dogfoodLanePolicy = (0, DogfoodRuntime_1.dogfoodLanePlan)(dogfoodFramework, {
|
|
968
|
+
nativeRuntimeAvailable: dogfoodNativeAvailable,
|
|
969
|
+
browserRuntimeAvailable: dogfoodBrowserAvailable,
|
|
970
|
+
}, dogfoodLane);
|
|
961
971
|
const selectedDogfoodNativeTarget = dogfoodNativeTargets.find((target) => target.id === dogfoodNativeTargetId) || null;
|
|
962
972
|
const dogfoodLaneReady = dogfoodLaneChoices.some((option) => option.lane === dogfoodLane && option.supported)
|
|
963
973
|
&& (dogfoodLane !== 'webrtc' || !!selectedDogfoodNativeTarget?.enabled);
|
|
@@ -994,11 +1004,12 @@ const FeedbackModal = () => {
|
|
|
994
1004
|
},
|
|
995
1005
|
];
|
|
996
1006
|
const dogfoodStartBlocked = !dogfoodSetupReady || !dogfoodLaneReady;
|
|
997
|
-
const
|
|
1007
|
+
const activeDogfoodLane = dogfoodRuntime?.project.lane || dogfoodLane;
|
|
1008
|
+
const dogfoodSourceLabel = activeDogfoodLane === 'webrtc'
|
|
998
1009
|
? selectedDogfoodNativeTarget
|
|
999
1010
|
? [selectedDogfoodNativeTarget.label, selectedDogfoodNativeTarget.platform].filter(Boolean).join(' · ')
|
|
1000
1011
|
: 'Native simulator, emulator, or device'
|
|
1001
|
-
:
|
|
1012
|
+
: activeDogfoodLane === 'hermes'
|
|
1002
1013
|
? `Hermes build · ${machineCard.title}`
|
|
1003
1014
|
: `${dogfoodFramework === 'flutter' ? 'Flutter web compiler' : 'Metro / browser build'} · ${machineCard.title}`;
|
|
1004
1015
|
// Once the user fires off a vibe task, swap the entire modal body
|
|
@@ -1031,7 +1042,7 @@ const FeedbackModal = () => {
|
|
|
1031
1042
|
<QuickActionIcon_1.QuickActionIcon />
|
|
1032
1043
|
<YaverModeBadgeGate />
|
|
1033
1044
|
{visible && (<react_native_1.Modal visible={visible} animationType="slide" transparent onRequestClose={handleClose}>
|
|
1034
|
-
<react_native_1.
|
|
1045
|
+
<react_native_1.KeyboardAvoidingView style={styles.overlay} behavior={react_native_1.Platform.OS === 'ios' ? 'padding' : 'height'}>
|
|
1035
1046
|
<react_native_1.Pressable style={styles.backdrop} onPress={handleClose} accessibilityLabel="Close feedback"/>
|
|
1036
1047
|
<react_native_1.View
|
|
1037
1048
|
// Tablet: cap modal width and center as a card-style
|
|
@@ -1049,29 +1060,37 @@ const FeedbackModal = () => {
|
|
|
1049
1060
|
}
|
|
1050
1061
|
: null,
|
|
1051
1062
|
]}>
|
|
1052
|
-
<react_native_1.ScrollView ref={scrollRef} style={styles.scroll} contentContainerStyle={styles.scrollContent} keyboardShouldPersistTaps="handled" keyboardDismissMode={react_native_1.Platform.OS === 'ios' ? 'interactive' : 'on-drag'} contentInsetAdjustmentBehavior="always"
|
|
1063
|
+
<react_native_1.ScrollView ref={scrollRef} style={styles.scroll} contentContainerStyle={styles.scrollContent} keyboardShouldPersistTaps="handled" keyboardDismissMode={react_native_1.Platform.OS === 'ios' ? 'interactive' : 'on-drag'} contentInsetAdjustmentBehavior="always"
|
|
1064
|
+
// KeyboardAvoidingView is the single inset owner. Letting the
|
|
1065
|
+
// ScrollView add a second UIKit inset lifts the composer twice
|
|
1066
|
+
// on short phones and still leaves its action row clipped.
|
|
1067
|
+
automaticallyAdjustKeyboardInsets={false}>
|
|
1053
1068
|
<react_native_1.View style={styles.header}>
|
|
1054
1069
|
<react_native_1.Text style={styles.title}>
|
|
1055
|
-
{
|
|
1070
|
+
{dogfoodOnboarding
|
|
1071
|
+
? `Set up ${dogfoodOnboarding.projectName || dogfoodOnboarding.label || 'this app'} Dogfood`
|
|
1072
|
+
: dogfoodActive
|
|
1073
|
+
? `${YaverFeedback_1.YaverFeedback.getDogfoodStatus().label || 'App'} Developer Mode`
|
|
1074
|
+
: 'Send Feedback'}
|
|
1056
1075
|
</react_native_1.Text>
|
|
1057
1076
|
<react_native_1.Pressable onPress={handleClose} hitSlop={12} style={styles.closeBtn} accessibilityRole="button" accessibilityLabel="Close">
|
|
1058
1077
|
<react_native_1.Text style={styles.closeIcon}>×</react_native_1.Text>
|
|
1059
1078
|
</react_native_1.Pressable>
|
|
1060
1079
|
</react_native_1.View>
|
|
1061
1080
|
|
|
1062
|
-
<react_native_1.View style={styles.tabs} accessibilityRole="tablist">
|
|
1081
|
+
{(!dogfoodOnboarding || dogfoodSetupStage === 'runtime') ? (<react_native_1.View style={styles.tabs} accessibilityRole="tablist">
|
|
1063
1082
|
{['chat', 'settings'].map((tab) => (<react_native_1.Pressable key={tab} onPress={() => setActiveTab(tab)} style={[styles.tab, activeTab === tab && styles.tabSelected]} accessibilityRole="tab" accessibilityState={{ selected: activeTab === tab }}>
|
|
1064
1083
|
<react_native_1.Text style={[styles.tabText, activeTab === tab && styles.tabTextSelected]}>{tab === 'chat' ? 'Chat' : 'Settings'}</react_native_1.Text>
|
|
1065
1084
|
</react_native_1.Pressable>))}
|
|
1066
|
-
</react_native_1.View>
|
|
1085
|
+
</react_native_1.View>) : null}
|
|
1067
1086
|
|
|
1068
1087
|
<react_native_1.View style={[styles.tabContent, activeTab !== 'settings' && styles.hidden]}>
|
|
1069
1088
|
<>
|
|
1070
|
-
{
|
|
1071
|
-
<react_native_1.Text style={styles.dogfoodWizardTitle}>
|
|
1089
|
+
{dogfoodOnboarding ? (<react_native_1.View style={styles.dogfoodWizard}>
|
|
1090
|
+
<react_native_1.Text style={styles.dogfoodWizardTitle}>Connect the app to its checkout</react_native_1.Text>
|
|
1072
1091
|
<react_native_1.Text style={styles.dogfoodWizardHint}>
|
|
1073
1092
|
{dogfoodEnrollment?.status === 'active'
|
|
1074
|
-
? '
|
|
1093
|
+
? 'Choose one development machine, coding runner, and checkout. Then choose how to preview it.'
|
|
1075
1094
|
: `Signed in · installation ${dogfoodEnrollment?.status || 'checking'}`}
|
|
1076
1095
|
</react_native_1.Text>
|
|
1077
1096
|
{dogfoodEnrollment?.status === 'active' && dogfoodSetupStage === 'setup' ? (<DogfoodSessionUi_1.DogfoodStatusRail steps={dogfoodSetupSteps} colors={FeedbackModalTheme_1.FEEDBACK_DOGFOOD_LIGHT_COLORS}/>) : null}
|
|
@@ -1125,7 +1144,7 @@ const FeedbackModal = () => {
|
|
|
1125
1144
|
<react_native_1.Text style={styles.dogfoodProjectDetail}>{dogfoodCheckoutDetail(project)}</react_native_1.Text>
|
|
1126
1145
|
</react_native_1.Pressable>))}
|
|
1127
1146
|
</react_native_1.View>) : null}
|
|
1128
|
-
<ActionRow label=
|
|
1147
|
+
<ActionRow label={dogfoodSetupReady ? 'Continue to runtime' : 'Complete the choices above'} tint="#5645d8" onPress={() => {
|
|
1129
1148
|
setDogfoodExpandedStep(null);
|
|
1130
1149
|
setDogfoodSetupStage('lane');
|
|
1131
1150
|
}} disabled={!dogfoodSetupReady}/>
|
|
@@ -1141,7 +1160,7 @@ const FeedbackModal = () => {
|
|
|
1141
1160
|
<react_native_1.Text style={styles.dogfoodSmallActionText}>Back</react_native_1.Text>
|
|
1142
1161
|
</react_native_1.Pressable>
|
|
1143
1162
|
</react_native_1.View>
|
|
1144
|
-
<DogfoodSessionUi_1.DogfoodLanePicker options={dogfoodLaneChoices} selected={dogfoodLane} colors={FeedbackModalTheme_1.FEEDBACK_DOGFOOD_LIGHT_COLORS} onSelect={(lane) => {
|
|
1163
|
+
<DogfoodSessionUi_1.DogfoodLanePicker options={dogfoodLaneChoices} selected={dogfoodLane} fallbackLane={dogfoodLanePolicy.fallback} colors={FeedbackModalTheme_1.FEEDBACK_DOGFOOD_LIGHT_COLORS} onSelect={(lane) => {
|
|
1145
1164
|
setDogfoodLane(lane);
|
|
1146
1165
|
const appId = YaverFeedback_1.YaverFeedback.getDogfoodOnboarding()?.appId;
|
|
1147
1166
|
if (appId)
|
|
@@ -1186,26 +1205,27 @@ const FeedbackModal = () => {
|
|
|
1186
1205
|
</>) : null}
|
|
1187
1206
|
</>)}
|
|
1188
1207
|
</react_native_1.View>) : null}
|
|
1208
|
+
{!dogfoodOnboarding ? <>
|
|
1189
1209
|
<react_native_1.Pressable onPress={() => {
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1210
|
+
if (!YaverFeedback_1.YaverFeedback.isAuthed()) {
|
|
1211
|
+
YaverFeedback_1.YaverFeedback.showLogin();
|
|
1212
|
+
return;
|
|
1213
|
+
}
|
|
1214
|
+
YaverFeedback_1.YaverFeedback.showMachinePicker();
|
|
1215
|
+
}} style={[
|
|
1216
|
+
styles.machineCard,
|
|
1217
|
+
machineCard.status === 'live' && styles.machineCardLive,
|
|
1218
|
+
machineCard.status === 'attention' && styles.machineCardAttention,
|
|
1219
|
+
machineCard.status === 'offline' && styles.machineCardOffline,
|
|
1220
|
+
]}>
|
|
1201
1221
|
<react_native_1.View style={styles.machineHeader}>
|
|
1202
1222
|
<react_native_1.View style={styles.machineTitleWrap}>
|
|
1203
1223
|
<react_native_1.View style={[
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1224
|
+
styles.machineDot,
|
|
1225
|
+
machineCard.status === 'live' && styles.machineDotLive,
|
|
1226
|
+
machineCard.status === 'attention' && styles.machineDotAttention,
|
|
1227
|
+
machineCard.status === 'offline' && styles.machineDotOffline,
|
|
1228
|
+
]}/>
|
|
1209
1229
|
<react_native_1.Text style={styles.machineLabel}>Coding Machine</react_native_1.Text>
|
|
1210
1230
|
</react_native_1.View>
|
|
1211
1231
|
<react_native_1.Text style={styles.machineAction}>
|
|
@@ -1235,14 +1255,14 @@ const FeedbackModal = () => {
|
|
|
1235
1255
|
<react_native_1.Text style={styles.runnerSectionTitle}>Coding Agents</react_native_1.Text>
|
|
1236
1256
|
<react_native_1.Text style={styles.runnerSectionSubtitle}>
|
|
1237
1257
|
{runnerStatusLoading
|
|
1238
|
-
|
|
1239
|
-
|
|
1258
|
+
? 'Refreshing runner status on the selected machine…'
|
|
1259
|
+
: `${readyRunnerCount} ready · ${needsAuthRunnerCount} need sign-in · ${missingRunnerCount} missing`}
|
|
1240
1260
|
</react_native_1.Text>
|
|
1241
1261
|
</react_native_1.View>
|
|
1242
1262
|
<react_native_1.Pressable onPress={() => void loadRunnerStatuses()} style={({ pressed }) => [
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1263
|
+
styles.runnerRefreshBtn,
|
|
1264
|
+
pressed && styles.buttonPressed,
|
|
1265
|
+
]} accessibilityRole="button" accessibilityLabel="Refresh coding-agent status">
|
|
1246
1266
|
<react_native_1.Text style={styles.runnerRefreshBtnText}>
|
|
1247
1267
|
{runnerStatusLoading ? 'Refreshing…' : 'Refresh'}
|
|
1248
1268
|
</react_native_1.Text>
|
|
@@ -1257,31 +1277,31 @@ const FeedbackModal = () => {
|
|
|
1257
1277
|
</react_native_1.View>
|
|
1258
1278
|
|
|
1259
1279
|
{runnerCards.map((row) => (<react_native_1.View key={row.id} style={[
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1280
|
+
styles.runnerCard,
|
|
1281
|
+
row.tone === 'ok' && styles.runnerCardOk,
|
|
1282
|
+
row.tone === 'warning' && styles.runnerCardWarning,
|
|
1283
|
+
row.tone === 'error' && styles.runnerCardError,
|
|
1284
|
+
]}>
|
|
1265
1285
|
<react_native_1.View style={styles.runnerCardTop}>
|
|
1266
1286
|
<react_native_1.View style={{ flex: 1 }}>
|
|
1267
1287
|
<react_native_1.Text style={styles.runnerCardTitle}>{row.name}</react_native_1.Text>
|
|
1268
1288
|
<react_native_1.Text style={[
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1289
|
+
styles.runnerCardStatus,
|
|
1290
|
+
row.tone === 'ok' && styles.runnerCardStatusOk,
|
|
1291
|
+
row.tone === 'warning' && styles.runnerCardStatusWarning,
|
|
1292
|
+
row.tone === 'error' && styles.runnerCardStatusError,
|
|
1293
|
+
]}>
|
|
1274
1294
|
{row.statusLine}
|
|
1275
1295
|
</react_native_1.Text>
|
|
1276
1296
|
</react_native_1.View>
|
|
1277
1297
|
<react_native_1.View style={styles.runnerCardActions}>
|
|
1278
1298
|
{row.ready ? (<react_native_1.Pressable onPress={() => {
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1299
|
+
const nextModel = row.models?.find((model) => model.isDefault)?.id || row.models?.[0]?.id || '';
|
|
1300
|
+
setPreferredRunnerState(row.id);
|
|
1301
|
+
setPreferredModelState(nextModel);
|
|
1302
|
+
void (0, preferences_1.setPreferredRunner)(row.id);
|
|
1303
|
+
void (0, preferences_1.setPreferredModel)(nextModel || null);
|
|
1304
|
+
}} style={({ pressed }) => [styles.runnerActionBtn, preferredRunner === row.id && styles.runnerActionBtnSelected, pressed && styles.buttonPressed]} accessibilityRole="button" accessibilityLabel={`Use ${row.name} for Vibing`}>
|
|
1285
1305
|
<react_native_1.Text style={styles.runnerActionBtnText}>{preferredRunner === row.id ? 'Using' : 'Use'}</react_native_1.Text>
|
|
1286
1306
|
</react_native_1.Pressable>) : null}
|
|
1287
1307
|
{row.actionRunner ? (<react_native_1.Pressable onPress={() => setRunnerAuthModal(row.actionRunner ?? null)} style={({ pressed }) => [styles.runnerActionBtn, pressed && styles.buttonPressed]} accessibilityRole="button" accessibilityLabel={`${row.actionLabel} ${row.name}`}>
|
|
@@ -1291,9 +1311,9 @@ const FeedbackModal = () => {
|
|
|
1291
1311
|
</react_native_1.View>
|
|
1292
1312
|
{preferredRunner === row.id && (row.models?.length || 0) > 0 ? (<react_native_1.ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.modelChoiceRow}>
|
|
1293
1313
|
{row.models.map((model) => (<react_native_1.Pressable key={model.id} onPress={() => {
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1314
|
+
setPreferredModelState(model.id);
|
|
1315
|
+
void (0, preferences_1.setPreferredModel)(model.id);
|
|
1316
|
+
}} style={[styles.modelChoice, preferredModel === model.id && styles.modelChoiceSelected]} accessibilityRole="button" accessibilityLabel={`Use ${model.name || model.id} model`}>
|
|
1297
1317
|
<react_native_1.Text style={[styles.modelChoiceText, preferredModel === model.id && styles.modelChoiceTextSelected]}>{model.name || model.id}</react_native_1.Text>
|
|
1298
1318
|
</react_native_1.Pressable>))}
|
|
1299
1319
|
</react_native_1.ScrollView>) : null}
|
|
@@ -1308,22 +1328,22 @@ const FeedbackModal = () => {
|
|
|
1308
1328
|
Quick access icon is hidden. Shake the phone if you want feedback back fast.
|
|
1309
1329
|
</react_native_1.Text>
|
|
1310
1330
|
<react_native_1.Pressable onPress={() => {
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1331
|
+
void YaverFeedback_1.YaverFeedback.setQuickIconVisible(true);
|
|
1332
|
+
setQuickIconHidden(false);
|
|
1333
|
+
}} style={({ pressed }) => [
|
|
1334
|
+
styles.quickIconToggle,
|
|
1335
|
+
pressed && styles.buttonPressed,
|
|
1336
|
+
]}>
|
|
1317
1337
|
<react_native_1.Text style={styles.quickIconToggleText}>Show quick icon again</react_native_1.Text>
|
|
1318
1338
|
</react_native_1.Pressable>
|
|
1319
1339
|
</react_native_1.View>)}
|
|
1320
1340
|
|
|
1321
1341
|
{YaverFeedback_1.YaverFeedback.isAuthed() ? (<react_native_1.Pressable onPress={() => {
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1342
|
+
void YaverFeedback_1.YaverFeedback.signOut().then(() => {
|
|
1343
|
+
handleClose();
|
|
1344
|
+
YaverFeedback_1.YaverFeedback.showLogin();
|
|
1345
|
+
});
|
|
1346
|
+
}} style={({ pressed }) => [styles.yaverSignOutBtn, pressed && styles.buttonPressed]} accessibilityRole="button" accessibilityLabel="Sign out of Yaver">
|
|
1327
1347
|
<react_native_1.Text style={styles.yaverSignOutText}>Sign out of Yaver</react_native_1.Text>
|
|
1328
1348
|
</react_native_1.Pressable>) : null}
|
|
1329
1349
|
|
|
@@ -1334,57 +1354,58 @@ const FeedbackModal = () => {
|
|
|
1334
1354
|
</react_native_1.Text>
|
|
1335
1355
|
<react_native_1.View style={styles.iconSelectorGrid}>
|
|
1336
1356
|
{Object.entries(preferences_1.QUICK_ICON_COLOR_PRESETS).map(([preset, colors]) => {
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1357
|
+
const selected = quickIconColorPreset === preset;
|
|
1358
|
+
return (<react_native_1.Pressable key={preset} onPress={() => {
|
|
1359
|
+
setQuickIconColorPreset(preset);
|
|
1360
|
+
void YaverFeedback_1.YaverFeedback.setQuickIconColorPreset(preset);
|
|
1361
|
+
}} style={[
|
|
1362
|
+
styles.iconOption,
|
|
1363
|
+
iconOptionWidthOverride ? { width: iconOptionWidthOverride } : null,
|
|
1364
|
+
selected && styles.iconOptionSelected,
|
|
1365
|
+
]}>
|
|
1346
1366
|
<react_native_1.View style={[
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1367
|
+
styles.iconOptionCircle,
|
|
1368
|
+
{
|
|
1369
|
+
backgroundColor: colors.backgroundColor,
|
|
1370
|
+
borderColor: colors.borderColor,
|
|
1371
|
+
shadowColor: colors.shadowColor,
|
|
1372
|
+
},
|
|
1373
|
+
]}>
|
|
1354
1374
|
<react_native_1.Text style={[
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1375
|
+
styles.iconOptionLabel,
|
|
1376
|
+
{ color: colors.foregroundColor },
|
|
1377
|
+
]}>
|
|
1358
1378
|
y
|
|
1359
1379
|
</react_native_1.Text>
|
|
1360
1380
|
</react_native_1.View>
|
|
1361
1381
|
<react_native_1.Text style={styles.iconOptionText}>{colors.label}</react_native_1.Text>
|
|
1362
1382
|
</react_native_1.Pressable>);
|
|
1363
|
-
|
|
1383
|
+
})}
|
|
1364
1384
|
</react_native_1.View>
|
|
1365
1385
|
</react_native_1.View>
|
|
1366
1386
|
|
|
1367
1387
|
{/* 1. Reload — Hot / Full / Rebuild Bundle.
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1388
|
+
Rendered from the shared decision seam, so a production
|
|
1389
|
+
build (__DEV__ false) renders nothing here at all, and a
|
|
1390
|
+
blocked action shows greyed WITH its reason underneath
|
|
1391
|
+
rather than vanishing. */}
|
|
1372
1392
|
{availableReloadActions.map((reloadAction) => (<react_native_1.View key={reloadAction.id} style={styles.reloadRow}>
|
|
1373
1393
|
<ActionRow label={reloadingId === reloadAction.id
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1394
|
+
? `${reloadAction.label}…`
|
|
1395
|
+
: reloadAction.label} tint={reloadAction.id === 'rebuild' ? '#0369a1' : '#9a5700'} onPress={() => {
|
|
1396
|
+
void handleReloadAction(reloadAction);
|
|
1397
|
+
}}
|
|
1398
|
+
// Never `disabled` at the Pressable level for a blocked
|
|
1399
|
+
// action: we WANT the tap so we can say why. Only a
|
|
1400
|
+
// genuinely busy modal blocks the press.
|
|
1401
|
+
disabled={busy && reloadingId !== reloadAction.id} busy={reloadingId === reloadAction.id}/>
|
|
1382
1402
|
<react_native_1.Text style={styles.reloadHint}>
|
|
1383
1403
|
{reloadAction.enabled
|
|
1384
|
-
|
|
1385
|
-
|
|
1404
|
+
? reloadAction.hint
|
|
1405
|
+
: reloadAction.disabledReason}
|
|
1386
1406
|
</react_native_1.Text>
|
|
1387
1407
|
</react_native_1.View>))}
|
|
1408
|
+
</> : null}
|
|
1388
1409
|
</>
|
|
1389
1410
|
</react_native_1.View>
|
|
1390
1411
|
|
|
@@ -1429,7 +1450,7 @@ const FeedbackModal = () => {
|
|
|
1429
1450
|
</react_native_1.Pressable>
|
|
1430
1451
|
</react_native_1.ScrollView>
|
|
1431
1452
|
</react_native_1.View>
|
|
1432
|
-
</react_native_1.
|
|
1453
|
+
</react_native_1.KeyboardAvoidingView>
|
|
1433
1454
|
</react_native_1.Modal>)}
|
|
1434
1455
|
{runnerAuthModal ? (<RunnerAuthNativeModal runner={runnerAuthModal} onClose={() => {
|
|
1435
1456
|
setRunnerAuthModal(null);
|
package/dist/LoginScreen.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export interface YaverLoginScreenProps {
|
|
|
4
4
|
onLoggedIn: (token: string) => void;
|
|
5
5
|
/** Optional cancel button shown in header. */
|
|
6
6
|
onCancel?: () => void;
|
|
7
|
+
/** Intent shown under the Yaver mark. Defaults to ordinary feedback. */
|
|
8
|
+
subtitle?: string;
|
|
7
9
|
}
|
|
8
10
|
/**
|
|
9
11
|
* Full-screen in-SDK login. Mirrors the Yaver mobile app login UX: native
|
package/dist/LoginScreen.js
CHANGED
|
@@ -91,7 +91,7 @@ const iconStyles = react_native_1.StyleSheet.create({
|
|
|
91
91
|
* Apple Sign-In on iOS, in-app browser OAuth for Google/GitHub/GitLab/
|
|
92
92
|
* Microsoft (no codes, no leaving the app), and inline email/password.
|
|
93
93
|
*/
|
|
94
|
-
const YaverLoginScreen = ({ onLoggedIn, onCancel, }) => {
|
|
94
|
+
const YaverLoginScreen = ({ onLoggedIn, onCancel, subtitle = 'Sign in to send feedback', }) => {
|
|
95
95
|
const [busyProvider, setBusyProvider] = (0, react_1.useState)(null);
|
|
96
96
|
const [showEmailForm, setShowEmailForm] = (0, react_1.useState)(false);
|
|
97
97
|
const [isSignUp, setIsSignUp] = (0, react_1.useState)(false);
|
|
@@ -186,7 +186,7 @@ const YaverLoginScreen = ({ onLoggedIn, onCancel, }) => {
|
|
|
186
186
|
|
|
187
187
|
<react_native_1.View style={styles.header}>
|
|
188
188
|
<react_native_1.Text style={styles.logo}>Yaver</react_native_1.Text>
|
|
189
|
-
<react_native_1.Text style={styles.subtitle}>
|
|
189
|
+
<react_native_1.Text style={styles.subtitle}>{subtitle}</react_native_1.Text>
|
|
190
190
|
</react_native_1.View>
|
|
191
191
|
|
|
192
192
|
<react_native_1.View style={styles.buttons}>
|
|
@@ -4,8 +4,10 @@ export interface YaverMachinePickerProps {
|
|
|
4
4
|
token: string;
|
|
5
5
|
/** Currently-selected deviceId (from config / cache) — highlighted. */
|
|
6
6
|
currentDeviceId?: string;
|
|
7
|
-
onPick: (device: RemoteDevice) => void
|
|
7
|
+
onPick: (device: RemoteDevice) => void | Promise<void>;
|
|
8
8
|
onCancel?: () => void;
|
|
9
|
+
/** Optional flow-specific title, e.g. "Choose a machine for SFMG". */
|
|
10
|
+
title?: string;
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* List of remote dev machines owned by the signed-in user.
|
|
@@ -45,14 +45,18 @@ const PairDeviceModal_1 = require("./PairDeviceModal");
|
|
|
45
45
|
* SDK then uses that device's deviceId for agent discovery (LAN probe +
|
|
46
46
|
* relay fallback through Convex).
|
|
47
47
|
*/
|
|
48
|
-
const YaverMachinePickerScreen = ({ token, currentDeviceId, onPick, onCancel, }) => {
|
|
48
|
+
const YaverMachinePickerScreen = ({ token, currentDeviceId, onPick, onCancel, title = 'Choose a machine', }) => {
|
|
49
49
|
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
50
50
|
const [refreshing, setRefreshing] = (0, react_1.useState)(false);
|
|
51
51
|
const [error, setError] = (0, react_1.useState)(null);
|
|
52
52
|
const [list, setList] = (0, react_1.useState)({ owned: [] });
|
|
53
53
|
const [pairingDevice, setPairingDevice] = (0, react_1.useState)(null);
|
|
54
54
|
const [reachability, setReachability] = (0, react_1.useState)({});
|
|
55
|
+
const [selectingDeviceId, setSelectingDeviceId] = (0, react_1.useState)(null);
|
|
56
|
+
const mountedRef = (0, react_1.useRef)(true);
|
|
57
|
+
const loadGenerationRef = (0, react_1.useRef)(0);
|
|
55
58
|
const load = (0, react_1.useCallback)(async (silent = false) => {
|
|
59
|
+
const generation = ++loadGenerationRef.current;
|
|
56
60
|
if (!silent)
|
|
57
61
|
setLoading(true);
|
|
58
62
|
setError(null);
|
|
@@ -60,22 +64,17 @@ const YaverMachinePickerScreen = ({ token, currentDeviceId, onPick, onCancel, })
|
|
|
60
64
|
const result = await (0, auth_1.listReachableDevices)(token);
|
|
61
65
|
setList(result);
|
|
62
66
|
setReachability({});
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return next;
|
|
77
|
-
});
|
|
78
|
-
})();
|
|
67
|
+
// Convex already gives us a fresh, heartbeat-gated online answer. Render
|
|
68
|
+
// that immediately. Only probe cloud-offline rows to detect a phone-LAN
|
|
69
|
+
// route that came back before the next heartbeat, and publish each result
|
|
70
|
+
// as it arrives instead of holding the whole list behind the slowest box.
|
|
71
|
+
for (const device of result.owned.filter((candidate) => !candidate.isOnline)) {
|
|
72
|
+
void (0, auth_1.probeDeviceReachability)(device).then((probe) => {
|
|
73
|
+
if (!mountedRef.current || loadGenerationRef.current !== generation)
|
|
74
|
+
return;
|
|
75
|
+
setReachability((prev) => ({ ...prev, [device.deviceId]: probe }));
|
|
76
|
+
}).catch(() => { });
|
|
77
|
+
}
|
|
79
78
|
if (result.owned.length === 0) {
|
|
80
79
|
setError('No machines found yet. Run `yaver auth` + `yaver serve` on your machine.');
|
|
81
80
|
}
|
|
@@ -89,7 +88,9 @@ const YaverMachinePickerScreen = ({ token, currentDeviceId, onPick, onCancel, })
|
|
|
89
88
|
}
|
|
90
89
|
}, [token]);
|
|
91
90
|
(0, react_1.useEffect)(() => {
|
|
91
|
+
mountedRef.current = true;
|
|
92
92
|
void load();
|
|
93
|
+
return () => { mountedRef.current = false; };
|
|
93
94
|
}, [load]);
|
|
94
95
|
const handlePick = async (device) => {
|
|
95
96
|
// Needs-auth device — show the in-SDK pair modal instead of
|
|
@@ -101,7 +102,9 @@ const YaverMachinePickerScreen = ({ token, currentDeviceId, onPick, onCancel, })
|
|
|
101
102
|
setPairingDevice(device);
|
|
102
103
|
return;
|
|
103
104
|
}
|
|
104
|
-
const direct =
|
|
105
|
+
const direct = device.isOnline
|
|
106
|
+
? { reachable: true }
|
|
107
|
+
: await (0, auth_1.probeDeviceReachability)(device);
|
|
105
108
|
// Do not hard-block selection just because the LAN /health probe
|
|
106
109
|
// failed. The standalone SDK can still reach a healthy machine via
|
|
107
110
|
// the normal selected-device discovery path (including relay), and
|
|
@@ -114,11 +117,19 @@ const YaverMachinePickerScreen = ({ token, currentDeviceId, onPick, onCancel, })
|
|
|
114
117
|
setReachability((prev) => ({ ...prev, [device.deviceId]: direct }));
|
|
115
118
|
return;
|
|
116
119
|
}
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
setSelectingDeviceId(device.deviceId);
|
|
121
|
+
try {
|
|
122
|
+
await (0, auth_1.saveSelectedDeviceId)(device.deviceId);
|
|
123
|
+
await onPick(device);
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
if (mountedRef.current)
|
|
127
|
+
setSelectingDeviceId(null);
|
|
128
|
+
}
|
|
119
129
|
};
|
|
120
130
|
const renderDevice = (device) => {
|
|
121
131
|
const selected = device.deviceId === currentDeviceId;
|
|
132
|
+
const selecting = device.deviceId === selectingDeviceId;
|
|
122
133
|
const probe = reachability[device.deviceId];
|
|
123
134
|
// Trust Convex's `isOnline` — the backend already gates it on a
|
|
124
135
|
// fresh 90 s heartbeat (see backend/convex/devices.ts
|
|
@@ -137,14 +148,17 @@ const YaverMachinePickerScreen = ({ token, currentDeviceId, onPick, onCancel, })
|
|
|
137
148
|
: effectivelyReachable
|
|
138
149
|
? '#22c55e'
|
|
139
150
|
: device.isOnline
|
|
140
|
-
? '#
|
|
151
|
+
? '#22c55e'
|
|
141
152
|
: explicitlyOffline || !device.isOnline
|
|
142
153
|
? '#ef4444'
|
|
143
154
|
: '#22c55e';
|
|
144
155
|
// Derive a single short status phrase the user can act on.
|
|
145
156
|
let statusLine = device.platform;
|
|
146
|
-
if (
|
|
147
|
-
statusLine = '
|
|
157
|
+
if (selecting) {
|
|
158
|
+
statusLine = 'Connecting…';
|
|
159
|
+
}
|
|
160
|
+
else if (probe === undefined && device.isOnline) {
|
|
161
|
+
statusLine = device.platform || 'Online';
|
|
148
162
|
}
|
|
149
163
|
else if (!device.isOnline && effectivelyReachable) {
|
|
150
164
|
statusLine = 'Reachable now — waiting for cloud status to refresh';
|
|
@@ -166,20 +180,21 @@ const YaverMachinePickerScreen = ({ token, currentDeviceId, onPick, onCancel, })
|
|
|
166
180
|
// Happy-path subtitle.
|
|
167
181
|
statusLine = device.platform;
|
|
168
182
|
}
|
|
169
|
-
return (<react_native_1.TouchableOpacity key={device.deviceId} style={[styles.deviceRow, selected && styles.deviceSelected]} onPress={() => handlePick(device)}>
|
|
183
|
+
return (<react_native_1.TouchableOpacity key={device.deviceId} style={[styles.deviceRow, selected && styles.deviceSelected]} onPress={() => handlePick(device)} disabled={selectingDeviceId !== null}>
|
|
170
184
|
<react_native_1.View style={[styles.health, { backgroundColor: healthColor }]}/>
|
|
171
185
|
<react_native_1.View style={{ flex: 1 }}>
|
|
172
186
|
<react_native_1.Text style={styles.deviceName}>{device.name || device.deviceId}</react_native_1.Text>
|
|
173
187
|
<react_native_1.Text style={styles.deviceMeta}>{statusLine}</react_native_1.Text>
|
|
174
188
|
</react_native_1.View>
|
|
175
|
-
{
|
|
189
|
+
{selecting ? <react_native_1.ActivityIndicator color="#a5b4fc" size="small"/> : null}
|
|
190
|
+
{selected && !selecting && <react_native_1.Text style={styles.selectedBadge}>selected</react_native_1.Text>}
|
|
176
191
|
</react_native_1.TouchableOpacity>);
|
|
177
192
|
};
|
|
178
193
|
return (<react_native_1.SafeAreaView style={styles.container}>
|
|
179
194
|
<react_native_1.View style={styles.header}>
|
|
180
|
-
<react_native_1.Text style={styles.title}>
|
|
195
|
+
<react_native_1.Text style={styles.title}>{title}</react_native_1.Text>
|
|
181
196
|
{onCancel && (<react_native_1.TouchableOpacity onPress={onCancel} style={styles.cancel}>
|
|
182
|
-
<react_native_1.Text style={styles.cancelText}>
|
|
197
|
+
<react_native_1.Text style={styles.cancelText}>Close</react_native_1.Text>
|
|
183
198
|
</react_native_1.TouchableOpacity>)}
|
|
184
199
|
</react_native_1.View>
|
|
185
200
|
|
|
@@ -189,7 +204,7 @@ const YaverMachinePickerScreen = ({ token, currentDeviceId, onPick, onCancel, })
|
|
|
189
204
|
}} tintColor="#6366f1"/>}>
|
|
190
205
|
{loading ? (<react_native_1.ActivityIndicator color="#6366f1" style={{ marginTop: 60 }}/>) : (<>
|
|
191
206
|
{list.owned.length > 0 && (<react_native_1.View style={styles.section}>
|
|
192
|
-
<react_native_1.Text style={styles.sectionTitle}>
|
|
207
|
+
<react_native_1.Text style={styles.sectionTitle}>Your machines</react_native_1.Text>
|
|
193
208
|
{list.owned.map(renderDevice)}
|
|
194
209
|
</react_native_1.View>)}
|
|
195
210
|
{error && <react_native_1.Text style={styles.error}>{error}</react_native_1.Text>}
|