yaver-feedback-react-native 0.9.2 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -1
- package/dist/AuthOverlay.d.ts +1 -14
- package/dist/AuthOverlay.js +9 -62
- package/dist/Discovery.js +0 -6
- package/dist/DogfoodRuntime.d.ts +124 -0
- package/dist/DogfoodRuntime.js +273 -0
- package/dist/FeedbackModal.js +347 -61
- package/dist/LoginScreen.d.ts +1 -5
- package/dist/LoginScreen.js +2 -6
- package/dist/MachinePickerScreen.d.ts +1 -3
- package/dist/MachinePickerScreen.js +6 -18
- package/dist/P2PClient.d.ts +116 -3
- package/dist/P2PClient.js +273 -3
- package/dist/P2PDogfoodDriver.d.ts +12 -0
- package/dist/P2PDogfoodDriver.js +118 -0
- package/dist/PairDeviceModal.d.ts +2 -3
- package/dist/ShakeDetector.js +31 -17
- package/dist/VibeChatScreen.d.ts +11 -1
- package/dist/VibeChatScreen.js +200 -41
- package/dist/YaverFeedback.d.ts +34 -0
- package/dist/YaverFeedback.js +124 -19
- package/dist/YaverModeBadge.d.ts +22 -0
- package/dist/YaverModeBadge.js +219 -0
- package/dist/__tests__/AuthDevices.test.js +1 -48
- package/dist/__tests__/DogfoodRuntime.test.d.ts +1 -0
- package/dist/__tests__/DogfoodRuntime.test.js +116 -0
- package/dist/__tests__/P2PDogfoodDriver.test.d.ts +1 -0
- package/dist/__tests__/P2PDogfoodDriver.test.js +64 -0
- package/dist/__tests__/ReportIdentity.test.d.ts +25 -1
- package/dist/__tests__/ReportIdentity.test.js +34 -22
- package/dist/__tests__/ShakeDetectorWeb.test.d.ts +1 -0
- package/dist/__tests__/ShakeDetectorWeb.test.js +27 -0
- package/dist/__tests__/YaverFeedback.test.js +13 -3
- package/dist/__tests__/deviceDogfood.test.d.ts +1 -0
- package/dist/__tests__/deviceDogfood.test.js +86 -0
- package/dist/__tests__/dogfoodPolicy.test.d.ts +1 -0
- package/dist/__tests__/dogfoodPolicy.test.js +33 -0
- package/dist/_core/ansi.d.ts +117 -0
- package/dist/_core/ansi.js +468 -0
- package/dist/_core/ansi.test.d.ts +1 -0
- package/dist/_core/ansi.test.js +225 -0
- package/dist/_core/buildFeedbackPrompt.d.ts +4 -9
- package/dist/_core/buildFeedbackPrompt.js +18 -72
- package/dist/_core/constants.d.ts +19 -6
- package/dist/_core/constants.js +20 -7
- package/dist/_core/device.d.ts +9 -23
- package/dist/_core/device.js +13 -28
- package/dist/_core/endpoints.d.ts +0 -7
- package/dist/_core/endpoints.js +0 -7
- package/dist/_core/index.d.ts +4 -0
- package/dist/_core/index.js +4 -0
- package/dist/_core/remoteless.d.ts +44 -0
- package/dist/_core/remoteless.js +75 -0
- package/dist/_core/trace.d.ts +47 -0
- package/dist/_core/trace.js +38 -0
- package/dist/_core/trace.test.d.ts +1 -0
- package/dist/_core/trace.test.js +60 -0
- package/dist/auth.d.ts +3 -60
- package/dist/auth.js +6 -88
- package/dist/deviceDogfood.d.ts +53 -0
- package/dist/deviceDogfood.js +137 -0
- package/dist/dogfoodPolicy.d.ts +25 -0
- package/dist/dogfoodPolicy.js +24 -0
- package/dist/index.d.ts +13 -4
- package/dist/index.js +24 -8
- package/dist/reloadActions.js +2 -2
- package/dist/types.d.ts +50 -7
- package/package.json +12 -3
- package/src/AuthOverlay.tsx +20 -106
- package/src/Discovery.ts +0 -6
- package/src/DogfoodRuntime.ts +373 -0
- package/src/FeedbackModal.tsx +445 -67
- package/src/LoginScreen.tsx +2 -22
- package/src/MachinePickerScreen.tsx +6 -21
- package/src/P2PClient.ts +347 -4
- package/src/P2PDogfoodDriver.ts +132 -0
- package/src/PairDeviceModal.tsx +2 -3
- package/src/ShakeDetector.ts +31 -18
- package/src/VibeChatScreen.tsx +232 -42
- package/src/YaverFeedback.ts +133 -22
- package/src/YaverModeBadge.tsx +234 -0
- package/src/__tests__/AuthDevices.test.ts +1 -52
- package/src/__tests__/DogfoodRuntime.test.ts +135 -0
- package/src/__tests__/P2PDogfoodDriver.test.ts +80 -0
- package/src/__tests__/ReportIdentity.test.ts +36 -27
- package/src/__tests__/ShakeDetectorWeb.test.ts +30 -0
- package/src/__tests__/YaverFeedback.test.ts +15 -3
- package/src/__tests__/deviceDogfood.test.ts +77 -0
- package/src/__tests__/dogfoodPolicy.test.ts +34 -0
- package/src/_core/ansi.test.ts +250 -0
- package/src/_core/ansi.ts +475 -0
- package/src/_core/buildFeedbackPrompt.ts +18 -95
- package/src/_core/constants.ts +20 -6
- package/src/_core/device.ts +13 -30
- package/src/_core/endpoints.ts +0 -7
- package/src/_core/index.ts +4 -0
- package/src/_core/remoteless.ts +110 -0
- package/src/_core/trace.test.ts +58 -0
- package/src/_core/trace.ts +75 -0
- package/src/auth.ts +7 -156
- package/src/deviceDogfood.ts +171 -0
- package/src/dogfoodPolicy.ts +40 -0
- package/src/index.ts +40 -11
- package/src/reloadActions.ts +2 -2
- package/src/types.ts +45 -7
- package/dist/GuestOnboardingScreen.d.ts +0 -8
- package/dist/GuestOnboardingScreen.js +0 -282
- package/src/GuestOnboardingScreen.tsx +0 -307
package/src/FeedbackModal.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
DeviceEventEmitter,
|
|
5
5
|
Keyboard,
|
|
6
6
|
KeyboardAvoidingView,
|
|
7
|
+
Linking,
|
|
7
8
|
Modal,
|
|
8
9
|
Platform,
|
|
9
10
|
Pressable,
|
|
@@ -35,6 +36,7 @@ import {
|
|
|
35
36
|
} from './types';
|
|
36
37
|
import { AuthOverlay } from './AuthOverlay';
|
|
37
38
|
import { QuickActionIcon } from './QuickActionIcon';
|
|
39
|
+
import { YaverModeBadge } from './YaverModeBadge';
|
|
38
40
|
import { VibeChatScreen } from './VibeChatScreen';
|
|
39
41
|
import { DeployPanel } from './DeployPanel';
|
|
40
42
|
import { listReachableDevices, RemoteDevice } from './auth';
|
|
@@ -48,6 +50,14 @@ import {
|
|
|
48
50
|
setPreferredModel,
|
|
49
51
|
setPreferredRunner,
|
|
50
52
|
} from './preferences';
|
|
53
|
+
import {
|
|
54
|
+
DogfoodController,
|
|
55
|
+
defaultDogfoodLane,
|
|
56
|
+
dogfoodLaneOptions,
|
|
57
|
+
type DogfoodLane,
|
|
58
|
+
type DogfoodSnapshot,
|
|
59
|
+
} from './DogfoodRuntime';
|
|
60
|
+
import { createP2PDogfoodDriver } from './P2PDogfoodDriver';
|
|
51
61
|
|
|
52
62
|
/**
|
|
53
63
|
* Simplified feedback modal — launch scope is 3 actions:
|
|
@@ -91,6 +101,7 @@ type RunnerCardState = {
|
|
|
91
101
|
detail?: string;
|
|
92
102
|
actionLabel?: string;
|
|
93
103
|
actionRunner?: string;
|
|
104
|
+
models?: Array<{ id: string; name?: string; provider?: string; isDefault?: boolean }>;
|
|
94
105
|
};
|
|
95
106
|
|
|
96
107
|
type ProviderEditorState = {
|
|
@@ -101,6 +112,14 @@ type ProviderEditorState = {
|
|
|
101
112
|
apiKey: string;
|
|
102
113
|
};
|
|
103
114
|
|
|
115
|
+
type DogfoodProjectChoice = {
|
|
116
|
+
name: string;
|
|
117
|
+
path: string;
|
|
118
|
+
framework?: string;
|
|
119
|
+
frameworks?: string[];
|
|
120
|
+
surfaces?: string[];
|
|
121
|
+
};
|
|
122
|
+
|
|
104
123
|
const PRIMARY_RUNNER_IDS = ['claude', 'codex', 'opencode'] as const;
|
|
105
124
|
|
|
106
125
|
function normalizeRunnerStatusRows(rows: RunnerAuthStatusRow[]): RunnerCardState[] {
|
|
@@ -142,6 +161,7 @@ function normalizeRunnerStatusRows(rows: RunnerAuthStatusRow[]): RunnerCardState
|
|
|
142
161
|
version: row.version,
|
|
143
162
|
tone: 'warning',
|
|
144
163
|
statusLine: 'Not installed on the selected machine',
|
|
164
|
+
models: row.models,
|
|
145
165
|
detail,
|
|
146
166
|
};
|
|
147
167
|
}
|
|
@@ -159,6 +179,7 @@ function normalizeRunnerStatusRows(rows: RunnerAuthStatusRow[]): RunnerCardState
|
|
|
159
179
|
statusLine: configured
|
|
160
180
|
? `${versionPrefix}Configured on the selected machine`
|
|
161
181
|
: `${versionPrefix}Needs provider config on the selected machine`,
|
|
182
|
+
models: row.models,
|
|
162
183
|
detail,
|
|
163
184
|
};
|
|
164
185
|
}
|
|
@@ -175,6 +196,7 @@ function normalizeRunnerStatusRows(rows: RunnerAuthStatusRow[]): RunnerCardState
|
|
|
175
196
|
statusLine: authed
|
|
176
197
|
? `${versionPrefix}Signed in on the selected machine`
|
|
177
198
|
: `${versionPrefix}Not signed in on the selected machine`,
|
|
199
|
+
models: row.models,
|
|
178
200
|
detail,
|
|
179
201
|
actionLabel: authed ? 'Re-auth' : 'Sign in',
|
|
180
202
|
actionRunner: id,
|
|
@@ -182,6 +204,36 @@ function normalizeRunnerStatusRows(rows: RunnerAuthStatusRow[]): RunnerCardState
|
|
|
182
204
|
});
|
|
183
205
|
}
|
|
184
206
|
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Renders the "inside Yaver" mark unless the app opted out.
|
|
210
|
+
*
|
|
211
|
+
* Lives here rather than in YaverModeBadge so the component stays a dumb view
|
|
212
|
+
* and the DEFAULT (`modeBadge !== false` → on) is expressed in exactly one
|
|
213
|
+
* place. Mounted from FeedbackModal because that is the component integrating
|
|
214
|
+
* apps already render — a default that required a new mount point would not be
|
|
215
|
+
* a default, it would be a feature request.
|
|
216
|
+
*/
|
|
217
|
+
const YaverModeBadgeGate: React.FC = () => {
|
|
218
|
+
const [, refresh] = useState(0);
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
const sub = DeviceEventEmitter.addListener('yaverFeedback:dogfoodChanged', () => refresh((n) => n + 1));
|
|
221
|
+
return () => sub.remove();
|
|
222
|
+
}, []);
|
|
223
|
+
const cfg = YaverFeedback.getConfig();
|
|
224
|
+
if (cfg?.modeBadge === false) return null;
|
|
225
|
+
const dogfood = YaverFeedback.getDogfoodStatus();
|
|
226
|
+
return (
|
|
227
|
+
<YaverModeBadge
|
|
228
|
+
position={cfg?.modeBadgePosition ?? 'bottom-left'}
|
|
229
|
+
force={dogfood.active}
|
|
230
|
+
dogfood={dogfood.active}
|
|
231
|
+
dogfoodLabel={dogfood.label}
|
|
232
|
+
onExitDogfood={() => YaverFeedback.exitDogfoodMode()}
|
|
233
|
+
/>
|
|
234
|
+
);
|
|
235
|
+
};
|
|
236
|
+
|
|
185
237
|
export const FeedbackModal: React.FC = () => {
|
|
186
238
|
const { width: winW, height: winH } = useWindowDimensions();
|
|
187
239
|
const isTablet = Math.min(winW, winH) >= 600;
|
|
@@ -189,6 +241,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
189
241
|
// looks empty on a 1024pt iPad. Mobile keeps 3-col.
|
|
190
242
|
const iconOptionWidthOverride = isTablet ? '18%' : undefined;
|
|
191
243
|
const [visible, setVisible] = useState(false);
|
|
244
|
+
const [dogfoodActive, setDogfoodActive] = useState(() => YaverFeedback.getDogfoodStatus().active);
|
|
192
245
|
const [action, setAction] = useState<ActionState>('idle');
|
|
193
246
|
const [error, setError] = useState<string | null>(null);
|
|
194
247
|
const [toast, setToast] = useState<string | null>(null);
|
|
@@ -231,9 +284,86 @@ export const FeedbackModal: React.FC = () => {
|
|
|
231
284
|
const [runnerStatusError, setRunnerStatusError] = useState<string | null>(null);
|
|
232
285
|
const [preferredRunner, setPreferredRunnerState] = useState<string | null>(null);
|
|
233
286
|
const [preferredModel, setPreferredModelState] = useState('');
|
|
287
|
+
const [activeTab, setActiveTab] = useState<'chat' | 'settings'>('chat');
|
|
234
288
|
const [showOpenCodeConfig, setShowOpenCodeConfig] = useState(false);
|
|
289
|
+
const scrollRef = useRef<ScrollView>(null);
|
|
290
|
+
const [dogfoodEnrollment, setDogfoodEnrollment] = useState<{
|
|
291
|
+
status: string;
|
|
292
|
+
installationId?: string;
|
|
293
|
+
error?: string;
|
|
294
|
+
} | null>(null);
|
|
295
|
+
const [dogfoodProjects, setDogfoodProjects] = useState<DogfoodProjectChoice[]>([]);
|
|
296
|
+
const [dogfoodProject, setDogfoodProject] = useState<DogfoodProjectChoice | null>(null);
|
|
297
|
+
const [dogfoodLane, setDogfoodLane] = useState<DogfoodLane>('browser');
|
|
298
|
+
const [dogfoodNativeAvailable, setDogfoodNativeAvailable] = useState(false);
|
|
299
|
+
const [dogfoodRuntime, setDogfoodRuntime] = useState<DogfoodSnapshot | null>(null);
|
|
300
|
+
const [dogfoodSetupLoading, setDogfoodSetupLoading] = useState(false);
|
|
301
|
+
const dogfoodControllerRef = useRef<DogfoodController | null>(null);
|
|
235
302
|
const mountedRef = useRef(true);
|
|
236
303
|
|
|
304
|
+
const loadDogfoodOnboarding = useCallback(async () => {
|
|
305
|
+
const onboarding = YaverFeedback.getDogfoodOnboarding();
|
|
306
|
+
if (!onboarding) return;
|
|
307
|
+
setDogfoodSetupLoading(true);
|
|
308
|
+
setDogfoodEnrollment({ status: 'checking' });
|
|
309
|
+
try {
|
|
310
|
+
const enrolled = await YaverFeedback.enableDeviceDogfood(onboarding);
|
|
311
|
+
if (!mountedRef.current) return;
|
|
312
|
+
setDogfoodEnrollment({ status: enrolled.status, installationId: enrolled.installationId });
|
|
313
|
+
if (enrolled.status !== 'active') return;
|
|
314
|
+
let client = YaverFeedback.getP2PClient();
|
|
315
|
+
if (!client && await YaverFeedback.reconnect()) client = YaverFeedback.getP2PClient();
|
|
316
|
+
if (!client) throw new Error('The selected Yaver machine is not reachable yet.');
|
|
317
|
+
const projects = await client.listDogfoodProjects();
|
|
318
|
+
if (!mountedRef.current) return;
|
|
319
|
+
setDogfoodProjects(projects);
|
|
320
|
+
const hint = String(onboarding.projectName || '').trim().toLowerCase();
|
|
321
|
+
const preferred = projects.find((item) => item.name.toLowerCase() === hint)
|
|
322
|
+
|| projects.find((item) => hint && item.name.toLowerCase().includes(hint))
|
|
323
|
+
|| projects[0]
|
|
324
|
+
|| null;
|
|
325
|
+
setDogfoodProject((current) => current && projects.some((item) => item.path === current.path) ? current : preferred);
|
|
326
|
+
if (preferred) setDogfoodLane(defaultDogfoodLane(preferred.framework || onboarding.framework || 'expo'));
|
|
327
|
+
} catch (cause) {
|
|
328
|
+
if (mountedRef.current) setDogfoodEnrollment({ status: 'failed', error: cause instanceof Error ? cause.message : String(cause) });
|
|
329
|
+
} finally {
|
|
330
|
+
if (mountedRef.current) setDogfoodSetupLoading(false);
|
|
331
|
+
}
|
|
332
|
+
}, []);
|
|
333
|
+
|
|
334
|
+
useEffect(() => {
|
|
335
|
+
let cancelled = false;
|
|
336
|
+
setDogfoodNativeAvailable(false);
|
|
337
|
+
const client = YaverFeedback.getP2PClient();
|
|
338
|
+
if (!client || !dogfoodProject) return () => { cancelled = true; };
|
|
339
|
+
const framework = dogfoodProject.framework || YaverFeedback.getDogfoodOnboarding()?.framework || 'expo';
|
|
340
|
+
void client.getDogfoodRemoteRuntimeCapabilities(dogfoodProject.path, framework)
|
|
341
|
+
.then((value) => {
|
|
342
|
+
if (!cancelled) setDogfoodNativeAvailable(value.targets.some((target) => target.enabled && target.id !== 'browser-window'));
|
|
343
|
+
})
|
|
344
|
+
.catch(() => {});
|
|
345
|
+
return () => { cancelled = true; };
|
|
346
|
+
}, [dogfoodProject]);
|
|
347
|
+
|
|
348
|
+
const startDogfoodRuntime = useCallback(async () => {
|
|
349
|
+
const client = YaverFeedback.getP2PClient();
|
|
350
|
+
const onboarding = YaverFeedback.getDogfoodOnboarding();
|
|
351
|
+
if (!client || !dogfoodProject || !onboarding) return;
|
|
352
|
+
await dogfoodControllerRef.current?.stop().catch(() => {});
|
|
353
|
+
const framework = dogfoodProject.framework || onboarding.framework || 'expo';
|
|
354
|
+
const controller = new DogfoodController({
|
|
355
|
+
name: dogfoodProject.name,
|
|
356
|
+
workDir: dogfoodProject.path,
|
|
357
|
+
framework,
|
|
358
|
+
lane: dogfoodLane,
|
|
359
|
+
}, createP2PDogfoodDriver(client), {
|
|
360
|
+
onChange: (snapshot) => { if (mountedRef.current) setDogfoodRuntime(snapshot); },
|
|
361
|
+
});
|
|
362
|
+
dogfoodControllerRef.current = controller;
|
|
363
|
+
setDogfoodRuntime(controller.snapshot());
|
|
364
|
+
await controller.trigger().catch(() => {});
|
|
365
|
+
}, [dogfoodLane, dogfoodProject]);
|
|
366
|
+
|
|
237
367
|
/**
|
|
238
368
|
* Ask the machine what its dev server is doing, so the reload actions can
|
|
239
369
|
* be enabled/disabled against reality rather than against a guess.
|
|
@@ -291,9 +421,8 @@ export const FeedbackModal: React.FC = () => {
|
|
|
291
421
|
|
|
292
422
|
try {
|
|
293
423
|
const devices = await listReachableDevices(cfg.authToken);
|
|
294
|
-
const all = [...devices.owned, ...devices.shared];
|
|
295
424
|
const device =
|
|
296
|
-
|
|
425
|
+
devices.owned.find((candidate) => candidate.deviceId === cfg.preferredDeviceId) ?? null;
|
|
297
426
|
|
|
298
427
|
if (!device) {
|
|
299
428
|
if (mountedRef.current) {
|
|
@@ -315,9 +444,8 @@ export const FeedbackModal: React.FC = () => {
|
|
|
315
444
|
reachable = await client.health();
|
|
316
445
|
}
|
|
317
446
|
|
|
318
|
-
const hostHint = device.hostEmail ? ` via ${device.hostEmail}` : '';
|
|
319
447
|
let status: MachineCardState['status'] = 'live';
|
|
320
|
-
let detail =
|
|
448
|
+
let detail = device.platform;
|
|
321
449
|
|
|
322
450
|
if (!device.isOnline) {
|
|
323
451
|
status = 'offline';
|
|
@@ -390,7 +518,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
390
518
|
if (!client) {
|
|
391
519
|
throw new Error('Not connected to the selected machine yet.');
|
|
392
520
|
}
|
|
393
|
-
const rows = await client.
|
|
521
|
+
const rows = await client.getAvailableRunners();
|
|
394
522
|
if (mountedRef.current) {
|
|
395
523
|
setRunnerCards(normalizeRunnerStatusRows(rows));
|
|
396
524
|
}
|
|
@@ -424,13 +552,20 @@ export const FeedbackModal: React.FC = () => {
|
|
|
424
552
|
mountedRef.current = true;
|
|
425
553
|
const sub = DeviceEventEmitter.addListener('yaverFeedback:startReport', () => {
|
|
426
554
|
if (YaverFeedback.isEnabled()) {
|
|
555
|
+
const onboarding = YaverFeedback.getDogfoodOnboarding();
|
|
556
|
+
const directDogfood = YaverFeedback.getDogfoodStatus().active;
|
|
557
|
+
setDogfoodActive(directDogfood);
|
|
427
558
|
setVisible(true);
|
|
428
559
|
setError(null);
|
|
429
560
|
setToast(null);
|
|
430
561
|
setProgress(null);
|
|
431
562
|
setAction('idle');
|
|
432
|
-
setShowVibeInput(
|
|
563
|
+
setShowVibeInput(directDogfood);
|
|
433
564
|
setVibePrompt('');
|
|
565
|
+
if (onboarding) {
|
|
566
|
+
setActiveTab('settings');
|
|
567
|
+
void loadDogfoodOnboarding();
|
|
568
|
+
}
|
|
434
569
|
// Re-read the "user hid the quick icon" flag on every open so
|
|
435
570
|
// the re-enable row reflects the latest preference (the user
|
|
436
571
|
// might have hidden or shown it between opens).
|
|
@@ -448,6 +583,14 @@ export const FeedbackModal: React.FC = () => {
|
|
|
448
583
|
void loadRunnerStatuses();
|
|
449
584
|
}
|
|
450
585
|
});
|
|
586
|
+
const dogfoodSub = DeviceEventEmitter.addListener(
|
|
587
|
+
'yaverFeedback:dogfoodChanged',
|
|
588
|
+
(payload: { active?: boolean; exited?: boolean }) => {
|
|
589
|
+
if (!mountedRef.current) return;
|
|
590
|
+
setDogfoodActive(payload?.active === true);
|
|
591
|
+
if (payload?.exited === true) setVisible(false);
|
|
592
|
+
},
|
|
593
|
+
);
|
|
451
594
|
// Agent streams build / compile progress through the BlackBox
|
|
452
595
|
// SSE command channel as `command: "status"`; YaverFeedback re-emits
|
|
453
596
|
// it as `yaverFeedback:status`. Show the most recent message in the
|
|
@@ -472,9 +615,10 @@ export const FeedbackModal: React.FC = () => {
|
|
|
472
615
|
return () => {
|
|
473
616
|
mountedRef.current = false;
|
|
474
617
|
sub.remove();
|
|
618
|
+
dogfoodSub.remove();
|
|
475
619
|
statusSub.remove();
|
|
476
620
|
};
|
|
477
|
-
}, [loadRunnerStatuses, loadSelectedMachine]);
|
|
621
|
+
}, [loadDogfoodOnboarding, loadRunnerStatuses, loadSelectedMachine]);
|
|
478
622
|
|
|
479
623
|
useEffect(() => {
|
|
480
624
|
if (!visible) return;
|
|
@@ -511,6 +655,12 @@ export const FeedbackModal: React.FC = () => {
|
|
|
511
655
|
};
|
|
512
656
|
}, [visible]);
|
|
513
657
|
|
|
658
|
+
useEffect(() => {
|
|
659
|
+
if (!visible || !showVibeInput) return;
|
|
660
|
+
const timer = setTimeout(() => scrollRef.current?.scrollToEnd({ animated: true }), keyboardInset > 0 ? 120 : 40);
|
|
661
|
+
return () => clearTimeout(timer);
|
|
662
|
+
}, [keyboardInset, showVibeInput, visible]);
|
|
663
|
+
|
|
514
664
|
const closeSoon = useCallback((delayMs = 1200) => {
|
|
515
665
|
setTimeout(() => {
|
|
516
666
|
if (mountedRef.current) setVisible(false);
|
|
@@ -526,6 +676,9 @@ export const FeedbackModal: React.FC = () => {
|
|
|
526
676
|
setShowVibeInput(false);
|
|
527
677
|
setVibePrompt('');
|
|
528
678
|
setRunnerStatusError(null);
|
|
679
|
+
void dogfoodControllerRef.current?.stop().catch(() => {});
|
|
680
|
+
dogfoodControllerRef.current = null;
|
|
681
|
+
YaverFeedback.clearDogfoodOnboarding();
|
|
529
682
|
}, []);
|
|
530
683
|
|
|
531
684
|
// Helper: run a P2P call; on network failure, ask YaverFeedback to
|
|
@@ -629,8 +782,10 @@ export const FeedbackModal: React.FC = () => {
|
|
|
629
782
|
}
|
|
630
783
|
|
|
631
784
|
let ackMessage = `${reloadAction.label} requested.`;
|
|
632
|
-
await runWithReconnect(async (
|
|
633
|
-
const
|
|
785
|
+
await runWithReconnect(async () => {
|
|
786
|
+
const renderClient = YaverFeedback.getRenderP2PClient();
|
|
787
|
+
if (!renderClient) throw new Error('Render machine is not connected yet.');
|
|
788
|
+
const ack = await renderClient.reloadWithMode(reloadAction.mode, devSnapshot);
|
|
634
789
|
ackMessage = ack.message;
|
|
635
790
|
setToast(ack.message);
|
|
636
791
|
setProgress(0.2);
|
|
@@ -685,8 +840,10 @@ export const FeedbackModal: React.FC = () => {
|
|
|
685
840
|
// map this app to its MobileProject scan entry without needing
|
|
686
841
|
// `yaver dev start` to have been run.
|
|
687
842
|
let ackMessage = 'Reload request acknowledged.';
|
|
688
|
-
await runWithReconnect(async (
|
|
689
|
-
const
|
|
843
|
+
await runWithReconnect(async () => {
|
|
844
|
+
const renderClient = YaverFeedback.getRenderP2PClient();
|
|
845
|
+
if (!renderClient) throw new Error('Render machine is not connected yet.');
|
|
846
|
+
const ack = await renderClient.reloadApp('bundle');
|
|
690
847
|
ackMessage = ack.message;
|
|
691
848
|
setToast(ack.message);
|
|
692
849
|
setProgress(0.2);
|
|
@@ -875,6 +1032,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
875
1032
|
taskId: string;
|
|
876
1033
|
initialPrompt: string;
|
|
877
1034
|
project?: string;
|
|
1035
|
+
projectPath?: string;
|
|
878
1036
|
runner?: string;
|
|
879
1037
|
model?: string;
|
|
880
1038
|
} | null>(null);
|
|
@@ -943,6 +1101,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
943
1101
|
taskId: result.taskId,
|
|
944
1102
|
initialPrompt: promptText,
|
|
945
1103
|
project: identity.projectName,
|
|
1104
|
+
projectPath: identity.projectPath,
|
|
946
1105
|
runner: preferredRunner ?? undefined,
|
|
947
1106
|
model: preferredModel ?? undefined,
|
|
948
1107
|
});
|
|
@@ -962,6 +1121,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
962
1121
|
*/
|
|
963
1122
|
|
|
964
1123
|
const busy = action !== 'idle';
|
|
1124
|
+
const machineRouting = YaverFeedback.getMachineRouting();
|
|
965
1125
|
const readyRunnerCount = runnerCards.filter((row) => row.ready || row.authConfigured).length;
|
|
966
1126
|
const missingRunnerCount = runnerCards.filter((row) => !row.installed).length;
|
|
967
1127
|
const needsAuthRunnerCount = runnerCards.filter(
|
|
@@ -972,17 +1132,18 @@ export const FeedbackModal: React.FC = () => {
|
|
|
972
1132
|
// for the live chat screen. The chat manages its own SSE
|
|
973
1133
|
// subscription, multi-turn follow-ups, and Reload button. Closing
|
|
974
1134
|
// the chat returns to idle and clears the active vibe.
|
|
975
|
-
if (
|
|
1135
|
+
if (activeVibe) {
|
|
976
1136
|
const client = YaverFeedback.getP2PClient();
|
|
977
1137
|
return (
|
|
978
1138
|
<>
|
|
979
1139
|
<AuthOverlay />
|
|
980
1140
|
<QuickActionIcon />
|
|
1141
|
+
<YaverModeBadgeGate />
|
|
981
1142
|
<Modal
|
|
982
1143
|
visible={visible}
|
|
983
1144
|
animationType="slide"
|
|
984
1145
|
transparent
|
|
985
|
-
onRequestClose={
|
|
1146
|
+
onRequestClose={handleClose}
|
|
986
1147
|
>
|
|
987
1148
|
{client ? (
|
|
988
1149
|
<VibeChatScreen
|
|
@@ -990,11 +1151,20 @@ export const FeedbackModal: React.FC = () => {
|
|
|
990
1151
|
initialTaskId={activeVibe.taskId}
|
|
991
1152
|
initialUserPrompt={activeVibe.initialPrompt}
|
|
992
1153
|
project={activeVibe.project}
|
|
1154
|
+
projectPath={activeVibe.projectPath}
|
|
993
1155
|
runner={activeVibe.runner}
|
|
994
1156
|
model={activeVibe.model}
|
|
1157
|
+
voiceInputEnabled={YaverFeedback.getConfig()?.voiceInputEnabled === true}
|
|
995
1158
|
onClose={() => setActiveVibe(null)}
|
|
1159
|
+
onNewTopic={() => {
|
|
1160
|
+
setActiveVibe(null);
|
|
1161
|
+
setShowVibeInput(true);
|
|
1162
|
+
}}
|
|
1163
|
+
onMinimize={handleClose}
|
|
1164
|
+
codingMachine={YaverFeedback.getMachineRouting().codingDeviceId}
|
|
1165
|
+
renderMachine={YaverFeedback.getMachineRouting().renderDeviceId}
|
|
996
1166
|
onReload={async () => {
|
|
997
|
-
const c = YaverFeedback.
|
|
1167
|
+
const c = YaverFeedback.getRenderP2PClient();
|
|
998
1168
|
if (!c) throw new Error('Not connected');
|
|
999
1169
|
await c.reloadApp();
|
|
1000
1170
|
}}
|
|
@@ -1009,6 +1179,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1009
1179
|
<>
|
|
1010
1180
|
<AuthOverlay />
|
|
1011
1181
|
<QuickActionIcon />
|
|
1182
|
+
<YaverModeBadgeGate />
|
|
1012
1183
|
{visible && (
|
|
1013
1184
|
<Modal
|
|
1014
1185
|
visible={visible}
|
|
@@ -1045,6 +1216,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1045
1216
|
}}
|
|
1046
1217
|
>
|
|
1047
1218
|
<ScrollView
|
|
1219
|
+
ref={scrollRef}
|
|
1048
1220
|
style={styles.scroll}
|
|
1049
1221
|
contentContainerStyle={[
|
|
1050
1222
|
styles.scrollContent,
|
|
@@ -1054,9 +1226,13 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1054
1226
|
]}
|
|
1055
1227
|
keyboardShouldPersistTaps="handled"
|
|
1056
1228
|
keyboardDismissMode={Platform.OS === 'ios' ? 'interactive' : 'on-drag'}
|
|
1229
|
+
contentInsetAdjustmentBehavior="always"
|
|
1230
|
+
automaticallyAdjustKeyboardInsets={Platform.OS === 'ios'}
|
|
1057
1231
|
>
|
|
1058
1232
|
<View style={styles.header}>
|
|
1059
|
-
<Text style={styles.title}>
|
|
1233
|
+
<Text style={styles.title}>
|
|
1234
|
+
{dogfoodActive ? `${YaverFeedback.getDogfoodStatus().label || 'App'} Developer Mode` : 'Send Feedback'}
|
|
1235
|
+
</Text>
|
|
1060
1236
|
<Pressable
|
|
1061
1237
|
onPress={handleClose}
|
|
1062
1238
|
hitSlop={12}
|
|
@@ -1068,6 +1244,114 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1068
1244
|
</Pressable>
|
|
1069
1245
|
</View>
|
|
1070
1246
|
|
|
1247
|
+
<View style={styles.tabs} accessibilityRole="tablist">
|
|
1248
|
+
{(['chat', 'settings'] as const).map((tab) => (
|
|
1249
|
+
<Pressable
|
|
1250
|
+
key={tab}
|
|
1251
|
+
onPress={() => setActiveTab(tab)}
|
|
1252
|
+
style={[styles.tab, activeTab === tab && styles.tabSelected]}
|
|
1253
|
+
accessibilityRole="tab"
|
|
1254
|
+
accessibilityState={{ selected: activeTab === tab }}
|
|
1255
|
+
>
|
|
1256
|
+
<Text style={[styles.tabText, activeTab === tab && styles.tabTextSelected]}>{tab === 'chat' ? 'Chat' : 'Settings'}</Text>
|
|
1257
|
+
</Pressable>
|
|
1258
|
+
))}
|
|
1259
|
+
</View>
|
|
1260
|
+
|
|
1261
|
+
<View style={[styles.tabContent, activeTab !== 'settings' && styles.hidden]}>
|
|
1262
|
+
<>
|
|
1263
|
+
{YaverFeedback.getDogfoodOnboarding() ? (
|
|
1264
|
+
<View style={styles.dogfoodWizard}>
|
|
1265
|
+
<Text style={styles.dogfoodWizardTitle}>Dogfood this app</Text>
|
|
1266
|
+
<Text style={styles.dogfoodWizardHint}>
|
|
1267
|
+
OAuth ✓ · machine {machineCard.device ? '✓' : 'required'} · installation {dogfoodEnrollment?.status || 'checking'}
|
|
1268
|
+
</Text>
|
|
1269
|
+
{dogfoodEnrollment?.installationId ? (
|
|
1270
|
+
<Text selectable style={styles.dogfoodInstallationId}>
|
|
1271
|
+
This device · {dogfoodEnrollment.installationId}
|
|
1272
|
+
</Text>
|
|
1273
|
+
) : null}
|
|
1274
|
+
{dogfoodEnrollment?.status !== 'active' ? (
|
|
1275
|
+
<View style={styles.dogfoodPendingBox}>
|
|
1276
|
+
<Text style={styles.dogfoodPendingText}>
|
|
1277
|
+
{dogfoodEnrollment?.error
|
|
1278
|
+
|| 'Approve this installation from Yaver → Settings → Third-party app testing. A UUID alone never grants access.'}
|
|
1279
|
+
</Text>
|
|
1280
|
+
<Pressable
|
|
1281
|
+
onPress={() => void loadDogfoodOnboarding()}
|
|
1282
|
+
style={({ pressed }) => [styles.runnerRefreshBtn, pressed && styles.buttonPressed]}
|
|
1283
|
+
>
|
|
1284
|
+
<Text style={styles.runnerRefreshBtnText}>{dogfoodSetupLoading ? 'Checking…' : 'Check approval'}</Text>
|
|
1285
|
+
</Pressable>
|
|
1286
|
+
</View>
|
|
1287
|
+
) : (
|
|
1288
|
+
<>
|
|
1289
|
+
<Text style={styles.dogfoodStepLabel}>Project on selected machine</Text>
|
|
1290
|
+
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.dogfoodChoiceRow}>
|
|
1291
|
+
{dogfoodProjects.map((project) => (
|
|
1292
|
+
<Pressable
|
|
1293
|
+
key={project.path}
|
|
1294
|
+
onPress={() => {
|
|
1295
|
+
setDogfoodProject(project);
|
|
1296
|
+
setDogfoodLane(defaultDogfoodLane(project.framework || YaverFeedback.getDogfoodOnboarding()?.framework || 'expo'));
|
|
1297
|
+
setDogfoodRuntime(null);
|
|
1298
|
+
}}
|
|
1299
|
+
style={[styles.dogfoodChoice, dogfoodProject?.path === project.path && styles.dogfoodChoiceSelected]}
|
|
1300
|
+
>
|
|
1301
|
+
<Text style={[styles.dogfoodChoiceText, dogfoodProject?.path === project.path && styles.dogfoodChoiceTextSelected]}>{project.name}</Text>
|
|
1302
|
+
</Pressable>
|
|
1303
|
+
))}
|
|
1304
|
+
</ScrollView>
|
|
1305
|
+
<Text style={styles.dogfoodStepLabel}>Runtime lane</Text>
|
|
1306
|
+
<View style={styles.dogfoodChoiceRow}>
|
|
1307
|
+
{dogfoodLaneOptions(
|
|
1308
|
+
dogfoodProject?.framework || YaverFeedback.getDogfoodOnboarding()?.framework || 'expo',
|
|
1309
|
+
{ nativeRuntimeAvailable: dogfoodNativeAvailable },
|
|
1310
|
+
).map((option) => (
|
|
1311
|
+
<Pressable
|
|
1312
|
+
key={option.lane}
|
|
1313
|
+
onPress={() => option.supported && setDogfoodLane(option.lane)}
|
|
1314
|
+
style={[
|
|
1315
|
+
styles.dogfoodChoice,
|
|
1316
|
+
dogfoodLane === option.lane && styles.dogfoodChoiceSelected,
|
|
1317
|
+
!option.supported && styles.actionBtnDisabled,
|
|
1318
|
+
]}
|
|
1319
|
+
accessibilityState={{ disabled: !option.supported, selected: dogfoodLane === option.lane }}
|
|
1320
|
+
>
|
|
1321
|
+
<Text style={[styles.dogfoodChoiceText, dogfoodLane === option.lane && styles.dogfoodChoiceTextSelected]}>{option.label}</Text>
|
|
1322
|
+
</Pressable>
|
|
1323
|
+
))}
|
|
1324
|
+
</View>
|
|
1325
|
+
<Text style={styles.dogfoodWizardHint}>
|
|
1326
|
+
Runner: {[preferredRunner || 'automatic', preferredModel].filter(Boolean).join(' · ')}. Change it under Coding Agents below.
|
|
1327
|
+
</Text>
|
|
1328
|
+
<ActionRow
|
|
1329
|
+
label={dogfoodRuntime && !['idle', 'ready', 'failed', 'stopped'].includes(dogfoodRuntime.phase) ? dogfoodRuntime.message : 'Start Dogfood'}
|
|
1330
|
+
tint="#818cf8"
|
|
1331
|
+
onPress={() => void startDogfoodRuntime()}
|
|
1332
|
+
disabled={!dogfoodProject || !!(dogfoodRuntime && !['idle', 'ready', 'failed', 'stopped'].includes(dogfoodRuntime.phase))}
|
|
1333
|
+
busy={!!dogfoodRuntime && !['idle', 'ready', 'failed', 'stopped'].includes(dogfoodRuntime.phase)}
|
|
1334
|
+
/>
|
|
1335
|
+
{dogfoodRuntime ? (
|
|
1336
|
+
<View style={styles.dogfoodConsole}>
|
|
1337
|
+
<Text style={styles.dogfoodConsoleStatus}>{dogfoodRuntime.message}</Text>
|
|
1338
|
+
{dogfoodRuntime.logs.slice(-80).map((line, index) => (
|
|
1339
|
+
<Text key={`${line.at}-${index}`} selectable style={styles.dogfoodConsoleLine}>{line.text}</Text>
|
|
1340
|
+
))}
|
|
1341
|
+
{dogfoodRuntime.failure ? (
|
|
1342
|
+
<Text style={styles.dogfoodConsoleError}>{dogfoodRuntime.failure.error}{'\n'}{dogfoodRuntime.failure.remedy}</Text>
|
|
1343
|
+
) : null}
|
|
1344
|
+
{dogfoodRuntime.result?.url ? (
|
|
1345
|
+
<Pressable onPress={() => void Linking.openURL(dogfoodRuntime.result!.url!)} style={styles.dogfoodOpenPreview}>
|
|
1346
|
+
<Text style={styles.dogfoodOpenPreviewText}>Open dogfooded app</Text>
|
|
1347
|
+
</Pressable>
|
|
1348
|
+
) : null}
|
|
1349
|
+
</View>
|
|
1350
|
+
) : null}
|
|
1351
|
+
</>
|
|
1352
|
+
)}
|
|
1353
|
+
</View>
|
|
1354
|
+
) : null}
|
|
1071
1355
|
<Pressable
|
|
1072
1356
|
onPress={() => {
|
|
1073
1357
|
if (!YaverFeedback.isAuthed()) {
|
|
@@ -1093,7 +1377,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1093
1377
|
machineCard.status === 'offline' && styles.machineDotOffline,
|
|
1094
1378
|
]}
|
|
1095
1379
|
/>
|
|
1096
|
-
<Text style={styles.machineLabel}>
|
|
1380
|
+
<Text style={styles.machineLabel}>Coding Machine</Text>
|
|
1097
1381
|
</View>
|
|
1098
1382
|
<Text style={styles.machineAction}>
|
|
1099
1383
|
{machineCard.loading ? 'Refreshing…' : 'Change'}
|
|
@@ -1105,6 +1389,17 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1105
1389
|
<Text style={styles.machineMeta}>{machineCard.detail}</Text>
|
|
1106
1390
|
</Pressable>
|
|
1107
1391
|
|
|
1392
|
+
<View style={styles.machineRoutes}>
|
|
1393
|
+
<View style={styles.machineRouteCard}>
|
|
1394
|
+
<Text style={styles.machineRouteLabel}>Coding machine</Text>
|
|
1395
|
+
<Text style={styles.machineRouteValue} numberOfLines={1}>{machineRouting.codingDeviceId || machineCard.title}</Text>
|
|
1396
|
+
</View>
|
|
1397
|
+
<View style={styles.machineRouteCard}>
|
|
1398
|
+
<Text style={styles.machineRouteLabel}>Render machine</Text>
|
|
1399
|
+
<Text style={styles.machineRouteValue} numberOfLines={1}>{machineRouting.renderDeviceId || machineRouting.codingDeviceId || machineCard.title}</Text>
|
|
1400
|
+
</View>
|
|
1401
|
+
</View>
|
|
1402
|
+
|
|
1108
1403
|
<View style={styles.runnerSection}>
|
|
1109
1404
|
<View style={styles.runnerSectionHeader}>
|
|
1110
1405
|
<View style={{ flex: 1 }}>
|
|
@@ -1130,6 +1425,13 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1130
1425
|
</Pressable>
|
|
1131
1426
|
</View>
|
|
1132
1427
|
|
|
1428
|
+
<View style={styles.routingSummary}>
|
|
1429
|
+
<Text style={styles.routingSummaryLabel}>Vibing uses</Text>
|
|
1430
|
+
<Text style={styles.routingSummaryValue} numberOfLines={1}>
|
|
1431
|
+
{[preferredRunner || 'automatic runner', preferredModel].filter(Boolean).join(' · ')}
|
|
1432
|
+
</Text>
|
|
1433
|
+
</View>
|
|
1434
|
+
|
|
1133
1435
|
{runnerCards.map((row) => (
|
|
1134
1436
|
<View
|
|
1135
1437
|
key={row.id}
|
|
@@ -1154,20 +1456,53 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1154
1456
|
{row.statusLine}
|
|
1155
1457
|
</Text>
|
|
1156
1458
|
</View>
|
|
1157
|
-
{
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1459
|
+
<View style={styles.runnerCardActions}>
|
|
1460
|
+
{row.ready ? (
|
|
1461
|
+
<Pressable
|
|
1462
|
+
onPress={() => {
|
|
1463
|
+
const nextModel = row.models?.find((model) => model.isDefault)?.id || row.models?.[0]?.id || '';
|
|
1464
|
+
setPreferredRunnerState(row.id);
|
|
1465
|
+
setPreferredModelState(nextModel);
|
|
1466
|
+
void setPreferredRunner(row.id);
|
|
1467
|
+
void setPreferredModel(nextModel || null);
|
|
1468
|
+
}}
|
|
1469
|
+
style={({ pressed }) => [styles.runnerActionBtn, preferredRunner === row.id && styles.runnerActionBtnSelected, pressed && styles.buttonPressed]}
|
|
1470
|
+
accessibilityRole="button"
|
|
1471
|
+
accessibilityLabel={`Use ${row.name} for Vibing`}
|
|
1472
|
+
>
|
|
1473
|
+
<Text style={styles.runnerActionBtnText}>{preferredRunner === row.id ? 'Using' : 'Use'}</Text>
|
|
1474
|
+
</Pressable>
|
|
1475
|
+
) : null}
|
|
1476
|
+
{row.actionRunner ? (
|
|
1477
|
+
<Pressable
|
|
1478
|
+
onPress={() => setRunnerAuthModal(row.actionRunner ?? null)}
|
|
1479
|
+
style={({ pressed }) => [styles.runnerActionBtn, pressed && styles.buttonPressed]}
|
|
1480
|
+
accessibilityRole="button"
|
|
1481
|
+
accessibilityLabel={`${row.actionLabel} ${row.name}`}
|
|
1482
|
+
>
|
|
1483
|
+
<Text style={styles.runnerActionBtnText}>{row.actionLabel}</Text>
|
|
1484
|
+
</Pressable>
|
|
1485
|
+
) : null}
|
|
1486
|
+
</View>
|
|
1170
1487
|
</View>
|
|
1488
|
+
{preferredRunner === row.id && (row.models?.length || 0) > 0 ? (
|
|
1489
|
+
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.modelChoiceRow}>
|
|
1490
|
+
{row.models!.map((model) => (
|
|
1491
|
+
<Pressable
|
|
1492
|
+
key={model.id}
|
|
1493
|
+
onPress={() => {
|
|
1494
|
+
setPreferredModelState(model.id);
|
|
1495
|
+
void setPreferredModel(model.id);
|
|
1496
|
+
}}
|
|
1497
|
+
style={[styles.modelChoice, preferredModel === model.id && styles.modelChoiceSelected]}
|
|
1498
|
+
accessibilityRole="button"
|
|
1499
|
+
accessibilityLabel={`Use ${model.name || model.id} model`}
|
|
1500
|
+
>
|
|
1501
|
+
<Text style={[styles.modelChoiceText, preferredModel === model.id && styles.modelChoiceTextSelected]}>{model.name || model.id}</Text>
|
|
1502
|
+
</Pressable>
|
|
1503
|
+
))}
|
|
1504
|
+
</ScrollView>
|
|
1505
|
+
) : null}
|
|
1171
1506
|
{row.detail ? (
|
|
1172
1507
|
<Text style={styles.runnerCardDetail}>{row.detail}</Text>
|
|
1173
1508
|
) : null}
|
|
@@ -1278,7 +1613,10 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1278
1613
|
</Text>
|
|
1279
1614
|
</View>
|
|
1280
1615
|
))}
|
|
1616
|
+
</>
|
|
1617
|
+
</View>
|
|
1281
1618
|
|
|
1619
|
+
<View style={[styles.tabContent, activeTab !== 'chat' && styles.hidden]}>
|
|
1282
1620
|
{/* 3. Vibing — expands to an input box on first tap
|
|
1283
1621
|
so the user says WHAT they want to vibe on, just
|
|
1284
1622
|
like the Yaver mobile app's Vibing tab. Second
|
|
@@ -1339,13 +1677,13 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1339
1677
|
)}
|
|
1340
1678
|
|
|
1341
1679
|
{/* Screenshot & Fix */}
|
|
1342
|
-
<ActionRow
|
|
1680
|
+
{!dogfoodActive ? <ActionRow
|
|
1343
1681
|
label={action === 'capturing' ? 'Working…' : 'Screenshot & Fix'}
|
|
1344
1682
|
tint="#22c55e"
|
|
1345
1683
|
onPress={handleScreenshotAndFix}
|
|
1346
1684
|
disabled={busy}
|
|
1347
1685
|
busy={action === 'capturing'}
|
|
1348
|
-
/>
|
|
1686
|
+
/> : null}
|
|
1349
1687
|
|
|
1350
1688
|
{/* Deploy — opens an inline panel that talks to
|
|
1351
1689
|
/fleet/deploy-options on the agent and lets the user
|
|
@@ -1353,7 +1691,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1353
1691
|
it on. Capabilities (e.g. "Linux can't TestFlight")
|
|
1354
1692
|
come from the agent's doctor probes — no client-side
|
|
1355
1693
|
platform smarts here. */}
|
|
1356
|
-
{!showDeploy ? (
|
|
1694
|
+
{!dogfoodActive && (!showDeploy ? (
|
|
1357
1695
|
<ActionRow
|
|
1358
1696
|
label="Deploy"
|
|
1359
1697
|
tint="#7f8cf7"
|
|
@@ -1362,7 +1700,8 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1362
1700
|
/>
|
|
1363
1701
|
) : (
|
|
1364
1702
|
<DeployPanel onClose={() => setShowDeploy(false)} />
|
|
1365
|
-
)}
|
|
1703
|
+
))}
|
|
1704
|
+
</View>
|
|
1366
1705
|
|
|
1367
1706
|
{progress !== null && (
|
|
1368
1707
|
<View style={styles.progressTrack}>
|
|
@@ -1446,6 +1785,24 @@ const ActionRow: React.FC<ActionRowProps> = ({
|
|
|
1446
1785
|
);
|
|
1447
1786
|
|
|
1448
1787
|
const styles = StyleSheet.create({
|
|
1788
|
+
dogfoodWizard: { gap: 10, borderRadius: 14, borderWidth: 1, borderColor: 'rgba(129,140,248,0.38)', backgroundColor: 'rgba(129,140,248,0.08)', padding: 13 },
|
|
1789
|
+
dogfoodWizardTitle: { color: '#222229', fontSize: 17, fontWeight: '800' },
|
|
1790
|
+
dogfoodWizardHint: { color: '#6f6f7b', fontSize: 12, lineHeight: 17 },
|
|
1791
|
+
dogfoodInstallationId: { color: '#6555df', fontSize: 11, fontFamily: Platform.select({ ios: 'Menlo', android: 'monospace', default: 'monospace' }) },
|
|
1792
|
+
dogfoodPendingBox: { gap: 9, borderRadius: 11, padding: 10, backgroundColor: 'rgba(245,158,11,0.10)' },
|
|
1793
|
+
dogfoodPendingText: { color: '#8a5a10', fontSize: 12, lineHeight: 17 },
|
|
1794
|
+
dogfoodStepLabel: { color: '#555561', fontSize: 11, fontWeight: '800', textTransform: 'uppercase', letterSpacing: 0.7 },
|
|
1795
|
+
dogfoodChoiceRow: { flexDirection: 'row', gap: 7 },
|
|
1796
|
+
dogfoodChoice: { borderRadius: 9, borderWidth: 1, borderColor: '#d8d8e3', backgroundColor: '#fff', paddingHorizontal: 10, paddingVertical: 8 },
|
|
1797
|
+
dogfoodChoiceSelected: { borderColor: '#818cf8', backgroundColor: 'rgba(129,140,248,0.18)' },
|
|
1798
|
+
dogfoodChoiceText: { color: '#666671', fontSize: 12, fontWeight: '700' },
|
|
1799
|
+
dogfoodChoiceTextSelected: { color: '#5645d8' },
|
|
1800
|
+
dogfoodConsole: { maxHeight: 260, gap: 4, borderRadius: 11, padding: 10, backgroundColor: '#15151b' },
|
|
1801
|
+
dogfoodConsoleStatus: { color: '#a5b4fc', fontSize: 12, fontWeight: '800' },
|
|
1802
|
+
dogfoodConsoleLine: { color: '#d1d5db', fontSize: 10, lineHeight: 14, fontFamily: Platform.select({ ios: 'Menlo', android: 'monospace', default: 'monospace' }) },
|
|
1803
|
+
dogfoodConsoleError: { color: '#fca5a5', fontSize: 11, lineHeight: 16, marginTop: 5 },
|
|
1804
|
+
dogfoodOpenPreview: { alignSelf: 'flex-start', borderRadius: 9, paddingHorizontal: 11, paddingVertical: 8, marginTop: 6, backgroundColor: '#6555df' },
|
|
1805
|
+
dogfoodOpenPreviewText: { color: '#fff', fontSize: 12, fontWeight: '800' },
|
|
1449
1806
|
reloadRow: {
|
|
1450
1807
|
gap: 4,
|
|
1451
1808
|
},
|
|
@@ -1464,7 +1821,7 @@ const styles = StyleSheet.create({
|
|
|
1464
1821
|
gap: 10,
|
|
1465
1822
|
},
|
|
1466
1823
|
vibeInput: {
|
|
1467
|
-
color: '#
|
|
1824
|
+
color: '#24242b',
|
|
1468
1825
|
fontSize: 15,
|
|
1469
1826
|
minHeight: 64,
|
|
1470
1827
|
textAlignVertical: 'top',
|
|
@@ -1515,7 +1872,7 @@ const styles = StyleSheet.create({
|
|
|
1515
1872
|
justifyContent: 'flex-end',
|
|
1516
1873
|
},
|
|
1517
1874
|
modal: {
|
|
1518
|
-
backgroundColor: '#
|
|
1875
|
+
backgroundColor: '#f8f8fb',
|
|
1519
1876
|
borderTopLeftRadius: 22,
|
|
1520
1877
|
borderTopRightRadius: 22,
|
|
1521
1878
|
padding: 22,
|
|
@@ -1539,7 +1896,7 @@ const styles = StyleSheet.create({
|
|
|
1539
1896
|
title: {
|
|
1540
1897
|
fontSize: 20,
|
|
1541
1898
|
fontWeight: '700',
|
|
1542
|
-
color: '#
|
|
1899
|
+
color: '#17171d',
|
|
1543
1900
|
},
|
|
1544
1901
|
closeBtn: {
|
|
1545
1902
|
width: 36,
|
|
@@ -1547,10 +1904,10 @@ const styles = StyleSheet.create({
|
|
|
1547
1904
|
borderRadius: 18,
|
|
1548
1905
|
alignItems: 'center',
|
|
1549
1906
|
justifyContent: 'center',
|
|
1550
|
-
backgroundColor: '
|
|
1907
|
+
backgroundColor: '#ededf3',
|
|
1551
1908
|
},
|
|
1552
1909
|
closeIcon: {
|
|
1553
|
-
color: '#
|
|
1910
|
+
color: '#656570',
|
|
1554
1911
|
fontSize: 22,
|
|
1555
1912
|
lineHeight: 24,
|
|
1556
1913
|
fontWeight: '400',
|
|
@@ -1569,12 +1926,19 @@ const styles = StyleSheet.create({
|
|
|
1569
1926
|
fontSize: 15,
|
|
1570
1927
|
fontWeight: '700',
|
|
1571
1928
|
},
|
|
1929
|
+
tabs: { flexDirection: 'row', gap: 6, padding: 3, borderRadius: 12, backgroundColor: '#ededf3' },
|
|
1930
|
+
tab: { flex: 1, minHeight: 36, borderRadius: 9, alignItems: 'center', justifyContent: 'center' },
|
|
1931
|
+
tabSelected: { backgroundColor: '#fff' },
|
|
1932
|
+
tabText: { color: '#858590', fontSize: 12, fontWeight: '700' },
|
|
1933
|
+
tabTextSelected: { color: '#6252e8' },
|
|
1934
|
+
tabContent: { gap: 12 },
|
|
1935
|
+
hidden: { display: 'none' },
|
|
1572
1936
|
machineCard: {
|
|
1573
1937
|
borderRadius: 14,
|
|
1574
1938
|
borderWidth: 1,
|
|
1575
1939
|
padding: 14,
|
|
1576
|
-
backgroundColor: '
|
|
1577
|
-
borderColor: '
|
|
1940
|
+
backgroundColor: '#fff',
|
|
1941
|
+
borderColor: '#e1e1e8',
|
|
1578
1942
|
},
|
|
1579
1943
|
machineCardLive: {
|
|
1580
1944
|
backgroundColor: 'rgba(34,197,94,0.10)',
|
|
@@ -1588,6 +1952,10 @@ const styles = StyleSheet.create({
|
|
|
1588
1952
|
backgroundColor: 'rgba(239,68,68,0.10)',
|
|
1589
1953
|
borderColor: 'rgba(239,68,68,0.35)',
|
|
1590
1954
|
},
|
|
1955
|
+
machineRoutes: { flexDirection: 'row', gap: 8 },
|
|
1956
|
+
machineRouteCard: { flex: 1, minWidth: 0, borderRadius: 11, padding: 10, gap: 3, backgroundColor: '#fff', borderWidth: 1, borderColor: '#e1e1e8' },
|
|
1957
|
+
machineRouteLabel: { color: '#7b7b86', fontSize: 10, fontWeight: '700', textTransform: 'uppercase' },
|
|
1958
|
+
machineRouteValue: { color: '#222229', fontSize: 12, fontWeight: '700' },
|
|
1591
1959
|
machineHeader: {
|
|
1592
1960
|
flexDirection: 'row',
|
|
1593
1961
|
alignItems: 'center',
|
|
@@ -1615,7 +1983,7 @@ const styles = StyleSheet.create({
|
|
|
1615
1983
|
backgroundColor: '#ef4444',
|
|
1616
1984
|
},
|
|
1617
1985
|
machineLabel: {
|
|
1618
|
-
color: '#
|
|
1986
|
+
color: '#73737e',
|
|
1619
1987
|
fontSize: 12,
|
|
1620
1988
|
fontWeight: '700',
|
|
1621
1989
|
textTransform: 'uppercase',
|
|
@@ -1627,12 +1995,12 @@ const styles = StyleSheet.create({
|
|
|
1627
1995
|
fontWeight: '700',
|
|
1628
1996
|
},
|
|
1629
1997
|
machineName: {
|
|
1630
|
-
color: '#
|
|
1998
|
+
color: '#222229',
|
|
1631
1999
|
fontSize: 16,
|
|
1632
2000
|
fontWeight: '700',
|
|
1633
2001
|
},
|
|
1634
2002
|
machineMeta: {
|
|
1635
|
-
color: '#
|
|
2003
|
+
color: '#73737e',
|
|
1636
2004
|
fontSize: 12,
|
|
1637
2005
|
marginTop: 4,
|
|
1638
2006
|
lineHeight: 17,
|
|
@@ -1647,33 +2015,33 @@ const styles = StyleSheet.create({
|
|
|
1647
2015
|
gap: 10,
|
|
1648
2016
|
},
|
|
1649
2017
|
runnerSectionTitle: {
|
|
1650
|
-
color: '#
|
|
2018
|
+
color: '#222229',
|
|
1651
2019
|
fontSize: 16,
|
|
1652
2020
|
fontWeight: '700',
|
|
1653
2021
|
},
|
|
1654
2022
|
runnerSectionSubtitle: {
|
|
1655
2023
|
marginTop: 2,
|
|
1656
|
-
color: '#
|
|
2024
|
+
color: '#83838e',
|
|
1657
2025
|
fontSize: 12,
|
|
1658
2026
|
},
|
|
1659
2027
|
runnerRefreshBtn: {
|
|
1660
2028
|
borderRadius: 10,
|
|
1661
2029
|
borderWidth: 1,
|
|
1662
|
-
borderColor: '
|
|
1663
|
-
backgroundColor: '
|
|
2030
|
+
borderColor: '#dedee7',
|
|
2031
|
+
backgroundColor: '#fff',
|
|
1664
2032
|
paddingHorizontal: 10,
|
|
1665
2033
|
paddingVertical: 8,
|
|
1666
2034
|
},
|
|
1667
2035
|
runnerRefreshBtnText: {
|
|
1668
|
-
color: '#
|
|
2036
|
+
color: '#5f5f69',
|
|
1669
2037
|
fontSize: 12,
|
|
1670
2038
|
fontWeight: '600',
|
|
1671
2039
|
},
|
|
1672
2040
|
runnerCard: {
|
|
1673
2041
|
borderRadius: 12,
|
|
1674
2042
|
borderWidth: 1,
|
|
1675
|
-
borderColor: '
|
|
1676
|
-
backgroundColor: '
|
|
2043
|
+
borderColor: '#e2e2e9',
|
|
2044
|
+
backgroundColor: '#fff',
|
|
1677
2045
|
paddingHorizontal: 12,
|
|
1678
2046
|
paddingVertical: 11,
|
|
1679
2047
|
gap: 6,
|
|
@@ -1695,15 +2063,16 @@ const styles = StyleSheet.create({
|
|
|
1695
2063
|
alignItems: 'center',
|
|
1696
2064
|
gap: 10,
|
|
1697
2065
|
},
|
|
2066
|
+
runnerCardActions: { flexDirection: 'row', alignItems: 'center', gap: 6 },
|
|
1698
2067
|
runnerCardTitle: {
|
|
1699
|
-
color: '#
|
|
2068
|
+
color: '#222229',
|
|
1700
2069
|
fontSize: 14,
|
|
1701
2070
|
fontWeight: '700',
|
|
1702
2071
|
},
|
|
1703
2072
|
runnerCardStatus: {
|
|
1704
2073
|
marginTop: 2,
|
|
1705
2074
|
fontSize: 12,
|
|
1706
|
-
color: '#
|
|
2075
|
+
color: '#666671',
|
|
1707
2076
|
},
|
|
1708
2077
|
runnerCardStatusOk: {
|
|
1709
2078
|
color: '#86efac',
|
|
@@ -1715,7 +2084,7 @@ const styles = StyleSheet.create({
|
|
|
1715
2084
|
color: '#fca5a5',
|
|
1716
2085
|
},
|
|
1717
2086
|
runnerCardDetail: {
|
|
1718
|
-
color: '#
|
|
2087
|
+
color: '#858590',
|
|
1719
2088
|
fontSize: 11,
|
|
1720
2089
|
lineHeight: 16,
|
|
1721
2090
|
},
|
|
@@ -1732,6 +2101,15 @@ const styles = StyleSheet.create({
|
|
|
1732
2101
|
fontSize: 12,
|
|
1733
2102
|
fontWeight: '700',
|
|
1734
2103
|
},
|
|
2104
|
+
runnerActionBtnSelected: { borderColor: '#818cf8', backgroundColor: 'rgba(79,70,229,0.44)' },
|
|
2105
|
+
routingSummary: { flexDirection: 'row', alignItems: 'center', gap: 8, paddingHorizontal: 2 },
|
|
2106
|
+
routingSummaryLabel: { color: '#7c7c87', fontSize: 11, fontWeight: '600' },
|
|
2107
|
+
routingSummaryValue: { flex: 1, color: '#6555df', fontSize: 12, fontWeight: '700', textAlign: 'right' },
|
|
2108
|
+
modelChoiceRow: { gap: 6, paddingTop: 2 },
|
|
2109
|
+
modelChoice: { borderRadius: 9, borderWidth: 1, borderColor: 'rgba(148,163,184,0.18)', paddingHorizontal: 9, paddingVertical: 6 },
|
|
2110
|
+
modelChoiceSelected: { borderColor: '#818cf8', backgroundColor: 'rgba(79,70,229,0.30)' },
|
|
2111
|
+
modelChoiceText: { color: '#73737e', fontSize: 11 },
|
|
2112
|
+
modelChoiceTextSelected: { color: '#5e4ce6', fontWeight: '700' },
|
|
1735
2113
|
runnerSectionError: {
|
|
1736
2114
|
color: '#fca5a5',
|
|
1737
2115
|
fontSize: 12,
|
|
@@ -1743,7 +2121,7 @@ const styles = StyleSheet.create({
|
|
|
1743
2121
|
progressTrack: {
|
|
1744
2122
|
height: 6,
|
|
1745
2123
|
borderRadius: 3,
|
|
1746
|
-
backgroundColor: '
|
|
2124
|
+
backgroundColor: '#e5e5eb',
|
|
1747
2125
|
overflow: 'hidden',
|
|
1748
2126
|
marginTop: 4,
|
|
1749
2127
|
},
|
|
@@ -1770,10 +2148,10 @@ const styles = StyleSheet.create({
|
|
|
1770
2148
|
paddingVertical: 6,
|
|
1771
2149
|
paddingHorizontal: 10,
|
|
1772
2150
|
borderRadius: 10,
|
|
1773
|
-
backgroundColor: '
|
|
2151
|
+
backgroundColor: '#ededf3',
|
|
1774
2152
|
},
|
|
1775
2153
|
quickIconToggleText: {
|
|
1776
|
-
color: '#
|
|
2154
|
+
color: '#5f5f69',
|
|
1777
2155
|
fontSize: 12,
|
|
1778
2156
|
fontWeight: '700',
|
|
1779
2157
|
},
|
|
@@ -1785,25 +2163,25 @@ const styles = StyleSheet.create({
|
|
|
1785
2163
|
padding: 12,
|
|
1786
2164
|
},
|
|
1787
2165
|
quickIconNoteText: {
|
|
1788
|
-
color: '#
|
|
2166
|
+
color: '#8a5a12',
|
|
1789
2167
|
fontSize: 12,
|
|
1790
2168
|
lineHeight: 17,
|
|
1791
2169
|
},
|
|
1792
2170
|
iconSelector: {
|
|
1793
2171
|
borderRadius: 12,
|
|
1794
2172
|
borderWidth: 1,
|
|
1795
|
-
borderColor: '
|
|
1796
|
-
backgroundColor: '
|
|
2173
|
+
borderColor: '#e1e1e8',
|
|
2174
|
+
backgroundColor: '#fff',
|
|
1797
2175
|
padding: 12,
|
|
1798
2176
|
gap: 10,
|
|
1799
2177
|
},
|
|
1800
2178
|
iconSelectorTitle: {
|
|
1801
|
-
color: '#
|
|
2179
|
+
color: '#222229',
|
|
1802
2180
|
fontSize: 13,
|
|
1803
2181
|
fontWeight: '700',
|
|
1804
2182
|
},
|
|
1805
2183
|
iconSelectorText: {
|
|
1806
|
-
color: '#
|
|
2184
|
+
color: '#73737e',
|
|
1807
2185
|
fontSize: 12,
|
|
1808
2186
|
lineHeight: 17,
|
|
1809
2187
|
},
|
|
@@ -1817,8 +2195,8 @@ const styles = StyleSheet.create({
|
|
|
1817
2195
|
minWidth: 84,
|
|
1818
2196
|
borderRadius: 12,
|
|
1819
2197
|
borderWidth: 1,
|
|
1820
|
-
borderColor: '
|
|
1821
|
-
backgroundColor: '
|
|
2198
|
+
borderColor: '#e4e4ea',
|
|
2199
|
+
backgroundColor: '#f8f8fb',
|
|
1822
2200
|
paddingVertical: 10,
|
|
1823
2201
|
paddingHorizontal: 8,
|
|
1824
2202
|
alignItems: 'center',
|
|
@@ -1845,7 +2223,7 @@ const styles = StyleSheet.create({
|
|
|
1845
2223
|
fontWeight: '700',
|
|
1846
2224
|
},
|
|
1847
2225
|
iconOptionText: {
|
|
1848
|
-
color: '#
|
|
2226
|
+
color: '#555561',
|
|
1849
2227
|
fontSize: 11,
|
|
1850
2228
|
fontWeight: '600',
|
|
1851
2229
|
},
|
|
@@ -1853,14 +2231,14 @@ const styles = StyleSheet.create({
|
|
|
1853
2231
|
marginTop: 4,
|
|
1854
2232
|
borderRadius: 14,
|
|
1855
2233
|
borderWidth: 1,
|
|
1856
|
-
borderColor: '
|
|
2234
|
+
borderColor: '#e1e1e8',
|
|
1857
2235
|
paddingVertical: 15,
|
|
1858
2236
|
alignItems: 'center',
|
|
1859
2237
|
justifyContent: 'center',
|
|
1860
|
-
backgroundColor: '
|
|
2238
|
+
backgroundColor: '#fff',
|
|
1861
2239
|
},
|
|
1862
2240
|
cancelBtnText: {
|
|
1863
|
-
color: '#
|
|
2241
|
+
color: '#555561',
|
|
1864
2242
|
fontSize: 15,
|
|
1865
2243
|
fontWeight: '700',
|
|
1866
2244
|
},
|