yaver-feedback-react-native 0.8.12 → 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/app.plugin.js +13 -0
- package/dist/DeployPanel.d.ts +7 -0
- package/dist/DeployPanel.js +354 -0
- package/dist/FeedbackModal.js +408 -63
- package/dist/FloatingButton.js +25 -3
- package/dist/MachinePickerScreen.js +14 -5
- package/dist/P2PClient.d.ts +102 -1
- package/dist/P2PClient.js +313 -0
- package/dist/VibeChatScreen.d.ts +25 -0
- package/dist/VibeChatScreen.js +531 -0
- package/dist/_core/buildFeedbackPrompt.d.ts +13 -0
- package/dist/_core/buildFeedbackPrompt.js +77 -0
- package/dist/capture.d.ts +20 -0
- package/dist/capture.js +82 -0
- package/dist/preferences.d.ts +4 -0
- package/dist/preferences.js +62 -0
- package/dist/types.d.ts +66 -2
- package/dist/voice.d.ts +61 -0
- package/dist/voice.js +246 -0
- package/package.json +26 -3
- package/src/DeployPanel.tsx +403 -0
- package/src/FeedbackModal.tsx +510 -76
- package/src/FloatingButton.tsx +27 -3
- package/src/MachinePickerScreen.tsx +12 -3
- package/src/P2PClient.ts +326 -1
- package/src/VibeChatScreen.tsx +581 -0
- package/src/_core/buildFeedbackPrompt.ts +102 -0
- package/src/capture.ts +82 -0
- package/src/preferences.ts +56 -0
- package/src/types.ts +62 -2
- package/src/voice.ts +270 -0
package/dist/FeedbackModal.js
CHANGED
|
@@ -41,9 +41,91 @@ const capture_1 = require("./capture");
|
|
|
41
41
|
const upload_1 = require("./upload");
|
|
42
42
|
const AuthOverlay_1 = require("./AuthOverlay");
|
|
43
43
|
const QuickActionIcon_1 = require("./QuickActionIcon");
|
|
44
|
+
const VibeChatScreen_1 = require("./VibeChatScreen");
|
|
45
|
+
const DeployPanel_1 = require("./DeployPanel");
|
|
44
46
|
const auth_1 = require("./auth");
|
|
45
47
|
const preferences_1 = require("./preferences");
|
|
48
|
+
const PRIMARY_RUNNER_IDS = ['claude', 'codex', 'opencode'];
|
|
49
|
+
function normalizeRunnerStatusRows(rows) {
|
|
50
|
+
const byId = new Map();
|
|
51
|
+
for (const row of rows) {
|
|
52
|
+
const raw = String(row.id || '').trim().toLowerCase();
|
|
53
|
+
if (!raw)
|
|
54
|
+
continue;
|
|
55
|
+
const normalized = raw === 'claude-code' ? 'claude' : raw;
|
|
56
|
+
if (!PRIMARY_RUNNER_IDS.includes(normalized))
|
|
57
|
+
continue;
|
|
58
|
+
byId.set(normalized, { ...row, id: normalized });
|
|
59
|
+
}
|
|
60
|
+
return PRIMARY_RUNNER_IDS.map((id) => {
|
|
61
|
+
const baseName = id === 'claude' ? 'Claude Code' : id === 'codex' ? 'OpenAI Codex' : 'OpenCode';
|
|
62
|
+
const row = byId.get(id);
|
|
63
|
+
if (!row) {
|
|
64
|
+
return {
|
|
65
|
+
id,
|
|
66
|
+
name: baseName,
|
|
67
|
+
installed: false,
|
|
68
|
+
authConfigured: false,
|
|
69
|
+
ready: false,
|
|
70
|
+
tone: 'warning',
|
|
71
|
+
statusLine: 'Not installed on the selected machine',
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const versionPrefix = row.version?.trim() ? `${row.version.trim()} · ` : '';
|
|
75
|
+
const detail = row.error?.trim() || row.warning?.trim() || row.detail?.trim() || undefined;
|
|
76
|
+
if (!row.installed) {
|
|
77
|
+
return {
|
|
78
|
+
id,
|
|
79
|
+
name: row.name || baseName,
|
|
80
|
+
installed: false,
|
|
81
|
+
authConfigured: false,
|
|
82
|
+
ready: false,
|
|
83
|
+
version: row.version,
|
|
84
|
+
tone: 'warning',
|
|
85
|
+
statusLine: 'Not installed on the selected machine',
|
|
86
|
+
detail,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (id === 'opencode') {
|
|
90
|
+
const configured = row.authConfigured || row.ready;
|
|
91
|
+
return {
|
|
92
|
+
id,
|
|
93
|
+
name: row.name || baseName,
|
|
94
|
+
installed: row.installed,
|
|
95
|
+
authConfigured: row.authConfigured,
|
|
96
|
+
ready: row.ready,
|
|
97
|
+
version: row.version,
|
|
98
|
+
tone: configured ? 'ok' : 'warning',
|
|
99
|
+
statusLine: configured
|
|
100
|
+
? `${versionPrefix}Configured on the selected machine`
|
|
101
|
+
: `${versionPrefix}Needs provider config on the selected machine`,
|
|
102
|
+
detail,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const authed = row.authConfigured || row.ready;
|
|
106
|
+
return {
|
|
107
|
+
id,
|
|
108
|
+
name: row.name || baseName,
|
|
109
|
+
installed: row.installed,
|
|
110
|
+
authConfigured: row.authConfigured,
|
|
111
|
+
ready: row.ready,
|
|
112
|
+
version: row.version,
|
|
113
|
+
tone: authed ? 'ok' : 'warning',
|
|
114
|
+
statusLine: authed
|
|
115
|
+
? `${versionPrefix}Signed in on the selected machine`
|
|
116
|
+
: `${versionPrefix}Not signed in on the selected machine`,
|
|
117
|
+
detail,
|
|
118
|
+
actionLabel: authed ? 'Re-auth' : 'Sign in',
|
|
119
|
+
actionRunner: id,
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
}
|
|
46
123
|
const FeedbackModal = () => {
|
|
124
|
+
const { width: winW, height: winH } = (0, react_native_1.useWindowDimensions)();
|
|
125
|
+
const isTablet = Math.min(winW, winH) >= 600;
|
|
126
|
+
// Tablet color/icon picker fans out to 5/6 cols — 31% (3-col)
|
|
127
|
+
// looks empty on a 1024pt iPad. Mobile keeps 3-col.
|
|
128
|
+
const iconOptionWidthOverride = isTablet ? '18%' : undefined;
|
|
47
129
|
const [visible, setVisible] = (0, react_1.useState)(false);
|
|
48
130
|
const [action, setAction] = (0, react_1.useState)('idle');
|
|
49
131
|
const [error, setError] = (0, react_1.useState)(null);
|
|
@@ -61,6 +143,7 @@ const FeedbackModal = () => {
|
|
|
61
143
|
// "pick something for me" prompt (which in 0.7.13 pointed Claude at
|
|
62
144
|
// the wrong project because the matcher grepped the prompt itself).
|
|
63
145
|
const [showVibeInput, setShowVibeInput] = (0, react_1.useState)(false);
|
|
146
|
+
const [showDeploy, setShowDeploy] = (0, react_1.useState)(false);
|
|
64
147
|
const [vibePrompt, setVibePrompt] = (0, react_1.useState)('');
|
|
65
148
|
const [lastVibeTaskId, setLastVibeTaskId] = (0, react_1.useState)(null);
|
|
66
149
|
const [quickIconColorPreset, setQuickIconColorPreset] = (0, react_1.useState)(null);
|
|
@@ -73,6 +156,12 @@ const FeedbackModal = () => {
|
|
|
73
156
|
title: 'No machine selected',
|
|
74
157
|
detail: 'Pick a remote dev machine before using the feedback actions.',
|
|
75
158
|
});
|
|
159
|
+
const [runnerCards, setRunnerCards] = (0, react_1.useState)(() => normalizeRunnerStatusRows([]));
|
|
160
|
+
const [runnerStatusLoading, setRunnerStatusLoading] = (0, react_1.useState)(false);
|
|
161
|
+
const [runnerStatusError, setRunnerStatusError] = (0, react_1.useState)(null);
|
|
162
|
+
const [preferredRunner, setPreferredRunnerState] = (0, react_1.useState)(null);
|
|
163
|
+
const [preferredModel, setPreferredModelState] = (0, react_1.useState)('');
|
|
164
|
+
const [showOpenCodeConfig, setShowOpenCodeConfig] = (0, react_1.useState)(false);
|
|
76
165
|
const mountedRef = (0, react_1.useRef)(true);
|
|
77
166
|
const loadSelectedMachine = (0, react_1.useCallback)(async () => {
|
|
78
167
|
const cfg = YaverFeedback_1.YaverFeedback.getConfig();
|
|
@@ -170,6 +259,72 @@ const FeedbackModal = () => {
|
|
|
170
259
|
}
|
|
171
260
|
}
|
|
172
261
|
}, []);
|
|
262
|
+
const loadRunnerStatuses = (0, react_1.useCallback)(async () => {
|
|
263
|
+
const cfg = YaverFeedback_1.YaverFeedback.getConfig();
|
|
264
|
+
if (!cfg?.authToken) {
|
|
265
|
+
if (mountedRef.current) {
|
|
266
|
+
setRunnerCards(normalizeRunnerStatusRows([]));
|
|
267
|
+
setRunnerStatusError('Sign in to inspect coding-agent status.');
|
|
268
|
+
setRunnerStatusLoading(false);
|
|
269
|
+
}
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (!cfg.preferredDeviceId) {
|
|
273
|
+
if (mountedRef.current) {
|
|
274
|
+
setRunnerCards(normalizeRunnerStatusRows([]));
|
|
275
|
+
setRunnerStatusError('Pick a machine to inspect coding-agent status.');
|
|
276
|
+
setRunnerStatusLoading(false);
|
|
277
|
+
}
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (mountedRef.current) {
|
|
281
|
+
setRunnerStatusLoading(true);
|
|
282
|
+
setRunnerStatusError(null);
|
|
283
|
+
}
|
|
284
|
+
try {
|
|
285
|
+
let client = YaverFeedback_1.YaverFeedback.getP2PClient();
|
|
286
|
+
if (!client) {
|
|
287
|
+
const ok = await YaverFeedback_1.YaverFeedback.reconnect();
|
|
288
|
+
if (ok)
|
|
289
|
+
client = YaverFeedback_1.YaverFeedback.getP2PClient();
|
|
290
|
+
}
|
|
291
|
+
if (!client) {
|
|
292
|
+
throw new Error('Not connected to the selected machine yet.');
|
|
293
|
+
}
|
|
294
|
+
const rows = await client.getRunnerAuthStatus();
|
|
295
|
+
if (mountedRef.current) {
|
|
296
|
+
setRunnerCards(normalizeRunnerStatusRows(rows));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
catch (err) {
|
|
300
|
+
if (mountedRef.current) {
|
|
301
|
+
setRunnerCards(normalizeRunnerStatusRows([]));
|
|
302
|
+
setRunnerStatusError(err instanceof Error ? err.message : String(err));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
finally {
|
|
306
|
+
if (mountedRef.current)
|
|
307
|
+
setRunnerStatusLoading(false);
|
|
308
|
+
}
|
|
309
|
+
}, []);
|
|
310
|
+
const loadRoutingPrefs = (0, react_1.useCallback)(async () => {
|
|
311
|
+
try {
|
|
312
|
+
const [runner, model] = await Promise.all([
|
|
313
|
+
(0, preferences_1.getPreferredRunner)(),
|
|
314
|
+
(0, preferences_1.getPreferredModel)(),
|
|
315
|
+
]);
|
|
316
|
+
if (!mountedRef.current)
|
|
317
|
+
return;
|
|
318
|
+
setPreferredRunnerState(runner);
|
|
319
|
+
setPreferredModelState(model ?? '');
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
if (!mountedRef.current)
|
|
323
|
+
return;
|
|
324
|
+
setPreferredRunnerState(null);
|
|
325
|
+
setPreferredModelState('');
|
|
326
|
+
}
|
|
327
|
+
}, []);
|
|
173
328
|
(0, react_1.useEffect)(() => {
|
|
174
329
|
mountedRef.current = true;
|
|
175
330
|
const sub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:startReport', () => {
|
|
@@ -197,6 +352,7 @@ const FeedbackModal = () => {
|
|
|
197
352
|
})
|
|
198
353
|
.catch(() => { });
|
|
199
354
|
void loadSelectedMachine();
|
|
355
|
+
void loadRunnerStatuses();
|
|
200
356
|
}
|
|
201
357
|
});
|
|
202
358
|
// Agent streams build / compile progress through the BlackBox
|
|
@@ -224,15 +380,16 @@ const FeedbackModal = () => {
|
|
|
224
380
|
sub.remove();
|
|
225
381
|
statusSub.remove();
|
|
226
382
|
};
|
|
227
|
-
}, [loadSelectedMachine]);
|
|
383
|
+
}, [loadRunnerStatuses, loadSelectedMachine]);
|
|
228
384
|
(0, react_1.useEffect)(() => {
|
|
229
385
|
if (!visible)
|
|
230
386
|
return;
|
|
231
387
|
const interval = setInterval(() => {
|
|
232
388
|
void loadSelectedMachine();
|
|
389
|
+
void loadRunnerStatuses();
|
|
233
390
|
}, 5000);
|
|
234
391
|
return () => clearInterval(interval);
|
|
235
|
-
}, [loadSelectedMachine, visible]);
|
|
392
|
+
}, [loadRunnerStatuses, loadSelectedMachine, visible]);
|
|
236
393
|
(0, react_1.useEffect)(() => {
|
|
237
394
|
if (!visible) {
|
|
238
395
|
setKeyboardInset(0);
|
|
@@ -265,6 +422,7 @@ const FeedbackModal = () => {
|
|
|
265
422
|
setAction('idle');
|
|
266
423
|
setShowVibeInput(false);
|
|
267
424
|
setVibePrompt('');
|
|
425
|
+
setRunnerStatusError(null);
|
|
268
426
|
}, []);
|
|
269
427
|
// Helper: run a P2P call; on network failure, ask YaverFeedback to
|
|
270
428
|
// re-query Convex for the fresh IP and retry once. Solves the common
|
|
@@ -488,6 +646,13 @@ const FeedbackModal = () => {
|
|
|
488
646
|
setShowVibeInput(false);
|
|
489
647
|
}
|
|
490
648
|
}, [showVibeInput, vibePrompt]);
|
|
649
|
+
// Hold the active vibe-chat session — set when handleVibingSubmit
|
|
650
|
+
// returns a fresh taskId. Renders <VibeChatScreen> which streams the
|
|
651
|
+
// SSE transcript, supports multi-turn follow-ups via /tasks/{id}/
|
|
652
|
+
// resume, and exposes a Reload button. Mirrors the in-Yaver native
|
|
653
|
+
// pane's transcript-mode behaviour, just rendered in RN here.
|
|
654
|
+
const [activeVibe, setActiveVibe] = (0, react_1.useState)(null);
|
|
655
|
+
const [includeScreenshot, setIncludeScreenshot] = (0, react_1.useState)(true);
|
|
491
656
|
const handleVibingSubmit = (0, react_1.useCallback)(async () => {
|
|
492
657
|
const client = YaverFeedback_1.YaverFeedback.getP2PClient();
|
|
493
658
|
if (!client) {
|
|
@@ -506,13 +671,49 @@ const FeedbackModal = () => {
|
|
|
506
671
|
.join('\n')
|
|
507
672
|
: '';
|
|
508
673
|
const userPrompt = vibePrompt.trim();
|
|
509
|
-
const
|
|
674
|
+
const promptText = userPrompt
|
|
510
675
|
? userPrompt + errNote
|
|
511
676
|
: 'Pick the next small improvement or fix for this app based on recent activity and the current screen.' +
|
|
512
677
|
errNote;
|
|
513
|
-
|
|
678
|
+
// Optional screenshot — captured from the host app's window.
|
|
679
|
+
// captureScreenshotBase64 returns null when react-native-view-
|
|
680
|
+
// shot isn't installed; we skip the screenshot rather than
|
|
681
|
+
// abort the whole feedback in that case.
|
|
682
|
+
let screenshotBase64;
|
|
683
|
+
if (includeScreenshot) {
|
|
684
|
+
const cap = await Promise.resolve().then(() => __importStar(require('./capture')));
|
|
685
|
+
const captured = await cap.captureScreenshotBase64();
|
|
686
|
+
if (captured?.base64) {
|
|
687
|
+
screenshotBase64 = captured.base64;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
// Resolve project context the same way reloadApp / vibing did.
|
|
691
|
+
const { resolveAppIdentity } = await Promise.resolve().then(() => __importStar(require('./P2PClient')));
|
|
692
|
+
const identity = resolveAppIdentity();
|
|
693
|
+
// Pull the user's preferred runner / model from local prefs.
|
|
694
|
+
// Both are optional — the agent falls back to whatever runner
|
|
695
|
+
// is signed in if neither is provided.
|
|
696
|
+
const prefs = await Promise.resolve().then(() => __importStar(require('./preferences')));
|
|
697
|
+
const preferredRunner = (await prefs.getPreferredRunner?.()) ?? null;
|
|
698
|
+
const preferredModel = (await prefs.getPreferredModel?.()) ?? null;
|
|
699
|
+
const result = await client.createFeedbackTask({
|
|
700
|
+
userPrompt: promptText,
|
|
701
|
+
projectName: identity.projectName,
|
|
702
|
+
projectPath: identity.projectPath,
|
|
703
|
+
runner: preferredRunner ?? undefined,
|
|
704
|
+
model: preferredModel ?? undefined,
|
|
705
|
+
screenshotBase64,
|
|
706
|
+
});
|
|
514
707
|
setLastVibeTaskId(result.taskId);
|
|
515
|
-
|
|
708
|
+
// Hand off to VibeChatScreen — it streams the SSE transcript,
|
|
709
|
+
// accepts follow-ups, and surfaces a Reload button.
|
|
710
|
+
setActiveVibe({
|
|
711
|
+
taskId: result.taskId,
|
|
712
|
+
initialPrompt: promptText,
|
|
713
|
+
project: identity.projectName,
|
|
714
|
+
runner: preferredRunner ?? undefined,
|
|
715
|
+
model: preferredModel ?? undefined,
|
|
716
|
+
});
|
|
516
717
|
setVibePrompt('');
|
|
517
718
|
setShowVibeInput(false);
|
|
518
719
|
}
|
|
@@ -523,20 +724,57 @@ const FeedbackModal = () => {
|
|
|
523
724
|
if (mountedRef.current)
|
|
524
725
|
setAction('idle');
|
|
525
726
|
}
|
|
526
|
-
}, [vibePrompt]);
|
|
727
|
+
}, [vibePrompt, includeScreenshot]);
|
|
527
728
|
/*
|
|
528
729
|
const handleScreenRecording = useCallback(async () => {
|
|
529
730
|
...
|
|
530
731
|
}, [closeSoon, isRecordingVideo, lastVideo]);
|
|
531
732
|
*/
|
|
532
733
|
const busy = action !== 'idle';
|
|
734
|
+
const readyRunnerCount = runnerCards.filter((row) => row.ready || row.authConfigured).length;
|
|
735
|
+
const missingRunnerCount = runnerCards.filter((row) => !row.installed).length;
|
|
736
|
+
const needsAuthRunnerCount = runnerCards.filter((row) => row.installed && !row.authConfigured && !row.ready).length;
|
|
737
|
+
// Once the user fires off a vibe task, swap the entire modal body
|
|
738
|
+
// for the live chat screen. The chat manages its own SSE
|
|
739
|
+
// subscription, multi-turn follow-ups, and Reload button. Closing
|
|
740
|
+
// the chat returns to idle and clears the active vibe.
|
|
741
|
+
if (visible && activeVibe) {
|
|
742
|
+
const client = YaverFeedback_1.YaverFeedback.getP2PClient();
|
|
743
|
+
return (<>
|
|
744
|
+
<AuthOverlay_1.AuthOverlay />
|
|
745
|
+
<QuickActionIcon_1.QuickActionIcon />
|
|
746
|
+
<react_native_1.Modal visible={visible} animationType="slide" transparent onRequestClose={() => setActiveVibe(null)}>
|
|
747
|
+
{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 () => {
|
|
748
|
+
const c = YaverFeedback_1.YaverFeedback.getP2PClient();
|
|
749
|
+
if (!c)
|
|
750
|
+
throw new Error('Not connected');
|
|
751
|
+
await c.reloadApp();
|
|
752
|
+
}}/>) : null}
|
|
753
|
+
</react_native_1.Modal>
|
|
754
|
+
</>);
|
|
755
|
+
}
|
|
533
756
|
return (<>
|
|
534
757
|
<AuthOverlay_1.AuthOverlay />
|
|
535
758
|
<QuickActionIcon_1.QuickActionIcon />
|
|
536
759
|
{visible && (<react_native_1.Modal visible={visible} animationType="slide" transparent onRequestClose={handleClose}>
|
|
537
760
|
<react_native_1.Pressable style={styles.overlay} onPress={handleClose}>
|
|
538
761
|
<react_native_1.KeyboardAvoidingView behavior={react_native_1.Platform.OS === 'ios' ? 'padding' : 'height'} keyboardVerticalOffset={react_native_1.Platform.OS === 'ios' ? 12 : 0} style={styles.kbAvoider} pointerEvents="box-none">
|
|
539
|
-
<react_native_1.Pressable
|
|
762
|
+
<react_native_1.Pressable
|
|
763
|
+
// Tablet: cap modal width and center as a card-style
|
|
764
|
+
// sheet rather than a phone bottom sheet that stretches
|
|
765
|
+
// across a 12.9" iPad. Phone behaviour unchanged.
|
|
766
|
+
style={[
|
|
767
|
+
styles.modal,
|
|
768
|
+
isTablet
|
|
769
|
+
? {
|
|
770
|
+
width: '100%',
|
|
771
|
+
maxWidth: 640,
|
|
772
|
+
alignSelf: 'center',
|
|
773
|
+
borderTopLeftRadius: 22,
|
|
774
|
+
borderTopRightRadius: 22,
|
|
775
|
+
}
|
|
776
|
+
: null,
|
|
777
|
+
]} onPress={(e) => {
|
|
540
778
|
e.stopPropagation();
|
|
541
779
|
react_native_1.Keyboard.dismiss();
|
|
542
780
|
}}>
|
|
@@ -585,6 +823,57 @@ const FeedbackModal = () => {
|
|
|
585
823
|
<react_native_1.Text style={styles.machineMeta}>{machineCard.detail}</react_native_1.Text>
|
|
586
824
|
</react_native_1.Pressable>
|
|
587
825
|
|
|
826
|
+
<react_native_1.View style={styles.runnerSection}>
|
|
827
|
+
<react_native_1.View style={styles.runnerSectionHeader}>
|
|
828
|
+
<react_native_1.View style={{ flex: 1 }}>
|
|
829
|
+
<react_native_1.Text style={styles.runnerSectionTitle}>Coding Agents</react_native_1.Text>
|
|
830
|
+
<react_native_1.Text style={styles.runnerSectionSubtitle}>
|
|
831
|
+
{runnerStatusLoading
|
|
832
|
+
? 'Refreshing runner status on the selected machine…'
|
|
833
|
+
: `${readyRunnerCount} ready · ${needsAuthRunnerCount} need sign-in · ${missingRunnerCount} missing`}
|
|
834
|
+
</react_native_1.Text>
|
|
835
|
+
</react_native_1.View>
|
|
836
|
+
<react_native_1.Pressable onPress={() => void loadRunnerStatuses()} style={({ pressed }) => [
|
|
837
|
+
styles.runnerRefreshBtn,
|
|
838
|
+
pressed && styles.buttonPressed,
|
|
839
|
+
]} accessibilityRole="button" accessibilityLabel="Refresh coding-agent status">
|
|
840
|
+
<react_native_1.Text style={styles.runnerRefreshBtnText}>
|
|
841
|
+
{runnerStatusLoading ? 'Refreshing…' : 'Refresh'}
|
|
842
|
+
</react_native_1.Text>
|
|
843
|
+
</react_native_1.Pressable>
|
|
844
|
+
</react_native_1.View>
|
|
845
|
+
|
|
846
|
+
{runnerCards.map((row) => (<react_native_1.View key={row.id} style={[
|
|
847
|
+
styles.runnerCard,
|
|
848
|
+
row.tone === 'ok' && styles.runnerCardOk,
|
|
849
|
+
row.tone === 'warning' && styles.runnerCardWarning,
|
|
850
|
+
row.tone === 'error' && styles.runnerCardError,
|
|
851
|
+
]}>
|
|
852
|
+
<react_native_1.View style={styles.runnerCardTop}>
|
|
853
|
+
<react_native_1.View style={{ flex: 1 }}>
|
|
854
|
+
<react_native_1.Text style={styles.runnerCardTitle}>{row.name}</react_native_1.Text>
|
|
855
|
+
<react_native_1.Text style={[
|
|
856
|
+
styles.runnerCardStatus,
|
|
857
|
+
row.tone === 'ok' && styles.runnerCardStatusOk,
|
|
858
|
+
row.tone === 'warning' && styles.runnerCardStatusWarning,
|
|
859
|
+
row.tone === 'error' && styles.runnerCardStatusError,
|
|
860
|
+
]}>
|
|
861
|
+
{row.statusLine}
|
|
862
|
+
</react_native_1.Text>
|
|
863
|
+
</react_native_1.View>
|
|
864
|
+
{row.actionRunner ? (<react_native_1.Pressable onPress={() => setRunnerAuthModal(row.actionRunner ?? null)} style={({ pressed }) => [
|
|
865
|
+
styles.runnerActionBtn,
|
|
866
|
+
pressed && styles.buttonPressed,
|
|
867
|
+
]} accessibilityRole="button" accessibilityLabel={`${row.actionLabel} ${row.name}`}>
|
|
868
|
+
<react_native_1.Text style={styles.runnerActionBtnText}>{row.actionLabel}</react_native_1.Text>
|
|
869
|
+
</react_native_1.Pressable>) : null}
|
|
870
|
+
</react_native_1.View>
|
|
871
|
+
{row.detail ? (<react_native_1.Text style={styles.runnerCardDetail}>{row.detail}</react_native_1.Text>) : null}
|
|
872
|
+
</react_native_1.View>))}
|
|
873
|
+
|
|
874
|
+
{runnerStatusError ? (<react_native_1.Text style={styles.runnerSectionError}>{runnerStatusError}</react_native_1.Text>) : null}
|
|
875
|
+
</react_native_1.View>
|
|
876
|
+
|
|
588
877
|
{quickIconHidden && (<react_native_1.View style={styles.quickIconNote}>
|
|
589
878
|
<react_native_1.Text style={styles.quickIconNoteText}>
|
|
590
879
|
Quick access icon is hidden. Shake the phone if you want feedback back fast.
|
|
@@ -613,6 +902,7 @@ const FeedbackModal = () => {
|
|
|
613
902
|
void YaverFeedback_1.YaverFeedback.setQuickIconColorPreset(preset);
|
|
614
903
|
}} style={[
|
|
615
904
|
styles.iconOption,
|
|
905
|
+
iconOptionWidthOverride ? { width: iconOptionWidthOverride } : null,
|
|
616
906
|
selected && styles.iconOptionSelected,
|
|
617
907
|
]}>
|
|
618
908
|
<react_native_1.View style={[
|
|
@@ -667,28 +957,13 @@ const FeedbackModal = () => {
|
|
|
667
957
|
{/* Screenshot & Fix */}
|
|
668
958
|
<ActionRow label={action === 'capturing' ? 'Working…' : 'Screenshot & Fix'} tint="#22c55e" onPress={handleScreenshotAndFix} disabled={busy} busy={action === 'capturing'}/>
|
|
669
959
|
|
|
670
|
-
{/*
|
|
671
|
-
on the
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
pressed && runnerAuthRowStyles.buttonPressed,
|
|
678
|
-
busy && runnerAuthRowStyles.buttonDisabled,
|
|
679
|
-
]} accessibilityRole="button" accessibilityLabel="Remote sign-in Codex">
|
|
680
|
-
<react_native_1.Text style={runnerAuthRowStyles.buttonLabel}>Remote sign-in</react_native_1.Text>
|
|
681
|
-
<react_native_1.Text style={runnerAuthRowStyles.buttonName}>Codex</react_native_1.Text>
|
|
682
|
-
</react_native_1.Pressable>
|
|
683
|
-
<react_native_1.Pressable onPress={() => setRunnerAuthModal('claude')} disabled={busy} style={({ pressed }) => [
|
|
684
|
-
runnerAuthRowStyles.button,
|
|
685
|
-
pressed && runnerAuthRowStyles.buttonPressed,
|
|
686
|
-
busy && runnerAuthRowStyles.buttonDisabled,
|
|
687
|
-
]} accessibilityRole="button" accessibilityLabel="Remote sign-in Claude">
|
|
688
|
-
<react_native_1.Text style={runnerAuthRowStyles.buttonLabel}>Remote sign-in</react_native_1.Text>
|
|
689
|
-
<react_native_1.Text style={runnerAuthRowStyles.buttonName}>Claude</react_native_1.Text>
|
|
690
|
-
</react_native_1.Pressable>
|
|
691
|
-
</react_native_1.View>
|
|
960
|
+
{/* Deploy — opens an inline panel that talks to
|
|
961
|
+
/fleet/deploy-options on the agent and lets the user
|
|
962
|
+
pick TestFlight / Play / Both, then a machine to run
|
|
963
|
+
it on. Capabilities (e.g. "Linux can't TestFlight")
|
|
964
|
+
come from the agent's doctor probes — no client-side
|
|
965
|
+
platform smarts here. */}
|
|
966
|
+
{!showDeploy ? (<ActionRow label="Deploy" tint="#7f8cf7" onPress={() => setShowDeploy(true)} disabled={busy}/>) : (<DeployPanel_1.DeployPanel onClose={() => setShowDeploy(false)}/>)}
|
|
692
967
|
|
|
693
968
|
{progress !== null && (<react_native_1.View style={styles.progressTrack}>
|
|
694
969
|
<react_native_1.View style={[
|
|
@@ -710,7 +985,10 @@ const FeedbackModal = () => {
|
|
|
710
985
|
</react_native_1.KeyboardAvoidingView>
|
|
711
986
|
</react_native_1.Pressable>
|
|
712
987
|
</react_native_1.Modal>)}
|
|
713
|
-
{runnerAuthModal ? (<RunnerAuthNativeModal runner={runnerAuthModal} onClose={() =>
|
|
988
|
+
{runnerAuthModal ? (<RunnerAuthNativeModal runner={runnerAuthModal} onClose={() => {
|
|
989
|
+
setRunnerAuthModal(null);
|
|
990
|
+
void loadRunnerStatuses();
|
|
991
|
+
}}/>) : null}
|
|
714
992
|
</>);
|
|
715
993
|
};
|
|
716
994
|
exports.FeedbackModal = FeedbackModal;
|
|
@@ -908,6 +1186,106 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
908
1186
|
marginTop: 4,
|
|
909
1187
|
lineHeight: 17,
|
|
910
1188
|
},
|
|
1189
|
+
runnerSection: {
|
|
1190
|
+
marginTop: 2,
|
|
1191
|
+
gap: 10,
|
|
1192
|
+
},
|
|
1193
|
+
runnerSectionHeader: {
|
|
1194
|
+
flexDirection: 'row',
|
|
1195
|
+
alignItems: 'center',
|
|
1196
|
+
gap: 10,
|
|
1197
|
+
},
|
|
1198
|
+
runnerSectionTitle: {
|
|
1199
|
+
color: '#f8fafc',
|
|
1200
|
+
fontSize: 16,
|
|
1201
|
+
fontWeight: '700',
|
|
1202
|
+
},
|
|
1203
|
+
runnerSectionSubtitle: {
|
|
1204
|
+
marginTop: 2,
|
|
1205
|
+
color: '#94a3b8',
|
|
1206
|
+
fontSize: 12,
|
|
1207
|
+
},
|
|
1208
|
+
runnerRefreshBtn: {
|
|
1209
|
+
borderRadius: 10,
|
|
1210
|
+
borderWidth: 1,
|
|
1211
|
+
borderColor: 'rgba(148,163,184,0.22)',
|
|
1212
|
+
backgroundColor: 'rgba(15,23,42,0.65)',
|
|
1213
|
+
paddingHorizontal: 10,
|
|
1214
|
+
paddingVertical: 8,
|
|
1215
|
+
},
|
|
1216
|
+
runnerRefreshBtnText: {
|
|
1217
|
+
color: '#cbd5e1',
|
|
1218
|
+
fontSize: 12,
|
|
1219
|
+
fontWeight: '600',
|
|
1220
|
+
},
|
|
1221
|
+
runnerCard: {
|
|
1222
|
+
borderRadius: 12,
|
|
1223
|
+
borderWidth: 1,
|
|
1224
|
+
borderColor: 'rgba(148,163,184,0.14)',
|
|
1225
|
+
backgroundColor: 'rgba(15,23,42,0.45)',
|
|
1226
|
+
paddingHorizontal: 12,
|
|
1227
|
+
paddingVertical: 11,
|
|
1228
|
+
gap: 6,
|
|
1229
|
+
},
|
|
1230
|
+
runnerCardOk: {
|
|
1231
|
+
borderColor: 'rgba(34,197,94,0.28)',
|
|
1232
|
+
backgroundColor: 'rgba(20,83,45,0.20)',
|
|
1233
|
+
},
|
|
1234
|
+
runnerCardWarning: {
|
|
1235
|
+
borderColor: 'rgba(251,191,36,0.28)',
|
|
1236
|
+
backgroundColor: 'rgba(120,53,15,0.18)',
|
|
1237
|
+
},
|
|
1238
|
+
runnerCardError: {
|
|
1239
|
+
borderColor: 'rgba(248,113,113,0.28)',
|
|
1240
|
+
backgroundColor: 'rgba(127,29,29,0.18)',
|
|
1241
|
+
},
|
|
1242
|
+
runnerCardTop: {
|
|
1243
|
+
flexDirection: 'row',
|
|
1244
|
+
alignItems: 'center',
|
|
1245
|
+
gap: 10,
|
|
1246
|
+
},
|
|
1247
|
+
runnerCardTitle: {
|
|
1248
|
+
color: '#f8fafc',
|
|
1249
|
+
fontSize: 14,
|
|
1250
|
+
fontWeight: '700',
|
|
1251
|
+
},
|
|
1252
|
+
runnerCardStatus: {
|
|
1253
|
+
marginTop: 2,
|
|
1254
|
+
fontSize: 12,
|
|
1255
|
+
color: '#cbd5e1',
|
|
1256
|
+
},
|
|
1257
|
+
runnerCardStatusOk: {
|
|
1258
|
+
color: '#86efac',
|
|
1259
|
+
},
|
|
1260
|
+
runnerCardStatusWarning: {
|
|
1261
|
+
color: '#fcd34d',
|
|
1262
|
+
},
|
|
1263
|
+
runnerCardStatusError: {
|
|
1264
|
+
color: '#fca5a5',
|
|
1265
|
+
},
|
|
1266
|
+
runnerCardDetail: {
|
|
1267
|
+
color: '#94a3b8',
|
|
1268
|
+
fontSize: 11,
|
|
1269
|
+
lineHeight: 16,
|
|
1270
|
+
},
|
|
1271
|
+
runnerActionBtn: {
|
|
1272
|
+
borderRadius: 10,
|
|
1273
|
+
borderWidth: 1,
|
|
1274
|
+
borderColor: 'rgba(129,140,248,0.35)',
|
|
1275
|
+
backgroundColor: 'rgba(67,56,202,0.22)',
|
|
1276
|
+
paddingHorizontal: 12,
|
|
1277
|
+
paddingVertical: 8,
|
|
1278
|
+
},
|
|
1279
|
+
runnerActionBtnText: {
|
|
1280
|
+
color: '#c7d2fe',
|
|
1281
|
+
fontSize: 12,
|
|
1282
|
+
fontWeight: '700',
|
|
1283
|
+
},
|
|
1284
|
+
runnerSectionError: {
|
|
1285
|
+
color: '#fca5a5',
|
|
1286
|
+
fontSize: 12,
|
|
1287
|
+
lineHeight: 18,
|
|
1288
|
+
},
|
|
911
1289
|
captureChoices: {
|
|
912
1290
|
gap: 10,
|
|
913
1291
|
},
|
|
@@ -1240,39 +1618,6 @@ const RunnerAuthNativeModal = ({ runner, onClose }) => {
|
|
|
1240
1618
|
</react_native_1.View>
|
|
1241
1619
|
</react_native_1.Modal>);
|
|
1242
1620
|
};
|
|
1243
|
-
const runnerAuthRowStyles = react_native_1.StyleSheet.create({
|
|
1244
|
-
container: {
|
|
1245
|
-
flexDirection: 'row',
|
|
1246
|
-
gap: 8,
|
|
1247
|
-
marginTop: 8,
|
|
1248
|
-
flexWrap: 'wrap',
|
|
1249
|
-
},
|
|
1250
|
-
button: {
|
|
1251
|
-
flexGrow: 1,
|
|
1252
|
-
flexBasis: 0,
|
|
1253
|
-
minWidth: 120,
|
|
1254
|
-
paddingHorizontal: 12,
|
|
1255
|
-
paddingVertical: 10,
|
|
1256
|
-
borderRadius: 10,
|
|
1257
|
-
borderWidth: 1,
|
|
1258
|
-
borderColor: 'rgba(148,163,184,0.22)',
|
|
1259
|
-
backgroundColor: 'rgba(15,23,42,0.6)',
|
|
1260
|
-
},
|
|
1261
|
-
buttonPressed: { opacity: 0.7 },
|
|
1262
|
-
buttonDisabled: { opacity: 0.4 },
|
|
1263
|
-
buttonLabel: {
|
|
1264
|
-
fontSize: 10,
|
|
1265
|
-
color: '#94a3b8',
|
|
1266
|
-
textTransform: 'uppercase',
|
|
1267
|
-
letterSpacing: 0.8,
|
|
1268
|
-
},
|
|
1269
|
-
buttonName: {
|
|
1270
|
-
marginTop: 2,
|
|
1271
|
-
fontSize: 14,
|
|
1272
|
-
fontWeight: '600',
|
|
1273
|
-
color: '#f1f5f9',
|
|
1274
|
-
},
|
|
1275
|
-
});
|
|
1276
1621
|
const runnerAuthModalStyles = react_native_1.StyleSheet.create({
|
|
1277
1622
|
overlay: {
|
|
1278
1623
|
flex: 1,
|
package/dist/FloatingButton.js
CHANGED
|
@@ -41,6 +41,14 @@ const FixReport_1 = require("./FixReport");
|
|
|
41
41
|
const BlackBox_1 = require("./BlackBox");
|
|
42
42
|
const DEFAULT_SIZE = 40;
|
|
43
43
|
const DEFAULT_COLOR = '#6366f1';
|
|
44
|
+
// Tablet detection — short-edge dp >= 600 means iPad / 7"+ Android
|
|
45
|
+
// tablet / Z Fold open. The SDK has no app-side responsive context
|
|
46
|
+
// to lean on (it's a guest in third-party apps), so we infer
|
|
47
|
+
// locally and bump the button + panel to tablet sizes.
|
|
48
|
+
const TABLET_SHORT_EDGE = 600;
|
|
49
|
+
function isTabletWindow(width, height) {
|
|
50
|
+
return Math.min(width, height) >= TABLET_SHORT_EDGE;
|
|
51
|
+
}
|
|
44
52
|
const DEFAULT_PANEL_BG = '#2d2d2d';
|
|
45
53
|
/**
|
|
46
54
|
* Draggable debug console button for the Yaver Feedback SDK.
|
|
@@ -71,7 +79,16 @@ const DEFAULT_PANEL_BG = '#2d2d2d';
|
|
|
71
79
|
* - **"quit"** → disable the SDK
|
|
72
80
|
*/
|
|
73
81
|
const FloatingButton = ({ onPress, initialPosition, size = DEFAULT_SIZE, color = DEFAULT_COLOR, showStatusDot = true, style: stylePreset = 'terminal', icon, agentUrl: agentUrlProp, authToken: authTokenProp, healthCheckInterval = 5000, panelBackgroundColor, }) => {
|
|
74
|
-
|
|
82
|
+
// Read window size live so the SDK overlay re-pins itself when
|
|
83
|
+
// the host app rotates or splits. The legacy snapshot via
|
|
84
|
+
// Dimensions.get only ran once and parked the button off-screen
|
|
85
|
+
// after orientation changes on iPad.
|
|
86
|
+
const { width: screenWidth, height: screenHeight } = (0, react_native_1.useWindowDimensions)();
|
|
87
|
+
const isTablet = isTabletWindow(screenWidth, screenHeight);
|
|
88
|
+
// Tablets get a larger touch target and a wider panel — phones
|
|
89
|
+
// keep the existing 40 / 280 defaults so guest apps aren't
|
|
90
|
+
// disrupted on small screens.
|
|
91
|
+
const effectiveSize = isTablet ? Math.max(size, 52) : size;
|
|
75
92
|
const defaultX = initialPosition?.x ?? 10;
|
|
76
93
|
const defaultY = initialPosition?.y ?? 90;
|
|
77
94
|
const pan = (0, react_1.useRef)(new react_native_1.Animated.ValueXY({ x: defaultX, y: defaultY })).current;
|
|
@@ -516,7 +533,12 @@ const FloatingButton = ({ onPress, initialPosition, size = DEFAULT_SIZE, color =
|
|
|
516
533
|
const isTerminal = stylePreset === 'terminal';
|
|
517
534
|
const buttonIcon = icon ?? 'y';
|
|
518
535
|
const btnBg = isConnected ? color : `${color}88`;
|
|
519
|
-
|
|
536
|
+
// Panel sizing — tablets get a wider compact panel (420) and a
|
|
537
|
+
// capped full-size panel (max 720 instead of full window) so the
|
|
538
|
+
// overlay doesn't dwarf the host app on a 12.9" iPad.
|
|
539
|
+
const compactPanelWidth = isTablet ? 420 : 280;
|
|
540
|
+
const fullPanelWidth = isTablet ? Math.min(screenWidth - 24, 720) : screenWidth - 24;
|
|
541
|
+
const panelWidth = fullSize ? fullPanelWidth : compactPanelWidth;
|
|
520
542
|
return (<react_native_1.Animated.View style={[s.root, { transform: [{ translateX: pan.x }, { translateY: pan.y }] }]} {...panResponder.panHandlers}>
|
|
521
543
|
{/* Console panel */}
|
|
522
544
|
{chatOpen && (<react_native_1.View style={[
|
|
@@ -642,7 +664,7 @@ const FloatingButton = ({ onPress, initialPosition, size = DEFAULT_SIZE, color =
|
|
|
642
664
|
<react_native_1.TouchableOpacity style={[
|
|
643
665
|
s.button,
|
|
644
666
|
isTerminal ? s.buttonTerminal : s.buttonMinimal,
|
|
645
|
-
{ backgroundColor: btnBg, width:
|
|
667
|
+
{ backgroundColor: btnBg, width: effectiveSize, height: effectiveSize },
|
|
646
668
|
!isTerminal && { borderRadius: size / 2 },
|
|
647
669
|
]} activeOpacity={0.7} onPress={handleTap}>
|
|
648
670
|
<react_native_1.Text style={[s.buttonIcon, isTerminal && s.mono, { fontSize: 22 }]}>
|