yaver-feedback-react-native 0.9.0 → 0.9.3
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/app.plugin.js +126 -9
- package/dist/AuthOverlay.d.ts +1 -14
- package/dist/AuthOverlay.js +9 -62
- package/dist/BlackBox.js +9 -1
- package/dist/DeployPanel.js +65 -0
- package/dist/Discovery.js +13 -8
- package/dist/DogfoodRuntime.d.ts +124 -0
- package/dist/DogfoodRuntime.js +273 -0
- package/dist/FeedbackModal.js +505 -65
- 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 +178 -2
- package/dist/P2PClient.js +493 -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 +2 -0
- package/dist/VibeChatScreen.d.ts +11 -1
- package/dist/VibeChatScreen.js +200 -41
- package/dist/YaverFeedback.d.ts +132 -0
- package/dist/YaverFeedback.js +633 -65
- package/dist/YaverModeBadge.d.ts +22 -0
- package/dist/YaverModeBadge.js +219 -0
- package/dist/__tests__/AuthDevices.test.js +1 -48
- 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__/DogfoodRuntime.test.d.ts +1 -0
- package/dist/__tests__/DogfoodRuntime.test.js +116 -0
- package/dist/__tests__/P2PClient.test.js +2 -2
- 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 -0
- package/dist/__tests__/ReportIdentity.test.js +180 -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__/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/__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/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 +1 -5
- package/dist/_core/device.js +5 -14
- 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 +17 -4
- package/dist/index.js +33 -7
- 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 +208 -8
- package/package.json +13 -4
- package/src/AuthOverlay.tsx +20 -106
- package/src/BlackBox.ts +10 -1
- package/src/DeployPanel.tsx +74 -0
- package/src/Discovery.ts +13 -8
- package/src/DogfoodRuntime.ts +373 -0
- package/src/FeedbackModal.tsx +619 -79
- package/src/LoginScreen.tsx +2 -22
- package/src/MachinePickerScreen.tsx +6 -21
- package/src/P2PClient.ts +580 -4
- package/src/P2PDogfoodDriver.ts +132 -0
- package/src/PairDeviceModal.tsx +2 -3
- package/src/ShakeDetector.ts +1 -0
- package/src/VibeChatScreen.tsx +232 -42
- package/src/YaverFeedback.ts +631 -68
- package/src/YaverModeBadge.tsx +234 -0
- package/src/__tests__/AuthDevices.test.ts +1 -52
- 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__/DogfoodRuntime.test.ts +135 -0
- package/src/__tests__/P2PClient.test.ts +2 -2
- package/src/__tests__/P2PDogfoodDriver.test.ts +80 -0
- package/src/__tests__/ReportIdentity.test.ts +212 -0
- package/src/__tests__/SDKToken.test.ts +1 -1
- package/src/__tests__/ShakeToggle.test.ts +153 -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/__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/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 +5 -16
- 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 +61 -11
- package/src/reloadActions.ts +273 -0
- package/src/storeShots.ts +189 -0
- package/src/types.ts +209 -8
- 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,
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
// stopVideoRecording,
|
|
26
27
|
} from './capture';
|
|
27
28
|
import { uploadFeedback } from './upload';
|
|
29
|
+
import { resolveReportIdentity } from './P2PClient';
|
|
28
30
|
import {
|
|
29
31
|
DeviceInfo,
|
|
30
32
|
FeedbackBundle,
|
|
@@ -34,9 +36,12 @@ import {
|
|
|
34
36
|
} from './types';
|
|
35
37
|
import { AuthOverlay } from './AuthOverlay';
|
|
36
38
|
import { QuickActionIcon } from './QuickActionIcon';
|
|
39
|
+
import { YaverModeBadge } from './YaverModeBadge';
|
|
37
40
|
import { VibeChatScreen } from './VibeChatScreen';
|
|
38
41
|
import { DeployPanel } from './DeployPanel';
|
|
39
42
|
import { listReachableDevices, RemoteDevice } from './auth';
|
|
43
|
+
import { reloadActions } from './reloadActions';
|
|
44
|
+
import type { DevServerSnapshot, ReloadAction } from './reloadActions';
|
|
40
45
|
import {
|
|
41
46
|
QUICK_ICON_COLOR_PRESETS,
|
|
42
47
|
QuickIconColorPreset,
|
|
@@ -45,6 +50,14 @@ import {
|
|
|
45
50
|
setPreferredModel,
|
|
46
51
|
setPreferredRunner,
|
|
47
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';
|
|
48
61
|
|
|
49
62
|
/**
|
|
50
63
|
* Simplified feedback modal — launch scope is 3 actions:
|
|
@@ -88,6 +101,7 @@ type RunnerCardState = {
|
|
|
88
101
|
detail?: string;
|
|
89
102
|
actionLabel?: string;
|
|
90
103
|
actionRunner?: string;
|
|
104
|
+
models?: Array<{ id: string; name?: string; provider?: string; isDefault?: boolean }>;
|
|
91
105
|
};
|
|
92
106
|
|
|
93
107
|
type ProviderEditorState = {
|
|
@@ -98,6 +112,14 @@ type ProviderEditorState = {
|
|
|
98
112
|
apiKey: string;
|
|
99
113
|
};
|
|
100
114
|
|
|
115
|
+
type DogfoodProjectChoice = {
|
|
116
|
+
name: string;
|
|
117
|
+
path: string;
|
|
118
|
+
framework?: string;
|
|
119
|
+
frameworks?: string[];
|
|
120
|
+
surfaces?: string[];
|
|
121
|
+
};
|
|
122
|
+
|
|
101
123
|
const PRIMARY_RUNNER_IDS = ['claude', 'codex', 'opencode'] as const;
|
|
102
124
|
|
|
103
125
|
function normalizeRunnerStatusRows(rows: RunnerAuthStatusRow[]): RunnerCardState[] {
|
|
@@ -139,6 +161,7 @@ function normalizeRunnerStatusRows(rows: RunnerAuthStatusRow[]): RunnerCardState
|
|
|
139
161
|
version: row.version,
|
|
140
162
|
tone: 'warning',
|
|
141
163
|
statusLine: 'Not installed on the selected machine',
|
|
164
|
+
models: row.models,
|
|
142
165
|
detail,
|
|
143
166
|
};
|
|
144
167
|
}
|
|
@@ -156,6 +179,7 @@ function normalizeRunnerStatusRows(rows: RunnerAuthStatusRow[]): RunnerCardState
|
|
|
156
179
|
statusLine: configured
|
|
157
180
|
? `${versionPrefix}Configured on the selected machine`
|
|
158
181
|
: `${versionPrefix}Needs provider config on the selected machine`,
|
|
182
|
+
models: row.models,
|
|
159
183
|
detail,
|
|
160
184
|
};
|
|
161
185
|
}
|
|
@@ -172,6 +196,7 @@ function normalizeRunnerStatusRows(rows: RunnerAuthStatusRow[]): RunnerCardState
|
|
|
172
196
|
statusLine: authed
|
|
173
197
|
? `${versionPrefix}Signed in on the selected machine`
|
|
174
198
|
: `${versionPrefix}Not signed in on the selected machine`,
|
|
199
|
+
models: row.models,
|
|
175
200
|
detail,
|
|
176
201
|
actionLabel: authed ? 'Re-auth' : 'Sign in',
|
|
177
202
|
actionRunner: id,
|
|
@@ -179,6 +204,36 @@ function normalizeRunnerStatusRows(rows: RunnerAuthStatusRow[]): RunnerCardState
|
|
|
179
204
|
});
|
|
180
205
|
}
|
|
181
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
|
+
|
|
182
237
|
export const FeedbackModal: React.FC = () => {
|
|
183
238
|
const { width: winW, height: winH } = useWindowDimensions();
|
|
184
239
|
const isTablet = Math.min(winW, winH) >= 600;
|
|
@@ -186,6 +241,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
186
241
|
// looks empty on a 1024pt iPad. Mobile keeps 3-col.
|
|
187
242
|
const iconOptionWidthOverride = isTablet ? '18%' : undefined;
|
|
188
243
|
const [visible, setVisible] = useState(false);
|
|
244
|
+
const [dogfoodActive, setDogfoodActive] = useState(() => YaverFeedback.getDogfoodStatus().active);
|
|
189
245
|
const [action, setAction] = useState<ActionState>('idle');
|
|
190
246
|
const [error, setError] = useState<string | null>(null);
|
|
191
247
|
const [toast, setToast] = useState<string | null>(null);
|
|
@@ -195,6 +251,11 @@ export const FeedbackModal: React.FC = () => {
|
|
|
195
251
|
// is our guaranteed UI for bringing the icon back — we surface a
|
|
196
252
|
// small "Show quick icon" row when this is true.
|
|
197
253
|
const [quickIconHidden, setQuickIconHidden] = useState(false);
|
|
254
|
+
// Dev-server snapshot behind the reload buttons. `null` means "we have not
|
|
255
|
+
// been able to ask" — rendered as "not connected", never as "no dev server".
|
|
256
|
+
const [devSnapshot, setDevSnapshot] = useState<DevServerSnapshot | null>(null);
|
|
257
|
+
// Which reload action is in flight, so only that button spins.
|
|
258
|
+
const [reloadingId, setReloadingId] = useState<string | null>(null);
|
|
198
259
|
const [runnerAuthModal, setRunnerAuthModal] = useState<string | null>(null);
|
|
199
260
|
// Vibing-input mode: same expand-on-tap pattern as email login.
|
|
200
261
|
// Tap "Vibing" once → the button reveals an input + Send; that lets
|
|
@@ -223,9 +284,108 @@ export const FeedbackModal: React.FC = () => {
|
|
|
223
284
|
const [runnerStatusError, setRunnerStatusError] = useState<string | null>(null);
|
|
224
285
|
const [preferredRunner, setPreferredRunnerState] = useState<string | null>(null);
|
|
225
286
|
const [preferredModel, setPreferredModelState] = useState('');
|
|
287
|
+
const [activeTab, setActiveTab] = useState<'chat' | 'settings'>('chat');
|
|
226
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);
|
|
227
302
|
const mountedRef = useRef(true);
|
|
228
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
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Ask the machine what its dev server is doing, so the reload actions can
|
|
369
|
+
* be enabled/disabled against reality rather than against a guess.
|
|
370
|
+
*
|
|
371
|
+
* Best-effort and deliberately null-on-failure: null means "we could not
|
|
372
|
+
* ask", which the seam renders as "not connected to a machine yet" — a
|
|
373
|
+
* different sentence from "no dev server is running", because they have
|
|
374
|
+
* different fixes.
|
|
375
|
+
*/
|
|
376
|
+
const refreshDevSnapshot = useCallback(async () => {
|
|
377
|
+
try {
|
|
378
|
+
const client = YaverFeedback.getP2PClient();
|
|
379
|
+
if (!client) {
|
|
380
|
+
setDevSnapshot(null);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
setDevSnapshot(await client.getDevServerStatus());
|
|
384
|
+
} catch {
|
|
385
|
+
setDevSnapshot(null);
|
|
386
|
+
}
|
|
387
|
+
}, []);
|
|
388
|
+
|
|
229
389
|
const loadSelectedMachine = useCallback(async () => {
|
|
230
390
|
const cfg = YaverFeedback.getConfig();
|
|
231
391
|
if (!cfg?.authToken) {
|
|
@@ -261,9 +421,8 @@ export const FeedbackModal: React.FC = () => {
|
|
|
261
421
|
|
|
262
422
|
try {
|
|
263
423
|
const devices = await listReachableDevices(cfg.authToken);
|
|
264
|
-
const all = [...devices.owned, ...devices.shared];
|
|
265
424
|
const device =
|
|
266
|
-
|
|
425
|
+
devices.owned.find((candidate) => candidate.deviceId === cfg.preferredDeviceId) ?? null;
|
|
267
426
|
|
|
268
427
|
if (!device) {
|
|
269
428
|
if (mountedRef.current) {
|
|
@@ -285,9 +444,8 @@ export const FeedbackModal: React.FC = () => {
|
|
|
285
444
|
reachable = await client.health();
|
|
286
445
|
}
|
|
287
446
|
|
|
288
|
-
const hostHint = device.hostEmail ? ` via ${device.hostEmail}` : '';
|
|
289
447
|
let status: MachineCardState['status'] = 'live';
|
|
290
|
-
let detail =
|
|
448
|
+
let detail = device.platform;
|
|
291
449
|
|
|
292
450
|
if (!device.isOnline) {
|
|
293
451
|
status = 'offline';
|
|
@@ -360,7 +518,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
360
518
|
if (!client) {
|
|
361
519
|
throw new Error('Not connected to the selected machine yet.');
|
|
362
520
|
}
|
|
363
|
-
const rows = await client.
|
|
521
|
+
const rows = await client.getAvailableRunners();
|
|
364
522
|
if (mountedRef.current) {
|
|
365
523
|
setRunnerCards(normalizeRunnerStatusRows(rows));
|
|
366
524
|
}
|
|
@@ -394,13 +552,20 @@ export const FeedbackModal: React.FC = () => {
|
|
|
394
552
|
mountedRef.current = true;
|
|
395
553
|
const sub = DeviceEventEmitter.addListener('yaverFeedback:startReport', () => {
|
|
396
554
|
if (YaverFeedback.isEnabled()) {
|
|
555
|
+
const onboarding = YaverFeedback.getDogfoodOnboarding();
|
|
556
|
+
const directDogfood = YaverFeedback.getDogfoodStatus().active;
|
|
557
|
+
setDogfoodActive(directDogfood);
|
|
397
558
|
setVisible(true);
|
|
398
559
|
setError(null);
|
|
399
560
|
setToast(null);
|
|
400
561
|
setProgress(null);
|
|
401
562
|
setAction('idle');
|
|
402
|
-
setShowVibeInput(
|
|
563
|
+
setShowVibeInput(directDogfood);
|
|
403
564
|
setVibePrompt('');
|
|
565
|
+
if (onboarding) {
|
|
566
|
+
setActiveTab('settings');
|
|
567
|
+
void loadDogfoodOnboarding();
|
|
568
|
+
}
|
|
404
569
|
// Re-read the "user hid the quick icon" flag on every open so
|
|
405
570
|
// the re-enable row reflects the latest preference (the user
|
|
406
571
|
// might have hidden or shown it between opens).
|
|
@@ -418,6 +583,14 @@ export const FeedbackModal: React.FC = () => {
|
|
|
418
583
|
void loadRunnerStatuses();
|
|
419
584
|
}
|
|
420
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
|
+
);
|
|
421
594
|
// Agent streams build / compile progress through the BlackBox
|
|
422
595
|
// SSE command channel as `command: "status"`; YaverFeedback re-emits
|
|
423
596
|
// it as `yaverFeedback:status`. Show the most recent message in the
|
|
@@ -442,18 +615,25 @@ export const FeedbackModal: React.FC = () => {
|
|
|
442
615
|
return () => {
|
|
443
616
|
mountedRef.current = false;
|
|
444
617
|
sub.remove();
|
|
618
|
+
dogfoodSub.remove();
|
|
445
619
|
statusSub.remove();
|
|
446
620
|
};
|
|
447
|
-
}, [loadRunnerStatuses, loadSelectedMachine]);
|
|
621
|
+
}, [loadDogfoodOnboarding, loadRunnerStatuses, loadSelectedMachine]);
|
|
448
622
|
|
|
449
623
|
useEffect(() => {
|
|
450
624
|
if (!visible) return;
|
|
625
|
+
// Poll the dev server alongside the machine + runners. A reload button
|
|
626
|
+
// whose enabled state was decided once, when the sheet opened, goes
|
|
627
|
+
// stale the moment the user starts Metro from another surface — and a
|
|
628
|
+
// stale "no dev server is running" reads as the product being broken.
|
|
629
|
+
void refreshDevSnapshot();
|
|
451
630
|
const interval = setInterval(() => {
|
|
452
631
|
void loadSelectedMachine();
|
|
453
632
|
void loadRunnerStatuses();
|
|
633
|
+
void refreshDevSnapshot();
|
|
454
634
|
}, 5000);
|
|
455
635
|
return () => clearInterval(interval);
|
|
456
|
-
}, [loadRunnerStatuses, loadSelectedMachine, visible]);
|
|
636
|
+
}, [loadRunnerStatuses, loadSelectedMachine, refreshDevSnapshot, visible]);
|
|
457
637
|
|
|
458
638
|
useEffect(() => {
|
|
459
639
|
if (!visible) {
|
|
@@ -475,6 +655,12 @@ export const FeedbackModal: React.FC = () => {
|
|
|
475
655
|
};
|
|
476
656
|
}, [visible]);
|
|
477
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
|
+
|
|
478
664
|
const closeSoon = useCallback((delayMs = 1200) => {
|
|
479
665
|
setTimeout(() => {
|
|
480
666
|
if (mountedRef.current) setVisible(false);
|
|
@@ -490,6 +676,9 @@ export const FeedbackModal: React.FC = () => {
|
|
|
490
676
|
setShowVibeInput(false);
|
|
491
677
|
setVibePrompt('');
|
|
492
678
|
setRunnerStatusError(null);
|
|
679
|
+
void dogfoodControllerRef.current?.stop().catch(() => {});
|
|
680
|
+
dogfoodControllerRef.current = null;
|
|
681
|
+
YaverFeedback.clearDogfoodOnboarding();
|
|
493
682
|
}, []);
|
|
494
683
|
|
|
495
684
|
// Helper: run a P2P call; on network failure, ask YaverFeedback to
|
|
@@ -542,7 +731,89 @@ export const FeedbackModal: React.FC = () => {
|
|
|
542
731
|
[],
|
|
543
732
|
);
|
|
544
733
|
|
|
545
|
-
// ─── 1.
|
|
734
|
+
// ─── 1. Reload ─────────────────────────────────────────────────────
|
|
735
|
+
//
|
|
736
|
+
// Three actions now, not one: Hot Reload (mode=fast), Full Reload
|
|
737
|
+
// (mode=full — Flutter's hot RESTART), and Rebuild Bundle
|
|
738
|
+
// (/dev/reload-app, the only one that works with Metro down).
|
|
739
|
+
//
|
|
740
|
+
// WHICH of them render, and which are disabled with what reason, is
|
|
741
|
+
// decided by the pure `reloadActions()` seam — never inline here, so the
|
|
742
|
+
// same policy holds on web, Flutter, Unity, Swift and Kotlin. In
|
|
743
|
+
// particular: a production build (`__DEV__ === false`) gets NONE of them.
|
|
744
|
+
|
|
745
|
+
const availableReloadActions = reloadActions(devSnapshot, {
|
|
746
|
+
// __DEV__ is React Native's own build flag. A release bundle sets it
|
|
747
|
+
// false, so a shipped app renders no reload UI at all — which is the
|
|
748
|
+
// point, and is what reloadActions.test.ts pins.
|
|
749
|
+
isDevBuild: typeof __DEV__ !== 'undefined' && __DEV__ === true,
|
|
750
|
+
connected: devSnapshot !== null,
|
|
751
|
+
machineLabel: machineCard.device?.name || undefined,
|
|
752
|
+
includeRebuild: true,
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
const handleReloadAction = useCallback(
|
|
756
|
+
async (reloadAction: ReloadAction) => {
|
|
757
|
+
if (!reloadAction.enabled) {
|
|
758
|
+
// Pressing a disabled action must SAY why. A row that does nothing
|
|
759
|
+
// is the same defect as a spinner that never resolves.
|
|
760
|
+
setToast(reloadAction.disabledReason || 'Reload is unavailable right now.');
|
|
761
|
+
setError(reloadAction.disabledReason || null);
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
setReloadingId(reloadAction.id);
|
|
765
|
+
setAction('hot-reloading');
|
|
766
|
+
setError(null);
|
|
767
|
+
setProgress(0);
|
|
768
|
+
setToast(`${reloadAction.label}…`);
|
|
769
|
+
try {
|
|
770
|
+
await loadSelectedMachine();
|
|
771
|
+
const selected = await YaverFeedback.getSelectedRemoteDevice();
|
|
772
|
+
if (!selected) {
|
|
773
|
+
YaverFeedback.showMachinePicker();
|
|
774
|
+
throw new Error('No machine selected. Pick a machine and try again.');
|
|
775
|
+
}
|
|
776
|
+
if (selected.needsAuth) {
|
|
777
|
+
YaverFeedback.showMachinePicker();
|
|
778
|
+
throw new Error('Selected machine needs pairing again.');
|
|
779
|
+
}
|
|
780
|
+
if (!selected.isOnline) {
|
|
781
|
+
throw new Error('Selected machine is offline. Start `yaver serve` on it first.');
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
let ackMessage = `${reloadAction.label} requested.`;
|
|
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);
|
|
789
|
+
ackMessage = ack.message;
|
|
790
|
+
setToast(ack.message);
|
|
791
|
+
setProgress(0.2);
|
|
792
|
+
});
|
|
793
|
+
setToast(ackMessage);
|
|
794
|
+
if (reloadAction.mode === 'bundle') closeSoon(2500);
|
|
795
|
+
} catch (err: unknown) {
|
|
796
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
797
|
+
setError(message);
|
|
798
|
+
setToast(
|
|
799
|
+
message.toLowerCase().indexOf('session expired') >= 0
|
|
800
|
+
? 'Session expired. Sign in again.'
|
|
801
|
+
: message,
|
|
802
|
+
);
|
|
803
|
+
await loadSelectedMachine();
|
|
804
|
+
setProgress(null);
|
|
805
|
+
} finally {
|
|
806
|
+
if (mountedRef.current) {
|
|
807
|
+
setAction('idle');
|
|
808
|
+
setReloadingId(null);
|
|
809
|
+
void refreshDevSnapshot();
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
},
|
|
813
|
+
[closeSoon, devSnapshot, loadSelectedMachine, refreshDevSnapshot, runWithReconnect],
|
|
814
|
+
);
|
|
815
|
+
|
|
816
|
+
/** Kept for the legacy one-tap path (BlackBox command, chat Reload button). */
|
|
546
817
|
const handleHotReload = useCallback(async () => {
|
|
547
818
|
setAction('hot-reloading');
|
|
548
819
|
setError(null);
|
|
@@ -569,8 +840,10 @@ export const FeedbackModal: React.FC = () => {
|
|
|
569
840
|
// map this app to its MobileProject scan entry without needing
|
|
570
841
|
// `yaver dev start` to have been run.
|
|
571
842
|
let ackMessage = 'Reload request acknowledged.';
|
|
572
|
-
await runWithReconnect(async (
|
|
573
|
-
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');
|
|
574
847
|
ackMessage = ack.message;
|
|
575
848
|
setToast(ack.message);
|
|
576
849
|
setProgress(0.2);
|
|
@@ -661,19 +934,36 @@ export const FeedbackModal: React.FC = () => {
|
|
|
661
934
|
try {
|
|
662
935
|
const { Dimensions } = require('react-native');
|
|
663
936
|
const { width, height } = Dimensions.get('window');
|
|
937
|
+
const cfg = YaverFeedback.getConfig();
|
|
938
|
+
const identity = resolveReportIdentity({
|
|
939
|
+
projectName: cfg?.projectName,
|
|
940
|
+
bundleId: cfg?.bundleId,
|
|
941
|
+
surface: cfg?.surface,
|
|
942
|
+
surfaces: cfg?.surfaces,
|
|
943
|
+
stack: cfg?.stack,
|
|
944
|
+
stacks: cfg?.stacks,
|
|
945
|
+
testSurfaces: cfg?.testSurfaces,
|
|
946
|
+
feedbackSdk: cfg?.feedbackSdk,
|
|
947
|
+
feedbackTransport: cfg?.feedbackTransport,
|
|
948
|
+
voiceCapabilities: cfg?.voiceCapabilities,
|
|
949
|
+
sttProvider: cfg?.sttProvider,
|
|
950
|
+
ttsProvider: cfg?.ttsProvider,
|
|
951
|
+
});
|
|
664
952
|
const deviceInfo: DeviceInfo = {
|
|
665
953
|
platform: Platform.OS,
|
|
666
954
|
osVersion: String(Platform.Version),
|
|
667
955
|
model: Platform.OS === 'ios' ? 'iOS Device' : 'Android Device',
|
|
668
956
|
screenWidth: width,
|
|
669
957
|
screenHeight: height,
|
|
958
|
+
appName: identity.appName,
|
|
670
959
|
};
|
|
671
960
|
const capturedErrors = YaverFeedback.getCapturedErrors();
|
|
672
961
|
const bundle: FeedbackBundle = {
|
|
673
962
|
metadata: {
|
|
674
963
|
timestamp: new Date().toISOString(),
|
|
675
|
-
|
|
676
|
-
app:
|
|
964
|
+
deviceInfo,
|
|
965
|
+
app: identity.app,
|
|
966
|
+
project: identity.project,
|
|
677
967
|
userNote: '[Screenshot + Fix]',
|
|
678
968
|
},
|
|
679
969
|
screenshots: [path],
|
|
@@ -742,6 +1032,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
742
1032
|
taskId: string;
|
|
743
1033
|
initialPrompt: string;
|
|
744
1034
|
project?: string;
|
|
1035
|
+
projectPath?: string;
|
|
745
1036
|
runner?: string;
|
|
746
1037
|
model?: string;
|
|
747
1038
|
} | null>(null);
|
|
@@ -810,6 +1101,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
810
1101
|
taskId: result.taskId,
|
|
811
1102
|
initialPrompt: promptText,
|
|
812
1103
|
project: identity.projectName,
|
|
1104
|
+
projectPath: identity.projectPath,
|
|
813
1105
|
runner: preferredRunner ?? undefined,
|
|
814
1106
|
model: preferredModel ?? undefined,
|
|
815
1107
|
});
|
|
@@ -829,6 +1121,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
829
1121
|
*/
|
|
830
1122
|
|
|
831
1123
|
const busy = action !== 'idle';
|
|
1124
|
+
const machineRouting = YaverFeedback.getMachineRouting();
|
|
832
1125
|
const readyRunnerCount = runnerCards.filter((row) => row.ready || row.authConfigured).length;
|
|
833
1126
|
const missingRunnerCount = runnerCards.filter((row) => !row.installed).length;
|
|
834
1127
|
const needsAuthRunnerCount = runnerCards.filter(
|
|
@@ -839,17 +1132,18 @@ export const FeedbackModal: React.FC = () => {
|
|
|
839
1132
|
// for the live chat screen. The chat manages its own SSE
|
|
840
1133
|
// subscription, multi-turn follow-ups, and Reload button. Closing
|
|
841
1134
|
// the chat returns to idle and clears the active vibe.
|
|
842
|
-
if (
|
|
1135
|
+
if (activeVibe) {
|
|
843
1136
|
const client = YaverFeedback.getP2PClient();
|
|
844
1137
|
return (
|
|
845
1138
|
<>
|
|
846
1139
|
<AuthOverlay />
|
|
847
1140
|
<QuickActionIcon />
|
|
1141
|
+
<YaverModeBadgeGate />
|
|
848
1142
|
<Modal
|
|
849
1143
|
visible={visible}
|
|
850
1144
|
animationType="slide"
|
|
851
1145
|
transparent
|
|
852
|
-
onRequestClose={
|
|
1146
|
+
onRequestClose={handleClose}
|
|
853
1147
|
>
|
|
854
1148
|
{client ? (
|
|
855
1149
|
<VibeChatScreen
|
|
@@ -857,11 +1151,20 @@ export const FeedbackModal: React.FC = () => {
|
|
|
857
1151
|
initialTaskId={activeVibe.taskId}
|
|
858
1152
|
initialUserPrompt={activeVibe.initialPrompt}
|
|
859
1153
|
project={activeVibe.project}
|
|
1154
|
+
projectPath={activeVibe.projectPath}
|
|
860
1155
|
runner={activeVibe.runner}
|
|
861
1156
|
model={activeVibe.model}
|
|
1157
|
+
voiceInputEnabled={YaverFeedback.getConfig()?.voiceInputEnabled === true}
|
|
862
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}
|
|
863
1166
|
onReload={async () => {
|
|
864
|
-
const c = YaverFeedback.
|
|
1167
|
+
const c = YaverFeedback.getRenderP2PClient();
|
|
865
1168
|
if (!c) throw new Error('Not connected');
|
|
866
1169
|
await c.reloadApp();
|
|
867
1170
|
}}
|
|
@@ -876,6 +1179,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
876
1179
|
<>
|
|
877
1180
|
<AuthOverlay />
|
|
878
1181
|
<QuickActionIcon />
|
|
1182
|
+
<YaverModeBadgeGate />
|
|
879
1183
|
{visible && (
|
|
880
1184
|
<Modal
|
|
881
1185
|
visible={visible}
|
|
@@ -912,6 +1216,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
912
1216
|
}}
|
|
913
1217
|
>
|
|
914
1218
|
<ScrollView
|
|
1219
|
+
ref={scrollRef}
|
|
915
1220
|
style={styles.scroll}
|
|
916
1221
|
contentContainerStyle={[
|
|
917
1222
|
styles.scrollContent,
|
|
@@ -921,9 +1226,13 @@ export const FeedbackModal: React.FC = () => {
|
|
|
921
1226
|
]}
|
|
922
1227
|
keyboardShouldPersistTaps="handled"
|
|
923
1228
|
keyboardDismissMode={Platform.OS === 'ios' ? 'interactive' : 'on-drag'}
|
|
1229
|
+
contentInsetAdjustmentBehavior="always"
|
|
1230
|
+
automaticallyAdjustKeyboardInsets={Platform.OS === 'ios'}
|
|
924
1231
|
>
|
|
925
1232
|
<View style={styles.header}>
|
|
926
|
-
<Text style={styles.title}>
|
|
1233
|
+
<Text style={styles.title}>
|
|
1234
|
+
{dogfoodActive ? `${YaverFeedback.getDogfoodStatus().label || 'App'} Developer Mode` : 'Send Feedback'}
|
|
1235
|
+
</Text>
|
|
927
1236
|
<Pressable
|
|
928
1237
|
onPress={handleClose}
|
|
929
1238
|
hitSlop={12}
|
|
@@ -935,6 +1244,114 @@ export const FeedbackModal: React.FC = () => {
|
|
|
935
1244
|
</Pressable>
|
|
936
1245
|
</View>
|
|
937
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}
|
|
938
1355
|
<Pressable
|
|
939
1356
|
onPress={() => {
|
|
940
1357
|
if (!YaverFeedback.isAuthed()) {
|
|
@@ -960,7 +1377,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
960
1377
|
machineCard.status === 'offline' && styles.machineDotOffline,
|
|
961
1378
|
]}
|
|
962
1379
|
/>
|
|
963
|
-
<Text style={styles.machineLabel}>
|
|
1380
|
+
<Text style={styles.machineLabel}>Coding Machine</Text>
|
|
964
1381
|
</View>
|
|
965
1382
|
<Text style={styles.machineAction}>
|
|
966
1383
|
{machineCard.loading ? 'Refreshing…' : 'Change'}
|
|
@@ -972,6 +1389,17 @@ export const FeedbackModal: React.FC = () => {
|
|
|
972
1389
|
<Text style={styles.machineMeta}>{machineCard.detail}</Text>
|
|
973
1390
|
</Pressable>
|
|
974
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
|
+
|
|
975
1403
|
<View style={styles.runnerSection}>
|
|
976
1404
|
<View style={styles.runnerSectionHeader}>
|
|
977
1405
|
<View style={{ flex: 1 }}>
|
|
@@ -997,6 +1425,13 @@ export const FeedbackModal: React.FC = () => {
|
|
|
997
1425
|
</Pressable>
|
|
998
1426
|
</View>
|
|
999
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
|
+
|
|
1000
1435
|
{runnerCards.map((row) => (
|
|
1001
1436
|
<View
|
|
1002
1437
|
key={row.id}
|
|
@@ -1021,20 +1456,53 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1021
1456
|
{row.statusLine}
|
|
1022
1457
|
</Text>
|
|
1023
1458
|
</View>
|
|
1024
|
-
{
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
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>
|
|
1037
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}
|
|
1038
1506
|
{row.detail ? (
|
|
1039
1507
|
<Text style={styles.runnerCardDetail}>{row.detail}</Text>
|
|
1040
1508
|
) : null}
|
|
@@ -1115,17 +1583,40 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1115
1583
|
</View>
|
|
1116
1584
|
</View>
|
|
1117
1585
|
|
|
1118
|
-
{/* 1.
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1586
|
+
{/* 1. Reload — Hot / Full / Rebuild Bundle.
|
|
1587
|
+
Rendered from the shared decision seam, so a production
|
|
1588
|
+
build (__DEV__ false) renders nothing here at all, and a
|
|
1589
|
+
blocked action shows greyed WITH its reason underneath
|
|
1590
|
+
rather than vanishing. */}
|
|
1591
|
+
{availableReloadActions.map((reloadAction) => (
|
|
1592
|
+
<View key={reloadAction.id} style={styles.reloadRow}>
|
|
1593
|
+
<ActionRow
|
|
1594
|
+
label={
|
|
1595
|
+
reloadingId === reloadAction.id
|
|
1596
|
+
? `${reloadAction.label}…`
|
|
1597
|
+
: reloadAction.label
|
|
1598
|
+
}
|
|
1599
|
+
tint={reloadAction.id === 'rebuild' ? '#38bdf8' : '#fbbf24'}
|
|
1600
|
+
onPress={() => {
|
|
1601
|
+
void handleReloadAction(reloadAction);
|
|
1602
|
+
}}
|
|
1603
|
+
// Never `disabled` at the Pressable level for a blocked
|
|
1604
|
+
// action: we WANT the tap so we can say why. Only a
|
|
1605
|
+
// genuinely busy modal blocks the press.
|
|
1606
|
+
disabled={busy && reloadingId !== reloadAction.id}
|
|
1607
|
+
busy={reloadingId === reloadAction.id}
|
|
1608
|
+
/>
|
|
1609
|
+
<Text style={styles.reloadHint}>
|
|
1610
|
+
{reloadAction.enabled
|
|
1611
|
+
? reloadAction.hint
|
|
1612
|
+
: reloadAction.disabledReason}
|
|
1613
|
+
</Text>
|
|
1614
|
+
</View>
|
|
1615
|
+
))}
|
|
1616
|
+
</>
|
|
1617
|
+
</View>
|
|
1128
1618
|
|
|
1619
|
+
<View style={[styles.tabContent, activeTab !== 'chat' && styles.hidden]}>
|
|
1129
1620
|
{/* 3. Vibing — expands to an input box on first tap
|
|
1130
1621
|
so the user says WHAT they want to vibe on, just
|
|
1131
1622
|
like the Yaver mobile app's Vibing tab. Second
|
|
@@ -1186,13 +1677,13 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1186
1677
|
)}
|
|
1187
1678
|
|
|
1188
1679
|
{/* Screenshot & Fix */}
|
|
1189
|
-
<ActionRow
|
|
1680
|
+
{!dogfoodActive ? <ActionRow
|
|
1190
1681
|
label={action === 'capturing' ? 'Working…' : 'Screenshot & Fix'}
|
|
1191
1682
|
tint="#22c55e"
|
|
1192
1683
|
onPress={handleScreenshotAndFix}
|
|
1193
1684
|
disabled={busy}
|
|
1194
1685
|
busy={action === 'capturing'}
|
|
1195
|
-
/>
|
|
1686
|
+
/> : null}
|
|
1196
1687
|
|
|
1197
1688
|
{/* Deploy — opens an inline panel that talks to
|
|
1198
1689
|
/fleet/deploy-options on the agent and lets the user
|
|
@@ -1200,7 +1691,7 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1200
1691
|
it on. Capabilities (e.g. "Linux can't TestFlight")
|
|
1201
1692
|
come from the agent's doctor probes — no client-side
|
|
1202
1693
|
platform smarts here. */}
|
|
1203
|
-
{!showDeploy ? (
|
|
1694
|
+
{!dogfoodActive && (!showDeploy ? (
|
|
1204
1695
|
<ActionRow
|
|
1205
1696
|
label="Deploy"
|
|
1206
1697
|
tint="#7f8cf7"
|
|
@@ -1209,7 +1700,8 @@ export const FeedbackModal: React.FC = () => {
|
|
|
1209
1700
|
/>
|
|
1210
1701
|
) : (
|
|
1211
1702
|
<DeployPanel onClose={() => setShowDeploy(false)} />
|
|
1212
|
-
)}
|
|
1703
|
+
))}
|
|
1704
|
+
</View>
|
|
1213
1705
|
|
|
1214
1706
|
{progress !== null && (
|
|
1215
1707
|
<View style={styles.progressTrack}>
|
|
@@ -1293,6 +1785,33 @@ const ActionRow: React.FC<ActionRowProps> = ({
|
|
|
1293
1785
|
);
|
|
1294
1786
|
|
|
1295
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' },
|
|
1806
|
+
reloadRow: {
|
|
1807
|
+
gap: 4,
|
|
1808
|
+
},
|
|
1809
|
+
reloadHint: {
|
|
1810
|
+
color: '#8b8b93',
|
|
1811
|
+
fontSize: 11,
|
|
1812
|
+
lineHeight: 15,
|
|
1813
|
+
paddingHorizontal: 4,
|
|
1814
|
+
},
|
|
1296
1815
|
vibeInputRow: {
|
|
1297
1816
|
backgroundColor: 'rgba(129,140,248,0.08)',
|
|
1298
1817
|
borderColor: 'rgba(129,140,248,0.4)',
|
|
@@ -1302,7 +1821,7 @@ const styles = StyleSheet.create({
|
|
|
1302
1821
|
gap: 10,
|
|
1303
1822
|
},
|
|
1304
1823
|
vibeInput: {
|
|
1305
|
-
color: '#
|
|
1824
|
+
color: '#24242b',
|
|
1306
1825
|
fontSize: 15,
|
|
1307
1826
|
minHeight: 64,
|
|
1308
1827
|
textAlignVertical: 'top',
|
|
@@ -1353,7 +1872,7 @@ const styles = StyleSheet.create({
|
|
|
1353
1872
|
justifyContent: 'flex-end',
|
|
1354
1873
|
},
|
|
1355
1874
|
modal: {
|
|
1356
|
-
backgroundColor: '#
|
|
1875
|
+
backgroundColor: '#f8f8fb',
|
|
1357
1876
|
borderTopLeftRadius: 22,
|
|
1358
1877
|
borderTopRightRadius: 22,
|
|
1359
1878
|
padding: 22,
|
|
@@ -1377,7 +1896,7 @@ const styles = StyleSheet.create({
|
|
|
1377
1896
|
title: {
|
|
1378
1897
|
fontSize: 20,
|
|
1379
1898
|
fontWeight: '700',
|
|
1380
|
-
color: '#
|
|
1899
|
+
color: '#17171d',
|
|
1381
1900
|
},
|
|
1382
1901
|
closeBtn: {
|
|
1383
1902
|
width: 36,
|
|
@@ -1385,10 +1904,10 @@ const styles = StyleSheet.create({
|
|
|
1385
1904
|
borderRadius: 18,
|
|
1386
1905
|
alignItems: 'center',
|
|
1387
1906
|
justifyContent: 'center',
|
|
1388
|
-
backgroundColor: '
|
|
1907
|
+
backgroundColor: '#ededf3',
|
|
1389
1908
|
},
|
|
1390
1909
|
closeIcon: {
|
|
1391
|
-
color: '#
|
|
1910
|
+
color: '#656570',
|
|
1392
1911
|
fontSize: 22,
|
|
1393
1912
|
lineHeight: 24,
|
|
1394
1913
|
fontWeight: '400',
|
|
@@ -1407,12 +1926,19 @@ const styles = StyleSheet.create({
|
|
|
1407
1926
|
fontSize: 15,
|
|
1408
1927
|
fontWeight: '700',
|
|
1409
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' },
|
|
1410
1936
|
machineCard: {
|
|
1411
1937
|
borderRadius: 14,
|
|
1412
1938
|
borderWidth: 1,
|
|
1413
1939
|
padding: 14,
|
|
1414
|
-
backgroundColor: '
|
|
1415
|
-
borderColor: '
|
|
1940
|
+
backgroundColor: '#fff',
|
|
1941
|
+
borderColor: '#e1e1e8',
|
|
1416
1942
|
},
|
|
1417
1943
|
machineCardLive: {
|
|
1418
1944
|
backgroundColor: 'rgba(34,197,94,0.10)',
|
|
@@ -1426,6 +1952,10 @@ const styles = StyleSheet.create({
|
|
|
1426
1952
|
backgroundColor: 'rgba(239,68,68,0.10)',
|
|
1427
1953
|
borderColor: 'rgba(239,68,68,0.35)',
|
|
1428
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' },
|
|
1429
1959
|
machineHeader: {
|
|
1430
1960
|
flexDirection: 'row',
|
|
1431
1961
|
alignItems: 'center',
|
|
@@ -1453,7 +1983,7 @@ const styles = StyleSheet.create({
|
|
|
1453
1983
|
backgroundColor: '#ef4444',
|
|
1454
1984
|
},
|
|
1455
1985
|
machineLabel: {
|
|
1456
|
-
color: '#
|
|
1986
|
+
color: '#73737e',
|
|
1457
1987
|
fontSize: 12,
|
|
1458
1988
|
fontWeight: '700',
|
|
1459
1989
|
textTransform: 'uppercase',
|
|
@@ -1465,12 +1995,12 @@ const styles = StyleSheet.create({
|
|
|
1465
1995
|
fontWeight: '700',
|
|
1466
1996
|
},
|
|
1467
1997
|
machineName: {
|
|
1468
|
-
color: '#
|
|
1998
|
+
color: '#222229',
|
|
1469
1999
|
fontSize: 16,
|
|
1470
2000
|
fontWeight: '700',
|
|
1471
2001
|
},
|
|
1472
2002
|
machineMeta: {
|
|
1473
|
-
color: '#
|
|
2003
|
+
color: '#73737e',
|
|
1474
2004
|
fontSize: 12,
|
|
1475
2005
|
marginTop: 4,
|
|
1476
2006
|
lineHeight: 17,
|
|
@@ -1485,33 +2015,33 @@ const styles = StyleSheet.create({
|
|
|
1485
2015
|
gap: 10,
|
|
1486
2016
|
},
|
|
1487
2017
|
runnerSectionTitle: {
|
|
1488
|
-
color: '#
|
|
2018
|
+
color: '#222229',
|
|
1489
2019
|
fontSize: 16,
|
|
1490
2020
|
fontWeight: '700',
|
|
1491
2021
|
},
|
|
1492
2022
|
runnerSectionSubtitle: {
|
|
1493
2023
|
marginTop: 2,
|
|
1494
|
-
color: '#
|
|
2024
|
+
color: '#83838e',
|
|
1495
2025
|
fontSize: 12,
|
|
1496
2026
|
},
|
|
1497
2027
|
runnerRefreshBtn: {
|
|
1498
2028
|
borderRadius: 10,
|
|
1499
2029
|
borderWidth: 1,
|
|
1500
|
-
borderColor: '
|
|
1501
|
-
backgroundColor: '
|
|
2030
|
+
borderColor: '#dedee7',
|
|
2031
|
+
backgroundColor: '#fff',
|
|
1502
2032
|
paddingHorizontal: 10,
|
|
1503
2033
|
paddingVertical: 8,
|
|
1504
2034
|
},
|
|
1505
2035
|
runnerRefreshBtnText: {
|
|
1506
|
-
color: '#
|
|
2036
|
+
color: '#5f5f69',
|
|
1507
2037
|
fontSize: 12,
|
|
1508
2038
|
fontWeight: '600',
|
|
1509
2039
|
},
|
|
1510
2040
|
runnerCard: {
|
|
1511
2041
|
borderRadius: 12,
|
|
1512
2042
|
borderWidth: 1,
|
|
1513
|
-
borderColor: '
|
|
1514
|
-
backgroundColor: '
|
|
2043
|
+
borderColor: '#e2e2e9',
|
|
2044
|
+
backgroundColor: '#fff',
|
|
1515
2045
|
paddingHorizontal: 12,
|
|
1516
2046
|
paddingVertical: 11,
|
|
1517
2047
|
gap: 6,
|
|
@@ -1533,15 +2063,16 @@ const styles = StyleSheet.create({
|
|
|
1533
2063
|
alignItems: 'center',
|
|
1534
2064
|
gap: 10,
|
|
1535
2065
|
},
|
|
2066
|
+
runnerCardActions: { flexDirection: 'row', alignItems: 'center', gap: 6 },
|
|
1536
2067
|
runnerCardTitle: {
|
|
1537
|
-
color: '#
|
|
2068
|
+
color: '#222229',
|
|
1538
2069
|
fontSize: 14,
|
|
1539
2070
|
fontWeight: '700',
|
|
1540
2071
|
},
|
|
1541
2072
|
runnerCardStatus: {
|
|
1542
2073
|
marginTop: 2,
|
|
1543
2074
|
fontSize: 12,
|
|
1544
|
-
color: '#
|
|
2075
|
+
color: '#666671',
|
|
1545
2076
|
},
|
|
1546
2077
|
runnerCardStatusOk: {
|
|
1547
2078
|
color: '#86efac',
|
|
@@ -1553,7 +2084,7 @@ const styles = StyleSheet.create({
|
|
|
1553
2084
|
color: '#fca5a5',
|
|
1554
2085
|
},
|
|
1555
2086
|
runnerCardDetail: {
|
|
1556
|
-
color: '#
|
|
2087
|
+
color: '#858590',
|
|
1557
2088
|
fontSize: 11,
|
|
1558
2089
|
lineHeight: 16,
|
|
1559
2090
|
},
|
|
@@ -1570,6 +2101,15 @@ const styles = StyleSheet.create({
|
|
|
1570
2101
|
fontSize: 12,
|
|
1571
2102
|
fontWeight: '700',
|
|
1572
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' },
|
|
1573
2113
|
runnerSectionError: {
|
|
1574
2114
|
color: '#fca5a5',
|
|
1575
2115
|
fontSize: 12,
|
|
@@ -1581,7 +2121,7 @@ const styles = StyleSheet.create({
|
|
|
1581
2121
|
progressTrack: {
|
|
1582
2122
|
height: 6,
|
|
1583
2123
|
borderRadius: 3,
|
|
1584
|
-
backgroundColor: '
|
|
2124
|
+
backgroundColor: '#e5e5eb',
|
|
1585
2125
|
overflow: 'hidden',
|
|
1586
2126
|
marginTop: 4,
|
|
1587
2127
|
},
|
|
@@ -1608,10 +2148,10 @@ const styles = StyleSheet.create({
|
|
|
1608
2148
|
paddingVertical: 6,
|
|
1609
2149
|
paddingHorizontal: 10,
|
|
1610
2150
|
borderRadius: 10,
|
|
1611
|
-
backgroundColor: '
|
|
2151
|
+
backgroundColor: '#ededf3',
|
|
1612
2152
|
},
|
|
1613
2153
|
quickIconToggleText: {
|
|
1614
|
-
color: '#
|
|
2154
|
+
color: '#5f5f69',
|
|
1615
2155
|
fontSize: 12,
|
|
1616
2156
|
fontWeight: '700',
|
|
1617
2157
|
},
|
|
@@ -1623,25 +2163,25 @@ const styles = StyleSheet.create({
|
|
|
1623
2163
|
padding: 12,
|
|
1624
2164
|
},
|
|
1625
2165
|
quickIconNoteText: {
|
|
1626
|
-
color: '#
|
|
2166
|
+
color: '#8a5a12',
|
|
1627
2167
|
fontSize: 12,
|
|
1628
2168
|
lineHeight: 17,
|
|
1629
2169
|
},
|
|
1630
2170
|
iconSelector: {
|
|
1631
2171
|
borderRadius: 12,
|
|
1632
2172
|
borderWidth: 1,
|
|
1633
|
-
borderColor: '
|
|
1634
|
-
backgroundColor: '
|
|
2173
|
+
borderColor: '#e1e1e8',
|
|
2174
|
+
backgroundColor: '#fff',
|
|
1635
2175
|
padding: 12,
|
|
1636
2176
|
gap: 10,
|
|
1637
2177
|
},
|
|
1638
2178
|
iconSelectorTitle: {
|
|
1639
|
-
color: '#
|
|
2179
|
+
color: '#222229',
|
|
1640
2180
|
fontSize: 13,
|
|
1641
2181
|
fontWeight: '700',
|
|
1642
2182
|
},
|
|
1643
2183
|
iconSelectorText: {
|
|
1644
|
-
color: '#
|
|
2184
|
+
color: '#73737e',
|
|
1645
2185
|
fontSize: 12,
|
|
1646
2186
|
lineHeight: 17,
|
|
1647
2187
|
},
|
|
@@ -1655,8 +2195,8 @@ const styles = StyleSheet.create({
|
|
|
1655
2195
|
minWidth: 84,
|
|
1656
2196
|
borderRadius: 12,
|
|
1657
2197
|
borderWidth: 1,
|
|
1658
|
-
borderColor: '
|
|
1659
|
-
backgroundColor: '
|
|
2198
|
+
borderColor: '#e4e4ea',
|
|
2199
|
+
backgroundColor: '#f8f8fb',
|
|
1660
2200
|
paddingVertical: 10,
|
|
1661
2201
|
paddingHorizontal: 8,
|
|
1662
2202
|
alignItems: 'center',
|
|
@@ -1683,7 +2223,7 @@ const styles = StyleSheet.create({
|
|
|
1683
2223
|
fontWeight: '700',
|
|
1684
2224
|
},
|
|
1685
2225
|
iconOptionText: {
|
|
1686
|
-
color: '#
|
|
2226
|
+
color: '#555561',
|
|
1687
2227
|
fontSize: 11,
|
|
1688
2228
|
fontWeight: '600',
|
|
1689
2229
|
},
|
|
@@ -1691,14 +2231,14 @@ const styles = StyleSheet.create({
|
|
|
1691
2231
|
marginTop: 4,
|
|
1692
2232
|
borderRadius: 14,
|
|
1693
2233
|
borderWidth: 1,
|
|
1694
|
-
borderColor: '
|
|
2234
|
+
borderColor: '#e1e1e8',
|
|
1695
2235
|
paddingVertical: 15,
|
|
1696
2236
|
alignItems: 'center',
|
|
1697
2237
|
justifyContent: 'center',
|
|
1698
|
-
backgroundColor: '
|
|
2238
|
+
backgroundColor: '#fff',
|
|
1699
2239
|
},
|
|
1700
2240
|
cancelBtnText: {
|
|
1701
|
-
color: '#
|
|
2241
|
+
color: '#555561',
|
|
1702
2242
|
fontSize: 15,
|
|
1703
2243
|
fontWeight: '700',
|
|
1704
2244
|
},
|