yaver-feedback-react-native 0.8.13 → 0.9.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/dist/FeedbackModal.js +318 -61
- package/dist/MachinePickerScreen.js +14 -5
- package/dist/P2PClient.d.ts +31 -1
- package/dist/P2PClient.js +59 -0
- package/dist/VibeChatScreen.d.ts +6 -1
- package/dist/VibeChatScreen.js +204 -1
- package/dist/capture.d.ts +6 -0
- package/dist/capture.js +53 -0
- package/dist/types.d.ts +57 -2
- package/dist/voice.d.ts +61 -0
- package/dist/voice.js +246 -0
- package/package.json +14 -3
- package/src/FeedbackModal.tsx +388 -74
- package/src/MachinePickerScreen.tsx +12 -3
- package/src/P2PClient.ts +79 -0
- package/src/VibeChatScreen.tsx +219 -0
- package/src/capture.ts +51 -0
- package/src/types.ts +53 -2
- package/src/voice.ts +270 -0
package/src/FeedbackModal.tsx
CHANGED
|
@@ -25,7 +25,13 @@ import {
|
|
|
25
25
|
// stopVideoRecording,
|
|
26
26
|
} from './capture';
|
|
27
27
|
import { uploadFeedback } from './upload';
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
DeviceInfo,
|
|
30
|
+
FeedbackBundle,
|
|
31
|
+
OpenCodeConfigSummary,
|
|
32
|
+
OpenCodeProviderSummary,
|
|
33
|
+
RunnerAuthStatusRow,
|
|
34
|
+
} from './types';
|
|
29
35
|
import { AuthOverlay } from './AuthOverlay';
|
|
30
36
|
import { QuickActionIcon } from './QuickActionIcon';
|
|
31
37
|
import { VibeChatScreen } from './VibeChatScreen';
|
|
@@ -34,6 +40,10 @@ import { listReachableDevices, RemoteDevice } from './auth';
|
|
|
34
40
|
import {
|
|
35
41
|
QUICK_ICON_COLOR_PRESETS,
|
|
36
42
|
QuickIconColorPreset,
|
|
43
|
+
getPreferredModel,
|
|
44
|
+
getPreferredRunner,
|
|
45
|
+
setPreferredModel,
|
|
46
|
+
setPreferredRunner,
|
|
37
47
|
} from './preferences';
|
|
38
48
|
|
|
39
49
|
/**
|
|
@@ -64,6 +74,111 @@ type MachineCardState = {
|
|
|
64
74
|
detail: string;
|
|
65
75
|
};
|
|
66
76
|
|
|
77
|
+
type RunnerTone = 'ok' | 'warning' | 'error' | 'neutral';
|
|
78
|
+
|
|
79
|
+
type RunnerCardState = {
|
|
80
|
+
id: string;
|
|
81
|
+
name: string;
|
|
82
|
+
installed: boolean;
|
|
83
|
+
authConfigured: boolean;
|
|
84
|
+
ready: boolean;
|
|
85
|
+
version?: string;
|
|
86
|
+
tone: RunnerTone;
|
|
87
|
+
statusLine: string;
|
|
88
|
+
detail?: string;
|
|
89
|
+
actionLabel?: string;
|
|
90
|
+
actionRunner?: string;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
type ProviderEditorState = {
|
|
94
|
+
mode: 'add' | 'edit';
|
|
95
|
+
id: string;
|
|
96
|
+
name: string;
|
|
97
|
+
baseUrl: string;
|
|
98
|
+
apiKey: string;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const PRIMARY_RUNNER_IDS = ['claude', 'codex', 'opencode'] as const;
|
|
102
|
+
|
|
103
|
+
function normalizeRunnerStatusRows(rows: RunnerAuthStatusRow[]): RunnerCardState[] {
|
|
104
|
+
const byId = new Map<string, RunnerAuthStatusRow>();
|
|
105
|
+
for (const row of rows) {
|
|
106
|
+
const raw = String(row.id || '').trim().toLowerCase();
|
|
107
|
+
if (!raw) continue;
|
|
108
|
+
const normalized = raw === 'claude-code' ? 'claude' : raw;
|
|
109
|
+
if (!PRIMARY_RUNNER_IDS.includes(normalized as (typeof PRIMARY_RUNNER_IDS)[number])) continue;
|
|
110
|
+
byId.set(normalized, { ...row, id: normalized });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return PRIMARY_RUNNER_IDS.map((id) => {
|
|
114
|
+
const baseName =
|
|
115
|
+
id === 'claude' ? 'Claude Code' : id === 'codex' ? 'OpenAI Codex' : 'OpenCode';
|
|
116
|
+
const row = byId.get(id);
|
|
117
|
+
if (!row) {
|
|
118
|
+
return {
|
|
119
|
+
id,
|
|
120
|
+
name: baseName,
|
|
121
|
+
installed: false,
|
|
122
|
+
authConfigured: false,
|
|
123
|
+
ready: false,
|
|
124
|
+
tone: 'warning',
|
|
125
|
+
statusLine: 'Not installed on the selected machine',
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const versionPrefix = row.version?.trim() ? `${row.version.trim()} · ` : '';
|
|
130
|
+
const detail = row.error?.trim() || row.warning?.trim() || row.detail?.trim() || undefined;
|
|
131
|
+
|
|
132
|
+
if (!row.installed) {
|
|
133
|
+
return {
|
|
134
|
+
id,
|
|
135
|
+
name: row.name || baseName,
|
|
136
|
+
installed: false,
|
|
137
|
+
authConfigured: false,
|
|
138
|
+
ready: false,
|
|
139
|
+
version: row.version,
|
|
140
|
+
tone: 'warning',
|
|
141
|
+
statusLine: 'Not installed on the selected machine',
|
|
142
|
+
detail,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (id === 'opencode') {
|
|
147
|
+
const configured = row.authConfigured || row.ready;
|
|
148
|
+
return {
|
|
149
|
+
id,
|
|
150
|
+
name: row.name || baseName,
|
|
151
|
+
installed: row.installed,
|
|
152
|
+
authConfigured: row.authConfigured,
|
|
153
|
+
ready: row.ready,
|
|
154
|
+
version: row.version,
|
|
155
|
+
tone: configured ? 'ok' : 'warning',
|
|
156
|
+
statusLine: configured
|
|
157
|
+
? `${versionPrefix}Configured on the selected machine`
|
|
158
|
+
: `${versionPrefix}Needs provider config on the selected machine`,
|
|
159
|
+
detail,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const authed = row.authConfigured || row.ready;
|
|
164
|
+
return {
|
|
165
|
+
id,
|
|
166
|
+
name: row.name || baseName,
|
|
167
|
+
installed: row.installed,
|
|
168
|
+
authConfigured: row.authConfigured,
|
|
169
|
+
ready: row.ready,
|
|
170
|
+
version: row.version,
|
|
171
|
+
tone: authed ? 'ok' : 'warning',
|
|
172
|
+
statusLine: authed
|
|
173
|
+
? `${versionPrefix}Signed in on the selected machine`
|
|
174
|
+
: `${versionPrefix}Not signed in on the selected machine`,
|
|
175
|
+
detail,
|
|
176
|
+
actionLabel: authed ? 'Re-auth' : 'Sign in',
|
|
177
|
+
actionRunner: id,
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
67
182
|
export const FeedbackModal: React.FC = () => {
|
|
68
183
|
const { width: winW, height: winH } = useWindowDimensions();
|
|
69
184
|
const isTablet = Math.min(winW, winH) >= 600;
|
|
@@ -101,6 +216,14 @@ export const FeedbackModal: React.FC = () => {
|
|
|
101
216
|
title: 'No machine selected',
|
|
102
217
|
detail: 'Pick a remote dev machine before using the feedback actions.',
|
|
103
218
|
});
|
|
219
|
+
const [runnerCards, setRunnerCards] = useState<RunnerCardState[]>(() =>
|
|
220
|
+
normalizeRunnerStatusRows([]),
|
|
221
|
+
);
|
|
222
|
+
const [runnerStatusLoading, setRunnerStatusLoading] = useState(false);
|
|
223
|
+
const [runnerStatusError, setRunnerStatusError] = useState<string | null>(null);
|
|
224
|
+
const [preferredRunner, setPreferredRunnerState] = useState<string | null>(null);
|
|
225
|
+
const [preferredModel, setPreferredModelState] = useState('');
|
|
226
|
+
const [showOpenCodeConfig, setShowOpenCodeConfig] = useState(false);
|
|
104
227
|
const mountedRef = useRef(true);
|
|
105
228
|
|
|
106
229
|
const loadSelectedMachine = useCallback(async () => {
|
|
@@ -204,6 +327,69 @@ export const FeedbackModal: React.FC = () => {
|
|
|
204
327
|
}
|
|
205
328
|
}, []);
|
|
206
329
|
|
|
330
|
+
const loadRunnerStatuses = useCallback(async () => {
|
|
331
|
+
const cfg = YaverFeedback.getConfig();
|
|
332
|
+
if (!cfg?.authToken) {
|
|
333
|
+
if (mountedRef.current) {
|
|
334
|
+
setRunnerCards(normalizeRunnerStatusRows([]));
|
|
335
|
+
setRunnerStatusError('Sign in to inspect coding-agent status.');
|
|
336
|
+
setRunnerStatusLoading(false);
|
|
337
|
+
}
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
if (!cfg.preferredDeviceId) {
|
|
341
|
+
if (mountedRef.current) {
|
|
342
|
+
setRunnerCards(normalizeRunnerStatusRows([]));
|
|
343
|
+
setRunnerStatusError('Pick a machine to inspect coding-agent status.');
|
|
344
|
+
setRunnerStatusLoading(false);
|
|
345
|
+
}
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (mountedRef.current) {
|
|
350
|
+
setRunnerStatusLoading(true);
|
|
351
|
+
setRunnerStatusError(null);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
try {
|
|
355
|
+
let client = YaverFeedback.getP2PClient();
|
|
356
|
+
if (!client) {
|
|
357
|
+
const ok = await YaverFeedback.reconnect();
|
|
358
|
+
if (ok) client = YaverFeedback.getP2PClient();
|
|
359
|
+
}
|
|
360
|
+
if (!client) {
|
|
361
|
+
throw new Error('Not connected to the selected machine yet.');
|
|
362
|
+
}
|
|
363
|
+
const rows = await client.getRunnerAuthStatus();
|
|
364
|
+
if (mountedRef.current) {
|
|
365
|
+
setRunnerCards(normalizeRunnerStatusRows(rows));
|
|
366
|
+
}
|
|
367
|
+
} catch (err) {
|
|
368
|
+
if (mountedRef.current) {
|
|
369
|
+
setRunnerCards(normalizeRunnerStatusRows([]));
|
|
370
|
+
setRunnerStatusError(err instanceof Error ? err.message : String(err));
|
|
371
|
+
}
|
|
372
|
+
} finally {
|
|
373
|
+
if (mountedRef.current) setRunnerStatusLoading(false);
|
|
374
|
+
}
|
|
375
|
+
}, []);
|
|
376
|
+
|
|
377
|
+
const loadRoutingPrefs = useCallback(async () => {
|
|
378
|
+
try {
|
|
379
|
+
const [runner, model] = await Promise.all([
|
|
380
|
+
getPreferredRunner(),
|
|
381
|
+
getPreferredModel(),
|
|
382
|
+
]);
|
|
383
|
+
if (!mountedRef.current) return;
|
|
384
|
+
setPreferredRunnerState(runner);
|
|
385
|
+
setPreferredModelState(model ?? '');
|
|
386
|
+
} catch {
|
|
387
|
+
if (!mountedRef.current) return;
|
|
388
|
+
setPreferredRunnerState(null);
|
|
389
|
+
setPreferredModelState('');
|
|
390
|
+
}
|
|
391
|
+
}, []);
|
|
392
|
+
|
|
207
393
|
useEffect(() => {
|
|
208
394
|
mountedRef.current = true;
|
|
209
395
|
const sub = DeviceEventEmitter.addListener('yaverFeedback:startReport', () => {
|
|
@@ -229,6 +415,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
229
415
|
})
|
|
230
416
|
.catch(() => {});
|
|
231
417
|
void loadSelectedMachine();
|
|
418
|
+
void loadRunnerStatuses();
|
|
232
419
|
}
|
|
233
420
|
});
|
|
234
421
|
// Agent streams build / compile progress through the BlackBox
|
|
@@ -257,15 +444,16 @@ export const FeedbackModal: React.FC = () => {
|
|
|
257
444
|
sub.remove();
|
|
258
445
|
statusSub.remove();
|
|
259
446
|
};
|
|
260
|
-
}, [loadSelectedMachine]);
|
|
447
|
+
}, [loadRunnerStatuses, loadSelectedMachine]);
|
|
261
448
|
|
|
262
449
|
useEffect(() => {
|
|
263
450
|
if (!visible) return;
|
|
264
451
|
const interval = setInterval(() => {
|
|
265
452
|
void loadSelectedMachine();
|
|
453
|
+
void loadRunnerStatuses();
|
|
266
454
|
}, 5000);
|
|
267
455
|
return () => clearInterval(interval);
|
|
268
|
-
}, [loadSelectedMachine, visible]);
|
|
456
|
+
}, [loadRunnerStatuses, loadSelectedMachine, visible]);
|
|
269
457
|
|
|
270
458
|
useEffect(() => {
|
|
271
459
|
if (!visible) {
|
|
@@ -301,6 +489,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
301
489
|
setAction('idle');
|
|
302
490
|
setShowVibeInput(false);
|
|
303
491
|
setVibePrompt('');
|
|
492
|
+
setRunnerStatusError(null);
|
|
304
493
|
}, []);
|
|
305
494
|
|
|
306
495
|
// Helper: run a P2P call; on network failure, ask YaverFeedback to
|
|
@@ -552,6 +741,9 @@ export const FeedbackModal: React.FC = () => {
|
|
|
552
741
|
const [activeVibe, setActiveVibe] = useState<{
|
|
553
742
|
taskId: string;
|
|
554
743
|
initialPrompt: string;
|
|
744
|
+
project?: string;
|
|
745
|
+
runner?: string;
|
|
746
|
+
model?: string;
|
|
555
747
|
} | null>(null);
|
|
556
748
|
const [includeScreenshot, setIncludeScreenshot] = useState<boolean>(true);
|
|
557
749
|
|
|
@@ -614,7 +806,13 @@ export const FeedbackModal: React.FC = () => {
|
|
|
614
806
|
setLastVibeTaskId(result.taskId);
|
|
615
807
|
// Hand off to VibeChatScreen — it streams the SSE transcript,
|
|
616
808
|
// accepts follow-ups, and surfaces a Reload button.
|
|
617
|
-
setActiveVibe({
|
|
809
|
+
setActiveVibe({
|
|
810
|
+
taskId: result.taskId,
|
|
811
|
+
initialPrompt: promptText,
|
|
812
|
+
project: identity.projectName,
|
|
813
|
+
runner: preferredRunner ?? undefined,
|
|
814
|
+
model: preferredModel ?? undefined,
|
|
815
|
+
});
|
|
618
816
|
setVibePrompt('');
|
|
619
817
|
setShowVibeInput(false);
|
|
620
818
|
} catch (err: unknown) {
|
|
@@ -631,6 +829,11 @@ export const FeedbackModal: React.FC = () => {
|
|
|
631
829
|
*/
|
|
632
830
|
|
|
633
831
|
const busy = action !== 'idle';
|
|
832
|
+
const readyRunnerCount = runnerCards.filter((row) => row.ready || row.authConfigured).length;
|
|
833
|
+
const missingRunnerCount = runnerCards.filter((row) => !row.installed).length;
|
|
834
|
+
const needsAuthRunnerCount = runnerCards.filter(
|
|
835
|
+
(row) => row.installed && !row.authConfigured && !row.ready,
|
|
836
|
+
).length;
|
|
634
837
|
|
|
635
838
|
// Once the user fires off a vibe task, swap the entire modal body
|
|
636
839
|
// for the live chat screen. The chat manages its own SSE
|
|
@@ -653,6 +856,9 @@ export const FeedbackModal: React.FC = () => {
|
|
|
653
856
|
client={client}
|
|
654
857
|
initialTaskId={activeVibe.taskId}
|
|
655
858
|
initialUserPrompt={activeVibe.initialPrompt}
|
|
859
|
+
project={activeVibe.project}
|
|
860
|
+
runner={activeVibe.runner}
|
|
861
|
+
model={activeVibe.model}
|
|
656
862
|
onClose={() => setActiveVibe(null)}
|
|
657
863
|
onReload={async () => {
|
|
658
864
|
const c = YaverFeedback.getP2PClient();
|
|
@@ -766,6 +972,80 @@ export const FeedbackModal: React.FC = () => {
|
|
|
766
972
|
<Text style={styles.machineMeta}>{machineCard.detail}</Text>
|
|
767
973
|
</Pressable>
|
|
768
974
|
|
|
975
|
+
<View style={styles.runnerSection}>
|
|
976
|
+
<View style={styles.runnerSectionHeader}>
|
|
977
|
+
<View style={{ flex: 1 }}>
|
|
978
|
+
<Text style={styles.runnerSectionTitle}>Coding Agents</Text>
|
|
979
|
+
<Text style={styles.runnerSectionSubtitle}>
|
|
980
|
+
{runnerStatusLoading
|
|
981
|
+
? 'Refreshing runner status on the selected machine…'
|
|
982
|
+
: `${readyRunnerCount} ready · ${needsAuthRunnerCount} need sign-in · ${missingRunnerCount} missing`}
|
|
983
|
+
</Text>
|
|
984
|
+
</View>
|
|
985
|
+
<Pressable
|
|
986
|
+
onPress={() => void loadRunnerStatuses()}
|
|
987
|
+
style={({ pressed }) => [
|
|
988
|
+
styles.runnerRefreshBtn,
|
|
989
|
+
pressed && styles.buttonPressed,
|
|
990
|
+
]}
|
|
991
|
+
accessibilityRole="button"
|
|
992
|
+
accessibilityLabel="Refresh coding-agent status"
|
|
993
|
+
>
|
|
994
|
+
<Text style={styles.runnerRefreshBtnText}>
|
|
995
|
+
{runnerStatusLoading ? 'Refreshing…' : 'Refresh'}
|
|
996
|
+
</Text>
|
|
997
|
+
</Pressable>
|
|
998
|
+
</View>
|
|
999
|
+
|
|
1000
|
+
{runnerCards.map((row) => (
|
|
1001
|
+
<View
|
|
1002
|
+
key={row.id}
|
|
1003
|
+
style={[
|
|
1004
|
+
styles.runnerCard,
|
|
1005
|
+
row.tone === 'ok' && styles.runnerCardOk,
|
|
1006
|
+
row.tone === 'warning' && styles.runnerCardWarning,
|
|
1007
|
+
row.tone === 'error' && styles.runnerCardError,
|
|
1008
|
+
]}
|
|
1009
|
+
>
|
|
1010
|
+
<View style={styles.runnerCardTop}>
|
|
1011
|
+
<View style={{ flex: 1 }}>
|
|
1012
|
+
<Text style={styles.runnerCardTitle}>{row.name}</Text>
|
|
1013
|
+
<Text
|
|
1014
|
+
style={[
|
|
1015
|
+
styles.runnerCardStatus,
|
|
1016
|
+
row.tone === 'ok' && styles.runnerCardStatusOk,
|
|
1017
|
+
row.tone === 'warning' && styles.runnerCardStatusWarning,
|
|
1018
|
+
row.tone === 'error' && styles.runnerCardStatusError,
|
|
1019
|
+
]}
|
|
1020
|
+
>
|
|
1021
|
+
{row.statusLine}
|
|
1022
|
+
</Text>
|
|
1023
|
+
</View>
|
|
1024
|
+
{row.actionRunner ? (
|
|
1025
|
+
<Pressable
|
|
1026
|
+
onPress={() => setRunnerAuthModal(row.actionRunner ?? null)}
|
|
1027
|
+
style={({ pressed }) => [
|
|
1028
|
+
styles.runnerActionBtn,
|
|
1029
|
+
pressed && styles.buttonPressed,
|
|
1030
|
+
]}
|
|
1031
|
+
accessibilityRole="button"
|
|
1032
|
+
accessibilityLabel={`${row.actionLabel} ${row.name}`}
|
|
1033
|
+
>
|
|
1034
|
+
<Text style={styles.runnerActionBtnText}>{row.actionLabel}</Text>
|
|
1035
|
+
</Pressable>
|
|
1036
|
+
) : null}
|
|
1037
|
+
</View>
|
|
1038
|
+
{row.detail ? (
|
|
1039
|
+
<Text style={styles.runnerCardDetail}>{row.detail}</Text>
|
|
1040
|
+
) : null}
|
|
1041
|
+
</View>
|
|
1042
|
+
))}
|
|
1043
|
+
|
|
1044
|
+
{runnerStatusError ? (
|
|
1045
|
+
<Text style={styles.runnerSectionError}>{runnerStatusError}</Text>
|
|
1046
|
+
) : null}
|
|
1047
|
+
</View>
|
|
1048
|
+
|
|
769
1049
|
{quickIconHidden && (
|
|
770
1050
|
<View style={styles.quickIconNote}>
|
|
771
1051
|
<Text style={styles.quickIconNoteText}>
|
|
@@ -931,41 +1211,6 @@ export const FeedbackModal: React.FC = () => {
|
|
|
931
1211
|
<DeployPanel onClose={() => setShowDeploy(false)} />
|
|
932
1212
|
)}
|
|
933
1213
|
|
|
934
|
-
{/* Remote sign-in buttons — trigger codex/claude device-auth
|
|
935
|
-
on the selected agent without leaving the app. Opens a
|
|
936
|
-
small native modal showing the verification URL + 8-char
|
|
937
|
-
code the user enters in any browser. No API keys. */}
|
|
938
|
-
<View style={runnerAuthRowStyles.container}>
|
|
939
|
-
<Pressable
|
|
940
|
-
onPress={() => setRunnerAuthModal('codex')}
|
|
941
|
-
disabled={busy}
|
|
942
|
-
style={({ pressed }) => [
|
|
943
|
-
runnerAuthRowStyles.button,
|
|
944
|
-
pressed && runnerAuthRowStyles.buttonPressed,
|
|
945
|
-
busy && runnerAuthRowStyles.buttonDisabled,
|
|
946
|
-
]}
|
|
947
|
-
accessibilityRole="button"
|
|
948
|
-
accessibilityLabel="Remote sign-in Codex"
|
|
949
|
-
>
|
|
950
|
-
<Text style={runnerAuthRowStyles.buttonLabel}>Remote sign-in</Text>
|
|
951
|
-
<Text style={runnerAuthRowStyles.buttonName}>Codex</Text>
|
|
952
|
-
</Pressable>
|
|
953
|
-
<Pressable
|
|
954
|
-
onPress={() => setRunnerAuthModal('claude')}
|
|
955
|
-
disabled={busy}
|
|
956
|
-
style={({ pressed }) => [
|
|
957
|
-
runnerAuthRowStyles.button,
|
|
958
|
-
pressed && runnerAuthRowStyles.buttonPressed,
|
|
959
|
-
busy && runnerAuthRowStyles.buttonDisabled,
|
|
960
|
-
]}
|
|
961
|
-
accessibilityRole="button"
|
|
962
|
-
accessibilityLabel="Remote sign-in Claude"
|
|
963
|
-
>
|
|
964
|
-
<Text style={runnerAuthRowStyles.buttonLabel}>Remote sign-in</Text>
|
|
965
|
-
<Text style={runnerAuthRowStyles.buttonName}>Claude</Text>
|
|
966
|
-
</Pressable>
|
|
967
|
-
</View>
|
|
968
|
-
|
|
969
1214
|
{progress !== null && (
|
|
970
1215
|
<View style={styles.progressTrack}>
|
|
971
1216
|
<View
|
|
@@ -999,7 +1244,10 @@ export const FeedbackModal: React.FC = () => {
|
|
|
999
1244
|
{runnerAuthModal ? (
|
|
1000
1245
|
<RunnerAuthNativeModal
|
|
1001
1246
|
runner={runnerAuthModal}
|
|
1002
|
-
onClose={() =>
|
|
1247
|
+
onClose={() => {
|
|
1248
|
+
setRunnerAuthModal(null);
|
|
1249
|
+
void loadRunnerStatuses();
|
|
1250
|
+
}}
|
|
1003
1251
|
/>
|
|
1004
1252
|
) : null}
|
|
1005
1253
|
</>
|
|
@@ -1227,6 +1475,106 @@ const styles = StyleSheet.create({
|
|
|
1227
1475
|
marginTop: 4,
|
|
1228
1476
|
lineHeight: 17,
|
|
1229
1477
|
},
|
|
1478
|
+
runnerSection: {
|
|
1479
|
+
marginTop: 2,
|
|
1480
|
+
gap: 10,
|
|
1481
|
+
},
|
|
1482
|
+
runnerSectionHeader: {
|
|
1483
|
+
flexDirection: 'row',
|
|
1484
|
+
alignItems: 'center',
|
|
1485
|
+
gap: 10,
|
|
1486
|
+
},
|
|
1487
|
+
runnerSectionTitle: {
|
|
1488
|
+
color: '#f8fafc',
|
|
1489
|
+
fontSize: 16,
|
|
1490
|
+
fontWeight: '700',
|
|
1491
|
+
},
|
|
1492
|
+
runnerSectionSubtitle: {
|
|
1493
|
+
marginTop: 2,
|
|
1494
|
+
color: '#94a3b8',
|
|
1495
|
+
fontSize: 12,
|
|
1496
|
+
},
|
|
1497
|
+
runnerRefreshBtn: {
|
|
1498
|
+
borderRadius: 10,
|
|
1499
|
+
borderWidth: 1,
|
|
1500
|
+
borderColor: 'rgba(148,163,184,0.22)',
|
|
1501
|
+
backgroundColor: 'rgba(15,23,42,0.65)',
|
|
1502
|
+
paddingHorizontal: 10,
|
|
1503
|
+
paddingVertical: 8,
|
|
1504
|
+
},
|
|
1505
|
+
runnerRefreshBtnText: {
|
|
1506
|
+
color: '#cbd5e1',
|
|
1507
|
+
fontSize: 12,
|
|
1508
|
+
fontWeight: '600',
|
|
1509
|
+
},
|
|
1510
|
+
runnerCard: {
|
|
1511
|
+
borderRadius: 12,
|
|
1512
|
+
borderWidth: 1,
|
|
1513
|
+
borderColor: 'rgba(148,163,184,0.14)',
|
|
1514
|
+
backgroundColor: 'rgba(15,23,42,0.45)',
|
|
1515
|
+
paddingHorizontal: 12,
|
|
1516
|
+
paddingVertical: 11,
|
|
1517
|
+
gap: 6,
|
|
1518
|
+
},
|
|
1519
|
+
runnerCardOk: {
|
|
1520
|
+
borderColor: 'rgba(34,197,94,0.28)',
|
|
1521
|
+
backgroundColor: 'rgba(20,83,45,0.20)',
|
|
1522
|
+
},
|
|
1523
|
+
runnerCardWarning: {
|
|
1524
|
+
borderColor: 'rgba(251,191,36,0.28)',
|
|
1525
|
+
backgroundColor: 'rgba(120,53,15,0.18)',
|
|
1526
|
+
},
|
|
1527
|
+
runnerCardError: {
|
|
1528
|
+
borderColor: 'rgba(248,113,113,0.28)',
|
|
1529
|
+
backgroundColor: 'rgba(127,29,29,0.18)',
|
|
1530
|
+
},
|
|
1531
|
+
runnerCardTop: {
|
|
1532
|
+
flexDirection: 'row',
|
|
1533
|
+
alignItems: 'center',
|
|
1534
|
+
gap: 10,
|
|
1535
|
+
},
|
|
1536
|
+
runnerCardTitle: {
|
|
1537
|
+
color: '#f8fafc',
|
|
1538
|
+
fontSize: 14,
|
|
1539
|
+
fontWeight: '700',
|
|
1540
|
+
},
|
|
1541
|
+
runnerCardStatus: {
|
|
1542
|
+
marginTop: 2,
|
|
1543
|
+
fontSize: 12,
|
|
1544
|
+
color: '#cbd5e1',
|
|
1545
|
+
},
|
|
1546
|
+
runnerCardStatusOk: {
|
|
1547
|
+
color: '#86efac',
|
|
1548
|
+
},
|
|
1549
|
+
runnerCardStatusWarning: {
|
|
1550
|
+
color: '#fcd34d',
|
|
1551
|
+
},
|
|
1552
|
+
runnerCardStatusError: {
|
|
1553
|
+
color: '#fca5a5',
|
|
1554
|
+
},
|
|
1555
|
+
runnerCardDetail: {
|
|
1556
|
+
color: '#94a3b8',
|
|
1557
|
+
fontSize: 11,
|
|
1558
|
+
lineHeight: 16,
|
|
1559
|
+
},
|
|
1560
|
+
runnerActionBtn: {
|
|
1561
|
+
borderRadius: 10,
|
|
1562
|
+
borderWidth: 1,
|
|
1563
|
+
borderColor: 'rgba(129,140,248,0.35)',
|
|
1564
|
+
backgroundColor: 'rgba(67,56,202,0.22)',
|
|
1565
|
+
paddingHorizontal: 12,
|
|
1566
|
+
paddingVertical: 8,
|
|
1567
|
+
},
|
|
1568
|
+
runnerActionBtnText: {
|
|
1569
|
+
color: '#c7d2fe',
|
|
1570
|
+
fontSize: 12,
|
|
1571
|
+
fontWeight: '700',
|
|
1572
|
+
},
|
|
1573
|
+
runnerSectionError: {
|
|
1574
|
+
color: '#fca5a5',
|
|
1575
|
+
fontSize: 12,
|
|
1576
|
+
lineHeight: 18,
|
|
1577
|
+
},
|
|
1230
1578
|
captureChoices: {
|
|
1231
1579
|
gap: 10,
|
|
1232
1580
|
},
|
|
@@ -1604,40 +1952,6 @@ const RunnerAuthNativeModal: React.FC<{
|
|
|
1604
1952
|
);
|
|
1605
1953
|
};
|
|
1606
1954
|
|
|
1607
|
-
const runnerAuthRowStyles = StyleSheet.create({
|
|
1608
|
-
container: {
|
|
1609
|
-
flexDirection: 'row',
|
|
1610
|
-
gap: 8,
|
|
1611
|
-
marginTop: 8,
|
|
1612
|
-
flexWrap: 'wrap',
|
|
1613
|
-
},
|
|
1614
|
-
button: {
|
|
1615
|
-
flexGrow: 1,
|
|
1616
|
-
flexBasis: 0,
|
|
1617
|
-
minWidth: 120,
|
|
1618
|
-
paddingHorizontal: 12,
|
|
1619
|
-
paddingVertical: 10,
|
|
1620
|
-
borderRadius: 10,
|
|
1621
|
-
borderWidth: 1,
|
|
1622
|
-
borderColor: 'rgba(148,163,184,0.22)',
|
|
1623
|
-
backgroundColor: 'rgba(15,23,42,0.6)',
|
|
1624
|
-
},
|
|
1625
|
-
buttonPressed: { opacity: 0.7 },
|
|
1626
|
-
buttonDisabled: { opacity: 0.4 },
|
|
1627
|
-
buttonLabel: {
|
|
1628
|
-
fontSize: 10,
|
|
1629
|
-
color: '#94a3b8',
|
|
1630
|
-
textTransform: 'uppercase',
|
|
1631
|
-
letterSpacing: 0.8,
|
|
1632
|
-
},
|
|
1633
|
-
buttonName: {
|
|
1634
|
-
marginTop: 2,
|
|
1635
|
-
fontSize: 14,
|
|
1636
|
-
fontWeight: '600',
|
|
1637
|
-
color: '#f1f5f9',
|
|
1638
|
-
},
|
|
1639
|
-
});
|
|
1640
|
-
|
|
1641
1955
|
const runnerAuthModalStyles = StyleSheet.create({
|
|
1642
1956
|
overlay: {
|
|
1643
1957
|
flex: 1,
|
|
@@ -102,7 +102,14 @@ export const YaverMachinePickerScreen: React.FC<YaverMachinePickerProps> = ({
|
|
|
102
102
|
return;
|
|
103
103
|
}
|
|
104
104
|
const direct = await probeDeviceReachability(device);
|
|
105
|
-
|
|
105
|
+
// Do not hard-block selection just because the LAN /health probe
|
|
106
|
+
// failed. The standalone SDK can still reach a healthy machine via
|
|
107
|
+
// the normal selected-device discovery path (including relay), and
|
|
108
|
+
// the Yaver host path may already be proving the machine works.
|
|
109
|
+
// Only treat the machine as unpickable when BOTH:
|
|
110
|
+
// 1. Convex says it is offline, and
|
|
111
|
+
// 2. the direct probe also failed.
|
|
112
|
+
if (!device.isOnline && !direct.reachable && !device.needsAuth) {
|
|
106
113
|
setError('Selected machine is not responding. Start `yaver serve` on it and try again.');
|
|
107
114
|
setReachability((prev) => ({ ...prev, [device.deviceId]: direct }));
|
|
108
115
|
return;
|
|
@@ -130,7 +137,9 @@ export const YaverMachinePickerScreen: React.FC<YaverMachinePickerProps> = ({
|
|
|
130
137
|
? '#f59e0b'
|
|
131
138
|
: effectivelyReachable
|
|
132
139
|
? '#22c55e'
|
|
133
|
-
:
|
|
140
|
+
: device.isOnline
|
|
141
|
+
? '#f59e0b'
|
|
142
|
+
: explicitlyOffline || !device.isOnline
|
|
134
143
|
? '#ef4444'
|
|
135
144
|
: '#22c55e';
|
|
136
145
|
// Derive a single short status phrase the user can act on.
|
|
@@ -145,7 +154,7 @@ export const YaverMachinePickerScreen: React.FC<YaverMachinePickerProps> = ({
|
|
|
145
154
|
statusLine =
|
|
146
155
|
'Needs pairing — open the Yaver app to adopt this machine';
|
|
147
156
|
} else if (explicitlyOffline) {
|
|
148
|
-
statusLine = '
|
|
157
|
+
statusLine = 'Online, but direct probe failed — relay / selected-machine path may still work';
|
|
149
158
|
} else if (device.runnerDown) {
|
|
150
159
|
statusLine = 'Runner down — restart the coding agent on the Mac';
|
|
151
160
|
} else {
|