yaver-feedback-react-native 0.8.13 → 0.9.2
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/app.plugin.js +126 -9
- package/dist/BlackBox.js +9 -1
- package/dist/DeployPanel.js +65 -0
- package/dist/Discovery.js +13 -2
- package/dist/FeedbackModal.js +477 -66
- package/dist/MachinePickerScreen.js +14 -5
- package/dist/P2PClient.d.ts +94 -1
- package/dist/P2PClient.js +281 -2
- package/dist/ShakeDetector.js +2 -0
- package/dist/VibeChatScreen.d.ts +6 -1
- package/dist/VibeChatScreen.js +204 -1
- package/dist/YaverFeedback.d.ts +98 -0
- package/dist/YaverFeedback.js +509 -46
- package/dist/__tests__/BlackBox.relayPassword.test.d.ts +1 -0
- package/dist/__tests__/BlackBox.relayPassword.test.js +105 -0
- package/dist/__tests__/BlackBoxAutoStart.test.d.ts +1 -0
- package/dist/__tests__/BlackBoxAutoStart.test.js +91 -0
- package/dist/__tests__/BlackBoxAutoStartColdStart.test.d.ts +1 -0
- package/dist/__tests__/BlackBoxAutoStartColdStart.test.js +156 -0
- package/dist/__tests__/BrowserLaneIcon.test.d.ts +3 -0
- package/dist/__tests__/BrowserLaneIcon.test.js +80 -0
- package/dist/__tests__/P2PClient.test.js +2 -2
- package/dist/__tests__/ReportIdentity.test.d.ts +1 -0
- package/dist/__tests__/ReportIdentity.test.js +168 -0
- package/dist/__tests__/SDKToken.test.js +1 -1
- package/dist/__tests__/ShakeToggle.test.d.ts +1 -0
- package/dist/__tests__/ShakeToggle.test.js +121 -0
- package/dist/__tests__/pickTargetDevice.test.d.ts +1 -0
- package/dist/__tests__/pickTargetDevice.test.js +89 -0
- package/dist/__tests__/reloadActions.test.d.ts +1 -0
- package/dist/__tests__/reloadActions.test.js +129 -0
- package/dist/__tests__/reloadActionsParity.test.d.ts +1 -0
- package/dist/__tests__/reloadActionsParity.test.js +42 -0
- package/dist/__tests__/types.test.js +5 -5
- package/dist/_core/device.d.ts +19 -9
- package/dist/_core/device.js +20 -14
- package/dist/capture.d.ts +6 -0
- package/dist/capture.js +53 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +11 -1
- package/dist/reloadActions.d.ts +88 -0
- package/dist/reloadActions.js +200 -0
- package/dist/storeShots.d.ts +67 -0
- package/dist/storeShots.js +137 -0
- package/dist/types.d.ts +215 -3
- package/dist/voice.d.ts +61 -0
- package/dist/voice.js +246 -0
- package/package.json +14 -3
- package/src/BlackBox.ts +10 -1
- package/src/DeployPanel.tsx +74 -0
- package/src/Discovery.ts +13 -2
- package/src/FeedbackModal.tsx +563 -87
- package/src/MachinePickerScreen.tsx +12 -3
- package/src/P2PClient.ts +314 -2
- package/src/ShakeDetector.ts +1 -0
- package/src/VibeChatScreen.tsx +219 -0
- package/src/YaverFeedback.ts +498 -46
- package/src/__tests__/BlackBox.relayPassword.test.ts +129 -0
- package/src/__tests__/BlackBoxAutoStart.test.ts +111 -0
- package/src/__tests__/BlackBoxAutoStartColdStart.test.ts +191 -0
- package/src/__tests__/BrowserLaneIcon.test.ts +85 -0
- package/src/__tests__/P2PClient.test.ts +2 -2
- package/src/__tests__/ReportIdentity.test.ts +203 -0
- package/src/__tests__/SDKToken.test.ts +1 -1
- package/src/__tests__/ShakeToggle.test.ts +153 -0
- package/src/__tests__/pickTargetDevice.test.ts +101 -0
- package/src/__tests__/reloadActions.test.ts +171 -0
- package/src/__tests__/reloadActionsParity.test.ts +49 -0
- package/src/__tests__/types.test.ts +5 -5
- package/src/_core/device.ts +20 -14
- package/src/capture.ts +51 -0
- package/src/index.ts +21 -0
- package/src/reloadActions.ts +273 -0
- package/src/storeShots.ts +189 -0
- package/src/types.ts +217 -3
- package/src/voice.ts +270 -0
package/dist/FeedbackModal.js
CHANGED
|
@@ -39,12 +39,89 @@ const react_native_1 = require("react-native");
|
|
|
39
39
|
const YaverFeedback_1 = require("./YaverFeedback");
|
|
40
40
|
const capture_1 = require("./capture");
|
|
41
41
|
const upload_1 = require("./upload");
|
|
42
|
+
const P2PClient_1 = require("./P2PClient");
|
|
42
43
|
const AuthOverlay_1 = require("./AuthOverlay");
|
|
43
44
|
const QuickActionIcon_1 = require("./QuickActionIcon");
|
|
44
45
|
const VibeChatScreen_1 = require("./VibeChatScreen");
|
|
45
46
|
const DeployPanel_1 = require("./DeployPanel");
|
|
46
47
|
const auth_1 = require("./auth");
|
|
48
|
+
const reloadActions_1 = require("./reloadActions");
|
|
47
49
|
const preferences_1 = require("./preferences");
|
|
50
|
+
const PRIMARY_RUNNER_IDS = ['claude', 'codex', 'opencode'];
|
|
51
|
+
function normalizeRunnerStatusRows(rows) {
|
|
52
|
+
const byId = new Map();
|
|
53
|
+
for (const row of rows) {
|
|
54
|
+
const raw = String(row.id || '').trim().toLowerCase();
|
|
55
|
+
if (!raw)
|
|
56
|
+
continue;
|
|
57
|
+
const normalized = raw === 'claude-code' ? 'claude' : raw;
|
|
58
|
+
if (!PRIMARY_RUNNER_IDS.includes(normalized))
|
|
59
|
+
continue;
|
|
60
|
+
byId.set(normalized, { ...row, id: normalized });
|
|
61
|
+
}
|
|
62
|
+
return PRIMARY_RUNNER_IDS.map((id) => {
|
|
63
|
+
const baseName = id === 'claude' ? 'Claude Code' : id === 'codex' ? 'OpenAI Codex' : 'OpenCode';
|
|
64
|
+
const row = byId.get(id);
|
|
65
|
+
if (!row) {
|
|
66
|
+
return {
|
|
67
|
+
id,
|
|
68
|
+
name: baseName,
|
|
69
|
+
installed: false,
|
|
70
|
+
authConfigured: false,
|
|
71
|
+
ready: false,
|
|
72
|
+
tone: 'warning',
|
|
73
|
+
statusLine: 'Not installed on the selected machine',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const versionPrefix = row.version?.trim() ? `${row.version.trim()} · ` : '';
|
|
77
|
+
const detail = row.error?.trim() || row.warning?.trim() || row.detail?.trim() || undefined;
|
|
78
|
+
if (!row.installed) {
|
|
79
|
+
return {
|
|
80
|
+
id,
|
|
81
|
+
name: row.name || baseName,
|
|
82
|
+
installed: false,
|
|
83
|
+
authConfigured: false,
|
|
84
|
+
ready: false,
|
|
85
|
+
version: row.version,
|
|
86
|
+
tone: 'warning',
|
|
87
|
+
statusLine: 'Not installed on the selected machine',
|
|
88
|
+
detail,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (id === 'opencode') {
|
|
92
|
+
const configured = row.authConfigured || row.ready;
|
|
93
|
+
return {
|
|
94
|
+
id,
|
|
95
|
+
name: row.name || baseName,
|
|
96
|
+
installed: row.installed,
|
|
97
|
+
authConfigured: row.authConfigured,
|
|
98
|
+
ready: row.ready,
|
|
99
|
+
version: row.version,
|
|
100
|
+
tone: configured ? 'ok' : 'warning',
|
|
101
|
+
statusLine: configured
|
|
102
|
+
? `${versionPrefix}Configured on the selected machine`
|
|
103
|
+
: `${versionPrefix}Needs provider config on the selected machine`,
|
|
104
|
+
detail,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const authed = row.authConfigured || row.ready;
|
|
108
|
+
return {
|
|
109
|
+
id,
|
|
110
|
+
name: row.name || baseName,
|
|
111
|
+
installed: row.installed,
|
|
112
|
+
authConfigured: row.authConfigured,
|
|
113
|
+
ready: row.ready,
|
|
114
|
+
version: row.version,
|
|
115
|
+
tone: authed ? 'ok' : 'warning',
|
|
116
|
+
statusLine: authed
|
|
117
|
+
? `${versionPrefix}Signed in on the selected machine`
|
|
118
|
+
: `${versionPrefix}Not signed in on the selected machine`,
|
|
119
|
+
detail,
|
|
120
|
+
actionLabel: authed ? 'Re-auth' : 'Sign in',
|
|
121
|
+
actionRunner: id,
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
}
|
|
48
125
|
const FeedbackModal = () => {
|
|
49
126
|
const { width: winW, height: winH } = (0, react_native_1.useWindowDimensions)();
|
|
50
127
|
const isTablet = Math.min(winW, winH) >= 600;
|
|
@@ -61,6 +138,11 @@ const FeedbackModal = () => {
|
|
|
61
138
|
// is our guaranteed UI for bringing the icon back — we surface a
|
|
62
139
|
// small "Show quick icon" row when this is true.
|
|
63
140
|
const [quickIconHidden, setQuickIconHidden] = (0, react_1.useState)(false);
|
|
141
|
+
// Dev-server snapshot behind the reload buttons. `null` means "we have not
|
|
142
|
+
// been able to ask" — rendered as "not connected", never as "no dev server".
|
|
143
|
+
const [devSnapshot, setDevSnapshot] = (0, react_1.useState)(null);
|
|
144
|
+
// Which reload action is in flight, so only that button spins.
|
|
145
|
+
const [reloadingId, setReloadingId] = (0, react_1.useState)(null);
|
|
64
146
|
const [runnerAuthModal, setRunnerAuthModal] = (0, react_1.useState)(null);
|
|
65
147
|
// Vibing-input mode: same expand-on-tap pattern as email login.
|
|
66
148
|
// Tap "Vibing" once → the button reveals an input + Send; that lets
|
|
@@ -81,7 +163,35 @@ const FeedbackModal = () => {
|
|
|
81
163
|
title: 'No machine selected',
|
|
82
164
|
detail: 'Pick a remote dev machine before using the feedback actions.',
|
|
83
165
|
});
|
|
166
|
+
const [runnerCards, setRunnerCards] = (0, react_1.useState)(() => normalizeRunnerStatusRows([]));
|
|
167
|
+
const [runnerStatusLoading, setRunnerStatusLoading] = (0, react_1.useState)(false);
|
|
168
|
+
const [runnerStatusError, setRunnerStatusError] = (0, react_1.useState)(null);
|
|
169
|
+
const [preferredRunner, setPreferredRunnerState] = (0, react_1.useState)(null);
|
|
170
|
+
const [preferredModel, setPreferredModelState] = (0, react_1.useState)('');
|
|
171
|
+
const [showOpenCodeConfig, setShowOpenCodeConfig] = (0, react_1.useState)(false);
|
|
84
172
|
const mountedRef = (0, react_1.useRef)(true);
|
|
173
|
+
/**
|
|
174
|
+
* Ask the machine what its dev server is doing, so the reload actions can
|
|
175
|
+
* be enabled/disabled against reality rather than against a guess.
|
|
176
|
+
*
|
|
177
|
+
* Best-effort and deliberately null-on-failure: null means "we could not
|
|
178
|
+
* ask", which the seam renders as "not connected to a machine yet" — a
|
|
179
|
+
* different sentence from "no dev server is running", because they have
|
|
180
|
+
* different fixes.
|
|
181
|
+
*/
|
|
182
|
+
const refreshDevSnapshot = (0, react_1.useCallback)(async () => {
|
|
183
|
+
try {
|
|
184
|
+
const client = YaverFeedback_1.YaverFeedback.getP2PClient();
|
|
185
|
+
if (!client) {
|
|
186
|
+
setDevSnapshot(null);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
setDevSnapshot(await client.getDevServerStatus());
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
setDevSnapshot(null);
|
|
193
|
+
}
|
|
194
|
+
}, []);
|
|
85
195
|
const loadSelectedMachine = (0, react_1.useCallback)(async () => {
|
|
86
196
|
const cfg = YaverFeedback_1.YaverFeedback.getConfig();
|
|
87
197
|
if (!cfg?.authToken) {
|
|
@@ -178,6 +288,72 @@ const FeedbackModal = () => {
|
|
|
178
288
|
}
|
|
179
289
|
}
|
|
180
290
|
}, []);
|
|
291
|
+
const loadRunnerStatuses = (0, react_1.useCallback)(async () => {
|
|
292
|
+
const cfg = YaverFeedback_1.YaverFeedback.getConfig();
|
|
293
|
+
if (!cfg?.authToken) {
|
|
294
|
+
if (mountedRef.current) {
|
|
295
|
+
setRunnerCards(normalizeRunnerStatusRows([]));
|
|
296
|
+
setRunnerStatusError('Sign in to inspect coding-agent status.');
|
|
297
|
+
setRunnerStatusLoading(false);
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if (!cfg.preferredDeviceId) {
|
|
302
|
+
if (mountedRef.current) {
|
|
303
|
+
setRunnerCards(normalizeRunnerStatusRows([]));
|
|
304
|
+
setRunnerStatusError('Pick a machine to inspect coding-agent status.');
|
|
305
|
+
setRunnerStatusLoading(false);
|
|
306
|
+
}
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (mountedRef.current) {
|
|
310
|
+
setRunnerStatusLoading(true);
|
|
311
|
+
setRunnerStatusError(null);
|
|
312
|
+
}
|
|
313
|
+
try {
|
|
314
|
+
let client = YaverFeedback_1.YaverFeedback.getP2PClient();
|
|
315
|
+
if (!client) {
|
|
316
|
+
const ok = await YaverFeedback_1.YaverFeedback.reconnect();
|
|
317
|
+
if (ok)
|
|
318
|
+
client = YaverFeedback_1.YaverFeedback.getP2PClient();
|
|
319
|
+
}
|
|
320
|
+
if (!client) {
|
|
321
|
+
throw new Error('Not connected to the selected machine yet.');
|
|
322
|
+
}
|
|
323
|
+
const rows = await client.getRunnerAuthStatus();
|
|
324
|
+
if (mountedRef.current) {
|
|
325
|
+
setRunnerCards(normalizeRunnerStatusRows(rows));
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
catch (err) {
|
|
329
|
+
if (mountedRef.current) {
|
|
330
|
+
setRunnerCards(normalizeRunnerStatusRows([]));
|
|
331
|
+
setRunnerStatusError(err instanceof Error ? err.message : String(err));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
finally {
|
|
335
|
+
if (mountedRef.current)
|
|
336
|
+
setRunnerStatusLoading(false);
|
|
337
|
+
}
|
|
338
|
+
}, []);
|
|
339
|
+
const loadRoutingPrefs = (0, react_1.useCallback)(async () => {
|
|
340
|
+
try {
|
|
341
|
+
const [runner, model] = await Promise.all([
|
|
342
|
+
(0, preferences_1.getPreferredRunner)(),
|
|
343
|
+
(0, preferences_1.getPreferredModel)(),
|
|
344
|
+
]);
|
|
345
|
+
if (!mountedRef.current)
|
|
346
|
+
return;
|
|
347
|
+
setPreferredRunnerState(runner);
|
|
348
|
+
setPreferredModelState(model ?? '');
|
|
349
|
+
}
|
|
350
|
+
catch {
|
|
351
|
+
if (!mountedRef.current)
|
|
352
|
+
return;
|
|
353
|
+
setPreferredRunnerState(null);
|
|
354
|
+
setPreferredModelState('');
|
|
355
|
+
}
|
|
356
|
+
}, []);
|
|
181
357
|
(0, react_1.useEffect)(() => {
|
|
182
358
|
mountedRef.current = true;
|
|
183
359
|
const sub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:startReport', () => {
|
|
@@ -205,6 +381,7 @@ const FeedbackModal = () => {
|
|
|
205
381
|
})
|
|
206
382
|
.catch(() => { });
|
|
207
383
|
void loadSelectedMachine();
|
|
384
|
+
void loadRunnerStatuses();
|
|
208
385
|
}
|
|
209
386
|
});
|
|
210
387
|
// Agent streams build / compile progress through the BlackBox
|
|
@@ -232,15 +409,22 @@ const FeedbackModal = () => {
|
|
|
232
409
|
sub.remove();
|
|
233
410
|
statusSub.remove();
|
|
234
411
|
};
|
|
235
|
-
}, [loadSelectedMachine]);
|
|
412
|
+
}, [loadRunnerStatuses, loadSelectedMachine]);
|
|
236
413
|
(0, react_1.useEffect)(() => {
|
|
237
414
|
if (!visible)
|
|
238
415
|
return;
|
|
416
|
+
// Poll the dev server alongside the machine + runners. A reload button
|
|
417
|
+
// whose enabled state was decided once, when the sheet opened, goes
|
|
418
|
+
// stale the moment the user starts Metro from another surface — and a
|
|
419
|
+
// stale "no dev server is running" reads as the product being broken.
|
|
420
|
+
void refreshDevSnapshot();
|
|
239
421
|
const interval = setInterval(() => {
|
|
240
422
|
void loadSelectedMachine();
|
|
423
|
+
void loadRunnerStatuses();
|
|
424
|
+
void refreshDevSnapshot();
|
|
241
425
|
}, 5000);
|
|
242
426
|
return () => clearInterval(interval);
|
|
243
|
-
}, [loadSelectedMachine, visible]);
|
|
427
|
+
}, [loadRunnerStatuses, loadSelectedMachine, refreshDevSnapshot, visible]);
|
|
244
428
|
(0, react_1.useEffect)(() => {
|
|
245
429
|
if (!visible) {
|
|
246
430
|
setKeyboardInset(0);
|
|
@@ -273,6 +457,7 @@ const FeedbackModal = () => {
|
|
|
273
457
|
setAction('idle');
|
|
274
458
|
setShowVibeInput(false);
|
|
275
459
|
setVibePrompt('');
|
|
460
|
+
setRunnerStatusError(null);
|
|
276
461
|
}, []);
|
|
277
462
|
// Helper: run a P2P call; on network failure, ask YaverFeedback to
|
|
278
463
|
// re-query Convex for the fresh IP and retry once. Solves the common
|
|
@@ -323,7 +508,81 @@ const FeedbackModal = () => {
|
|
|
323
508
|
await fn(fresh);
|
|
324
509
|
}
|
|
325
510
|
}, []);
|
|
326
|
-
// ─── 1.
|
|
511
|
+
// ─── 1. Reload ─────────────────────────────────────────────────────
|
|
512
|
+
//
|
|
513
|
+
// Three actions now, not one: Hot Reload (mode=fast), Full Reload
|
|
514
|
+
// (mode=full — Flutter's hot RESTART), and Rebuild Bundle
|
|
515
|
+
// (/dev/reload-app, the only one that works with Metro down).
|
|
516
|
+
//
|
|
517
|
+
// WHICH of them render, and which are disabled with what reason, is
|
|
518
|
+
// decided by the pure `reloadActions()` seam — never inline here, so the
|
|
519
|
+
// same policy holds on web, Flutter, Unity, Swift and Kotlin. In
|
|
520
|
+
// particular: a production build (`__DEV__ === false`) gets NONE of them.
|
|
521
|
+
const availableReloadActions = (0, reloadActions_1.reloadActions)(devSnapshot, {
|
|
522
|
+
// __DEV__ is React Native's own build flag. A release bundle sets it
|
|
523
|
+
// false, so a shipped app renders no reload UI at all — which is the
|
|
524
|
+
// point, and is what reloadActions.test.ts pins.
|
|
525
|
+
isDevBuild: typeof __DEV__ !== 'undefined' && __DEV__ === true,
|
|
526
|
+
connected: devSnapshot !== null,
|
|
527
|
+
machineLabel: machineCard.device?.name || undefined,
|
|
528
|
+
includeRebuild: true,
|
|
529
|
+
});
|
|
530
|
+
const handleReloadAction = (0, react_1.useCallback)(async (reloadAction) => {
|
|
531
|
+
if (!reloadAction.enabled) {
|
|
532
|
+
// Pressing a disabled action must SAY why. A row that does nothing
|
|
533
|
+
// is the same defect as a spinner that never resolves.
|
|
534
|
+
setToast(reloadAction.disabledReason || 'Reload is unavailable right now.');
|
|
535
|
+
setError(reloadAction.disabledReason || null);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
setReloadingId(reloadAction.id);
|
|
539
|
+
setAction('hot-reloading');
|
|
540
|
+
setError(null);
|
|
541
|
+
setProgress(0);
|
|
542
|
+
setToast(`${reloadAction.label}…`);
|
|
543
|
+
try {
|
|
544
|
+
await loadSelectedMachine();
|
|
545
|
+
const selected = await YaverFeedback_1.YaverFeedback.getSelectedRemoteDevice();
|
|
546
|
+
if (!selected) {
|
|
547
|
+
YaverFeedback_1.YaverFeedback.showMachinePicker();
|
|
548
|
+
throw new Error('No machine selected. Pick a machine and try again.');
|
|
549
|
+
}
|
|
550
|
+
if (selected.needsAuth) {
|
|
551
|
+
YaverFeedback_1.YaverFeedback.showMachinePicker();
|
|
552
|
+
throw new Error('Selected machine needs pairing again.');
|
|
553
|
+
}
|
|
554
|
+
if (!selected.isOnline) {
|
|
555
|
+
throw new Error('Selected machine is offline. Start `yaver serve` on it first.');
|
|
556
|
+
}
|
|
557
|
+
let ackMessage = `${reloadAction.label} requested.`;
|
|
558
|
+
await runWithReconnect(async (client) => {
|
|
559
|
+
const ack = await client.reloadWithMode(reloadAction.mode, devSnapshot);
|
|
560
|
+
ackMessage = ack.message;
|
|
561
|
+
setToast(ack.message);
|
|
562
|
+
setProgress(0.2);
|
|
563
|
+
});
|
|
564
|
+
setToast(ackMessage);
|
|
565
|
+
if (reloadAction.mode === 'bundle')
|
|
566
|
+
closeSoon(2500);
|
|
567
|
+
}
|
|
568
|
+
catch (err) {
|
|
569
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
570
|
+
setError(message);
|
|
571
|
+
setToast(message.toLowerCase().indexOf('session expired') >= 0
|
|
572
|
+
? 'Session expired. Sign in again.'
|
|
573
|
+
: message);
|
|
574
|
+
await loadSelectedMachine();
|
|
575
|
+
setProgress(null);
|
|
576
|
+
}
|
|
577
|
+
finally {
|
|
578
|
+
if (mountedRef.current) {
|
|
579
|
+
setAction('idle');
|
|
580
|
+
setReloadingId(null);
|
|
581
|
+
void refreshDevSnapshot();
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}, [closeSoon, devSnapshot, loadSelectedMachine, refreshDevSnapshot, runWithReconnect]);
|
|
585
|
+
/** Kept for the legacy one-tap path (BlackBox command, chat Reload button). */
|
|
327
586
|
const handleHotReload = (0, react_1.useCallback)(async () => {
|
|
328
587
|
setAction('hot-reloading');
|
|
329
588
|
setError(null);
|
|
@@ -429,19 +688,36 @@ const FeedbackModal = () => {
|
|
|
429
688
|
try {
|
|
430
689
|
const { Dimensions } = require('react-native');
|
|
431
690
|
const { width, height } = Dimensions.get('window');
|
|
691
|
+
const cfg = YaverFeedback_1.YaverFeedback.getConfig();
|
|
692
|
+
const identity = (0, P2PClient_1.resolveReportIdentity)({
|
|
693
|
+
projectName: cfg?.projectName,
|
|
694
|
+
bundleId: cfg?.bundleId,
|
|
695
|
+
surface: cfg?.surface,
|
|
696
|
+
surfaces: cfg?.surfaces,
|
|
697
|
+
stack: cfg?.stack,
|
|
698
|
+
stacks: cfg?.stacks,
|
|
699
|
+
testSurfaces: cfg?.testSurfaces,
|
|
700
|
+
feedbackSdk: cfg?.feedbackSdk,
|
|
701
|
+
feedbackTransport: cfg?.feedbackTransport,
|
|
702
|
+
voiceCapabilities: cfg?.voiceCapabilities,
|
|
703
|
+
sttProvider: cfg?.sttProvider,
|
|
704
|
+
ttsProvider: cfg?.ttsProvider,
|
|
705
|
+
});
|
|
432
706
|
const deviceInfo = {
|
|
433
707
|
platform: react_native_1.Platform.OS,
|
|
434
708
|
osVersion: String(react_native_1.Platform.Version),
|
|
435
709
|
model: react_native_1.Platform.OS === 'ios' ? 'iOS Device' : 'Android Device',
|
|
436
710
|
screenWidth: width,
|
|
437
711
|
screenHeight: height,
|
|
712
|
+
appName: identity.appName,
|
|
438
713
|
};
|
|
439
714
|
const capturedErrors = YaverFeedback_1.YaverFeedback.getCapturedErrors();
|
|
440
715
|
const bundle = {
|
|
441
716
|
metadata: {
|
|
442
717
|
timestamp: new Date().toISOString(),
|
|
443
|
-
|
|
444
|
-
app:
|
|
718
|
+
deviceInfo,
|
|
719
|
+
app: identity.app,
|
|
720
|
+
project: identity.project,
|
|
445
721
|
userNote: '[Screenshot + Fix]',
|
|
446
722
|
},
|
|
447
723
|
screenshots: [path],
|
|
@@ -557,7 +833,13 @@ const FeedbackModal = () => {
|
|
|
557
833
|
setLastVibeTaskId(result.taskId);
|
|
558
834
|
// Hand off to VibeChatScreen — it streams the SSE transcript,
|
|
559
835
|
// accepts follow-ups, and surfaces a Reload button.
|
|
560
|
-
setActiveVibe({
|
|
836
|
+
setActiveVibe({
|
|
837
|
+
taskId: result.taskId,
|
|
838
|
+
initialPrompt: promptText,
|
|
839
|
+
project: identity.projectName,
|
|
840
|
+
runner: preferredRunner ?? undefined,
|
|
841
|
+
model: preferredModel ?? undefined,
|
|
842
|
+
});
|
|
561
843
|
setVibePrompt('');
|
|
562
844
|
setShowVibeInput(false);
|
|
563
845
|
}
|
|
@@ -575,6 +857,9 @@ const FeedbackModal = () => {
|
|
|
575
857
|
}, [closeSoon, isRecordingVideo, lastVideo]);
|
|
576
858
|
*/
|
|
577
859
|
const busy = action !== 'idle';
|
|
860
|
+
const readyRunnerCount = runnerCards.filter((row) => row.ready || row.authConfigured).length;
|
|
861
|
+
const missingRunnerCount = runnerCards.filter((row) => !row.installed).length;
|
|
862
|
+
const needsAuthRunnerCount = runnerCards.filter((row) => row.installed && !row.authConfigured && !row.ready).length;
|
|
578
863
|
// Once the user fires off a vibe task, swap the entire modal body
|
|
579
864
|
// for the live chat screen. The chat manages its own SSE
|
|
580
865
|
// subscription, multi-turn follow-ups, and Reload button. Closing
|
|
@@ -585,7 +870,7 @@ const FeedbackModal = () => {
|
|
|
585
870
|
<AuthOverlay_1.AuthOverlay />
|
|
586
871
|
<QuickActionIcon_1.QuickActionIcon />
|
|
587
872
|
<react_native_1.Modal visible={visible} animationType="slide" transparent onRequestClose={() => setActiveVibe(null)}>
|
|
588
|
-
{client ? (<VibeChatScreen_1.VibeChatScreen client={client} initialTaskId={activeVibe.taskId} initialUserPrompt={activeVibe.initialPrompt} onClose={() => setActiveVibe(null)} onReload={async () => {
|
|
873
|
+
{client ? (<VibeChatScreen_1.VibeChatScreen client={client} initialTaskId={activeVibe.taskId} initialUserPrompt={activeVibe.initialPrompt} project={activeVibe.project} runner={activeVibe.runner} model={activeVibe.model} onClose={() => setActiveVibe(null)} onReload={async () => {
|
|
589
874
|
const c = YaverFeedback_1.YaverFeedback.getP2PClient();
|
|
590
875
|
if (!c)
|
|
591
876
|
throw new Error('Not connected');
|
|
@@ -664,6 +949,57 @@ const FeedbackModal = () => {
|
|
|
664
949
|
<react_native_1.Text style={styles.machineMeta}>{machineCard.detail}</react_native_1.Text>
|
|
665
950
|
</react_native_1.Pressable>
|
|
666
951
|
|
|
952
|
+
<react_native_1.View style={styles.runnerSection}>
|
|
953
|
+
<react_native_1.View style={styles.runnerSectionHeader}>
|
|
954
|
+
<react_native_1.View style={{ flex: 1 }}>
|
|
955
|
+
<react_native_1.Text style={styles.runnerSectionTitle}>Coding Agents</react_native_1.Text>
|
|
956
|
+
<react_native_1.Text style={styles.runnerSectionSubtitle}>
|
|
957
|
+
{runnerStatusLoading
|
|
958
|
+
? 'Refreshing runner status on the selected machine…'
|
|
959
|
+
: `${readyRunnerCount} ready · ${needsAuthRunnerCount} need sign-in · ${missingRunnerCount} missing`}
|
|
960
|
+
</react_native_1.Text>
|
|
961
|
+
</react_native_1.View>
|
|
962
|
+
<react_native_1.Pressable onPress={() => void loadRunnerStatuses()} style={({ pressed }) => [
|
|
963
|
+
styles.runnerRefreshBtn,
|
|
964
|
+
pressed && styles.buttonPressed,
|
|
965
|
+
]} accessibilityRole="button" accessibilityLabel="Refresh coding-agent status">
|
|
966
|
+
<react_native_1.Text style={styles.runnerRefreshBtnText}>
|
|
967
|
+
{runnerStatusLoading ? 'Refreshing…' : 'Refresh'}
|
|
968
|
+
</react_native_1.Text>
|
|
969
|
+
</react_native_1.Pressable>
|
|
970
|
+
</react_native_1.View>
|
|
971
|
+
|
|
972
|
+
{runnerCards.map((row) => (<react_native_1.View key={row.id} style={[
|
|
973
|
+
styles.runnerCard,
|
|
974
|
+
row.tone === 'ok' && styles.runnerCardOk,
|
|
975
|
+
row.tone === 'warning' && styles.runnerCardWarning,
|
|
976
|
+
row.tone === 'error' && styles.runnerCardError,
|
|
977
|
+
]}>
|
|
978
|
+
<react_native_1.View style={styles.runnerCardTop}>
|
|
979
|
+
<react_native_1.View style={{ flex: 1 }}>
|
|
980
|
+
<react_native_1.Text style={styles.runnerCardTitle}>{row.name}</react_native_1.Text>
|
|
981
|
+
<react_native_1.Text style={[
|
|
982
|
+
styles.runnerCardStatus,
|
|
983
|
+
row.tone === 'ok' && styles.runnerCardStatusOk,
|
|
984
|
+
row.tone === 'warning' && styles.runnerCardStatusWarning,
|
|
985
|
+
row.tone === 'error' && styles.runnerCardStatusError,
|
|
986
|
+
]}>
|
|
987
|
+
{row.statusLine}
|
|
988
|
+
</react_native_1.Text>
|
|
989
|
+
</react_native_1.View>
|
|
990
|
+
{row.actionRunner ? (<react_native_1.Pressable onPress={() => setRunnerAuthModal(row.actionRunner ?? null)} style={({ pressed }) => [
|
|
991
|
+
styles.runnerActionBtn,
|
|
992
|
+
pressed && styles.buttonPressed,
|
|
993
|
+
]} accessibilityRole="button" accessibilityLabel={`${row.actionLabel} ${row.name}`}>
|
|
994
|
+
<react_native_1.Text style={styles.runnerActionBtnText}>{row.actionLabel}</react_native_1.Text>
|
|
995
|
+
</react_native_1.Pressable>) : null}
|
|
996
|
+
</react_native_1.View>
|
|
997
|
+
{row.detail ? (<react_native_1.Text style={styles.runnerCardDetail}>{row.detail}</react_native_1.Text>) : null}
|
|
998
|
+
</react_native_1.View>))}
|
|
999
|
+
|
|
1000
|
+
{runnerStatusError ? (<react_native_1.Text style={styles.runnerSectionError}>{runnerStatusError}</react_native_1.Text>) : null}
|
|
1001
|
+
</react_native_1.View>
|
|
1002
|
+
|
|
667
1003
|
{quickIconHidden && (<react_native_1.View style={styles.quickIconNote}>
|
|
668
1004
|
<react_native_1.Text style={styles.quickIconNoteText}>
|
|
669
1005
|
Quick access icon is hidden. Shake the phone if you want feedback back fast.
|
|
@@ -716,8 +1052,27 @@ const FeedbackModal = () => {
|
|
|
716
1052
|
</react_native_1.View>
|
|
717
1053
|
</react_native_1.View>
|
|
718
1054
|
|
|
719
|
-
{/* 1.
|
|
720
|
-
|
|
1055
|
+
{/* 1. Reload — Hot / Full / Rebuild Bundle.
|
|
1056
|
+
Rendered from the shared decision seam, so a production
|
|
1057
|
+
build (__DEV__ false) renders nothing here at all, and a
|
|
1058
|
+
blocked action shows greyed WITH its reason underneath
|
|
1059
|
+
rather than vanishing. */}
|
|
1060
|
+
{availableReloadActions.map((reloadAction) => (<react_native_1.View key={reloadAction.id} style={styles.reloadRow}>
|
|
1061
|
+
<ActionRow label={reloadingId === reloadAction.id
|
|
1062
|
+
? `${reloadAction.label}…`
|
|
1063
|
+
: reloadAction.label} tint={reloadAction.id === 'rebuild' ? '#38bdf8' : '#fbbf24'} onPress={() => {
|
|
1064
|
+
void handleReloadAction(reloadAction);
|
|
1065
|
+
}}
|
|
1066
|
+
// Never `disabled` at the Pressable level for a blocked
|
|
1067
|
+
// action: we WANT the tap so we can say why. Only a
|
|
1068
|
+
// genuinely busy modal blocks the press.
|
|
1069
|
+
disabled={busy && reloadingId !== reloadAction.id} busy={reloadingId === reloadAction.id}/>
|
|
1070
|
+
<react_native_1.Text style={styles.reloadHint}>
|
|
1071
|
+
{reloadAction.enabled
|
|
1072
|
+
? reloadAction.hint
|
|
1073
|
+
: reloadAction.disabledReason}
|
|
1074
|
+
</react_native_1.Text>
|
|
1075
|
+
</react_native_1.View>))}
|
|
721
1076
|
|
|
722
1077
|
{/* 3. Vibing — expands to an input box on first tap
|
|
723
1078
|
so the user says WHAT they want to vibe on, just
|
|
@@ -755,29 +1110,6 @@ const FeedbackModal = () => {
|
|
|
755
1110
|
platform smarts here. */}
|
|
756
1111
|
{!showDeploy ? (<ActionRow label="Deploy" tint="#7f8cf7" onPress={() => setShowDeploy(true)} disabled={busy}/>) : (<DeployPanel_1.DeployPanel onClose={() => setShowDeploy(false)}/>)}
|
|
757
1112
|
|
|
758
|
-
{/* Remote sign-in buttons — trigger codex/claude device-auth
|
|
759
|
-
on the selected agent without leaving the app. Opens a
|
|
760
|
-
small native modal showing the verification URL + 8-char
|
|
761
|
-
code the user enters in any browser. No API keys. */}
|
|
762
|
-
<react_native_1.View style={runnerAuthRowStyles.container}>
|
|
763
|
-
<react_native_1.Pressable onPress={() => setRunnerAuthModal('codex')} disabled={busy} style={({ pressed }) => [
|
|
764
|
-
runnerAuthRowStyles.button,
|
|
765
|
-
pressed && runnerAuthRowStyles.buttonPressed,
|
|
766
|
-
busy && runnerAuthRowStyles.buttonDisabled,
|
|
767
|
-
]} accessibilityRole="button" accessibilityLabel="Remote sign-in Codex">
|
|
768
|
-
<react_native_1.Text style={runnerAuthRowStyles.buttonLabel}>Remote sign-in</react_native_1.Text>
|
|
769
|
-
<react_native_1.Text style={runnerAuthRowStyles.buttonName}>Codex</react_native_1.Text>
|
|
770
|
-
</react_native_1.Pressable>
|
|
771
|
-
<react_native_1.Pressable onPress={() => setRunnerAuthModal('claude')} disabled={busy} style={({ pressed }) => [
|
|
772
|
-
runnerAuthRowStyles.button,
|
|
773
|
-
pressed && runnerAuthRowStyles.buttonPressed,
|
|
774
|
-
busy && runnerAuthRowStyles.buttonDisabled,
|
|
775
|
-
]} accessibilityRole="button" accessibilityLabel="Remote sign-in Claude">
|
|
776
|
-
<react_native_1.Text style={runnerAuthRowStyles.buttonLabel}>Remote sign-in</react_native_1.Text>
|
|
777
|
-
<react_native_1.Text style={runnerAuthRowStyles.buttonName}>Claude</react_native_1.Text>
|
|
778
|
-
</react_native_1.Pressable>
|
|
779
|
-
</react_native_1.View>
|
|
780
|
-
|
|
781
1113
|
{progress !== null && (<react_native_1.View style={styles.progressTrack}>
|
|
782
1114
|
<react_native_1.View style={[
|
|
783
1115
|
styles.progressFill,
|
|
@@ -798,7 +1130,10 @@ const FeedbackModal = () => {
|
|
|
798
1130
|
</react_native_1.KeyboardAvoidingView>
|
|
799
1131
|
</react_native_1.Pressable>
|
|
800
1132
|
</react_native_1.Modal>)}
|
|
801
|
-
{runnerAuthModal ? (<RunnerAuthNativeModal runner={runnerAuthModal} onClose={() =>
|
|
1133
|
+
{runnerAuthModal ? (<RunnerAuthNativeModal runner={runnerAuthModal} onClose={() => {
|
|
1134
|
+
setRunnerAuthModal(null);
|
|
1135
|
+
void loadRunnerStatuses();
|
|
1136
|
+
}}/>) : null}
|
|
802
1137
|
</>);
|
|
803
1138
|
};
|
|
804
1139
|
exports.FeedbackModal = FeedbackModal;
|
|
@@ -814,6 +1149,15 @@ const ActionRow = ({ label, tint, onPress, disabled, busy, }) => (<react_native_
|
|
|
814
1149
|
{busy ? (<react_native_1.ActivityIndicator color={tint} size="small"/>) : (<react_native_1.Text style={[styles.actionText, { color: tint }]}>{label}</react_native_1.Text>)}
|
|
815
1150
|
</react_native_1.Pressable>);
|
|
816
1151
|
const styles = react_native_1.StyleSheet.create({
|
|
1152
|
+
reloadRow: {
|
|
1153
|
+
gap: 4,
|
|
1154
|
+
},
|
|
1155
|
+
reloadHint: {
|
|
1156
|
+
color: '#8b8b93',
|
|
1157
|
+
fontSize: 11,
|
|
1158
|
+
lineHeight: 15,
|
|
1159
|
+
paddingHorizontal: 4,
|
|
1160
|
+
},
|
|
817
1161
|
vibeInputRow: {
|
|
818
1162
|
backgroundColor: 'rgba(129,140,248,0.08)',
|
|
819
1163
|
borderColor: 'rgba(129,140,248,0.4)',
|
|
@@ -996,6 +1340,106 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
996
1340
|
marginTop: 4,
|
|
997
1341
|
lineHeight: 17,
|
|
998
1342
|
},
|
|
1343
|
+
runnerSection: {
|
|
1344
|
+
marginTop: 2,
|
|
1345
|
+
gap: 10,
|
|
1346
|
+
},
|
|
1347
|
+
runnerSectionHeader: {
|
|
1348
|
+
flexDirection: 'row',
|
|
1349
|
+
alignItems: 'center',
|
|
1350
|
+
gap: 10,
|
|
1351
|
+
},
|
|
1352
|
+
runnerSectionTitle: {
|
|
1353
|
+
color: '#f8fafc',
|
|
1354
|
+
fontSize: 16,
|
|
1355
|
+
fontWeight: '700',
|
|
1356
|
+
},
|
|
1357
|
+
runnerSectionSubtitle: {
|
|
1358
|
+
marginTop: 2,
|
|
1359
|
+
color: '#94a3b8',
|
|
1360
|
+
fontSize: 12,
|
|
1361
|
+
},
|
|
1362
|
+
runnerRefreshBtn: {
|
|
1363
|
+
borderRadius: 10,
|
|
1364
|
+
borderWidth: 1,
|
|
1365
|
+
borderColor: 'rgba(148,163,184,0.22)',
|
|
1366
|
+
backgroundColor: 'rgba(15,23,42,0.65)',
|
|
1367
|
+
paddingHorizontal: 10,
|
|
1368
|
+
paddingVertical: 8,
|
|
1369
|
+
},
|
|
1370
|
+
runnerRefreshBtnText: {
|
|
1371
|
+
color: '#cbd5e1',
|
|
1372
|
+
fontSize: 12,
|
|
1373
|
+
fontWeight: '600',
|
|
1374
|
+
},
|
|
1375
|
+
runnerCard: {
|
|
1376
|
+
borderRadius: 12,
|
|
1377
|
+
borderWidth: 1,
|
|
1378
|
+
borderColor: 'rgba(148,163,184,0.14)',
|
|
1379
|
+
backgroundColor: 'rgba(15,23,42,0.45)',
|
|
1380
|
+
paddingHorizontal: 12,
|
|
1381
|
+
paddingVertical: 11,
|
|
1382
|
+
gap: 6,
|
|
1383
|
+
},
|
|
1384
|
+
runnerCardOk: {
|
|
1385
|
+
borderColor: 'rgba(34,197,94,0.28)',
|
|
1386
|
+
backgroundColor: 'rgba(20,83,45,0.20)',
|
|
1387
|
+
},
|
|
1388
|
+
runnerCardWarning: {
|
|
1389
|
+
borderColor: 'rgba(251,191,36,0.28)',
|
|
1390
|
+
backgroundColor: 'rgba(120,53,15,0.18)',
|
|
1391
|
+
},
|
|
1392
|
+
runnerCardError: {
|
|
1393
|
+
borderColor: 'rgba(248,113,113,0.28)',
|
|
1394
|
+
backgroundColor: 'rgba(127,29,29,0.18)',
|
|
1395
|
+
},
|
|
1396
|
+
runnerCardTop: {
|
|
1397
|
+
flexDirection: 'row',
|
|
1398
|
+
alignItems: 'center',
|
|
1399
|
+
gap: 10,
|
|
1400
|
+
},
|
|
1401
|
+
runnerCardTitle: {
|
|
1402
|
+
color: '#f8fafc',
|
|
1403
|
+
fontSize: 14,
|
|
1404
|
+
fontWeight: '700',
|
|
1405
|
+
},
|
|
1406
|
+
runnerCardStatus: {
|
|
1407
|
+
marginTop: 2,
|
|
1408
|
+
fontSize: 12,
|
|
1409
|
+
color: '#cbd5e1',
|
|
1410
|
+
},
|
|
1411
|
+
runnerCardStatusOk: {
|
|
1412
|
+
color: '#86efac',
|
|
1413
|
+
},
|
|
1414
|
+
runnerCardStatusWarning: {
|
|
1415
|
+
color: '#fcd34d',
|
|
1416
|
+
},
|
|
1417
|
+
runnerCardStatusError: {
|
|
1418
|
+
color: '#fca5a5',
|
|
1419
|
+
},
|
|
1420
|
+
runnerCardDetail: {
|
|
1421
|
+
color: '#94a3b8',
|
|
1422
|
+
fontSize: 11,
|
|
1423
|
+
lineHeight: 16,
|
|
1424
|
+
},
|
|
1425
|
+
runnerActionBtn: {
|
|
1426
|
+
borderRadius: 10,
|
|
1427
|
+
borderWidth: 1,
|
|
1428
|
+
borderColor: 'rgba(129,140,248,0.35)',
|
|
1429
|
+
backgroundColor: 'rgba(67,56,202,0.22)',
|
|
1430
|
+
paddingHorizontal: 12,
|
|
1431
|
+
paddingVertical: 8,
|
|
1432
|
+
},
|
|
1433
|
+
runnerActionBtnText: {
|
|
1434
|
+
color: '#c7d2fe',
|
|
1435
|
+
fontSize: 12,
|
|
1436
|
+
fontWeight: '700',
|
|
1437
|
+
},
|
|
1438
|
+
runnerSectionError: {
|
|
1439
|
+
color: '#fca5a5',
|
|
1440
|
+
fontSize: 12,
|
|
1441
|
+
lineHeight: 18,
|
|
1442
|
+
},
|
|
999
1443
|
captureChoices: {
|
|
1000
1444
|
gap: 10,
|
|
1001
1445
|
},
|
|
@@ -1328,39 +1772,6 @@ const RunnerAuthNativeModal = ({ runner, onClose }) => {
|
|
|
1328
1772
|
</react_native_1.View>
|
|
1329
1773
|
</react_native_1.Modal>);
|
|
1330
1774
|
};
|
|
1331
|
-
const runnerAuthRowStyles = react_native_1.StyleSheet.create({
|
|
1332
|
-
container: {
|
|
1333
|
-
flexDirection: 'row',
|
|
1334
|
-
gap: 8,
|
|
1335
|
-
marginTop: 8,
|
|
1336
|
-
flexWrap: 'wrap',
|
|
1337
|
-
},
|
|
1338
|
-
button: {
|
|
1339
|
-
flexGrow: 1,
|
|
1340
|
-
flexBasis: 0,
|
|
1341
|
-
minWidth: 120,
|
|
1342
|
-
paddingHorizontal: 12,
|
|
1343
|
-
paddingVertical: 10,
|
|
1344
|
-
borderRadius: 10,
|
|
1345
|
-
borderWidth: 1,
|
|
1346
|
-
borderColor: 'rgba(148,163,184,0.22)',
|
|
1347
|
-
backgroundColor: 'rgba(15,23,42,0.6)',
|
|
1348
|
-
},
|
|
1349
|
-
buttonPressed: { opacity: 0.7 },
|
|
1350
|
-
buttonDisabled: { opacity: 0.4 },
|
|
1351
|
-
buttonLabel: {
|
|
1352
|
-
fontSize: 10,
|
|
1353
|
-
color: '#94a3b8',
|
|
1354
|
-
textTransform: 'uppercase',
|
|
1355
|
-
letterSpacing: 0.8,
|
|
1356
|
-
},
|
|
1357
|
-
buttonName: {
|
|
1358
|
-
marginTop: 2,
|
|
1359
|
-
fontSize: 14,
|
|
1360
|
-
fontWeight: '600',
|
|
1361
|
-
color: '#f1f5f9',
|
|
1362
|
-
},
|
|
1363
|
-
});
|
|
1364
1775
|
const runnerAuthModalStyles = react_native_1.StyleSheet.create({
|
|
1365
1776
|
overlay: {
|
|
1366
1777
|
flex: 1,
|